hasMany Relationship in Related Data view is not displayed correctly

Feature(s) impacted

I have a belongsTo relationship between 2 collections called Requests and Lawyers, where a Request belongs to a Lawyer. This is displayed in the Related Data section of the lawyers. On top of this I have another belongsTo relationship between 2 collections called Requests and Cases, where a Request belongs to a Case. This is displayed as a reference to the Cases collection, by displaying the title. When the title is not present, the id will be displayed for the case. Here lies the problem: Even if we have a title, when we display the Lawyers → Requests as Related data → The Case reference is displayed as the id and not the title.

Observed behavior

Described above. In addition to that, what is pretty weird is that if I click on the Request or on the Case id reference, the title gets quickly loaded and, if I go back the the view from related data, the title gets filled in.

This image shows how it looks before I click on one of the Requests:

Before

This screenshot is took after I click on a request:

After

Context

  • Project name: jurata
  • Team name: Jurata
  • Environment name: Development
  • Agent type & version:
  • Recent changes made on your end if any:

Hello and thank for your report :pray:
I will try to reproduce your issue. Also, thank you for the details of the ticket.

Hi @Mihai_Chiciu and welcome in our community :champagne: !!

I do not reproduce your issue. It might be linked to your agent version. Which agent are you using ?

  • @forestadmin/agent
  • forest-express-sequelize
  • forest-express-mongoose

  • And let me know the version also please :pray:.

Also can you confirm that the Reference Field is title on your collection Cases ?

Hello @Vince and thank you for your support!

I am using forest-express-mongoose v7.7.1.

Yes, the Reference Field is set as the title as shown below:

Okey I know we’ve had a few issues on related data on older versions. Could you try to at least upgrade to 7.9.4 and let me know if it works :pray: ?

I have just tried updating it and unfortunately it didn’t solve the issue. It has the same behavior.

Okey I’ll try with the exact same version as you to check if I do have the same issue :eyes:

I tried but it still works on my side :sweat_smile:. Could you please show me the definition of your models ?

@Vince sorry for the late response. Here are the models:

Cases:

const schema = Mongoose.Schema(
    {
      description: { type: String, required: true },
      status: { type: String, required: false },
      title: { type: String, required: false },
      note: { type: String, required: false },
    },
    { timestamps: true }
  );

return mongoose.model('cases', schema, 'cases');

Requests:

  const lawyerReferenceSchema = Mongoose.Schema(
    {
      _id: { type: String, required: true },
      slug: { type: String, required: true },
    },
    { _id: false }
  );

  const schema = Mongoose.Schema(
    {
      case: { type: Mongoose.Schema.Types.ObjectId, ref: 'cases', required: true },
      type: { type: String, required: true },
      lawyer: { type: lawyerReferenceSchema, required: true },
      status: { type: String, required: false },
    },
    { timestamps: true }
  );

  return mongoose.model('requests', schema, 'requests');

For the Lawyers, the Requests field is a Smart Field:

    {
      field: 'requests',
      type: ['String'],
      reference: 'requests._id',
      get: (lawyer) => {
        return requests.find({ 'lawyer._id': lawyer._id }).then((requestList) => {
          return requestList.map((r) => r._id);
        });
      },
    },

And here is the implementation of the hasMany relationship route:

router.get('/collectionsLawyers/:lawyerId/relationships/requests', permissionMiddlewareCreator.list(), (req, res) => {
  return requests
    .find({ 'lawyer._id': req.params.lawyerId })
    .then(async (selectedRequests) => {
      const serializer = new RecordSerializer({ name: 'requests' });
      return serializer.serialize(
        selectedRequests.sort((a, b) => -a.createdAt.valueOf() + b.createdAt.valueOf()),
        { count: selectedRequests.length }
      );
    })
    .then((s) => {
      res.send(s);
    });
});

I have stripped the models down a bit, but I left all the relevant data.

The issue is coming from this code:

It’s loading only the Requests but not the Cases. You should include all Cases linked to the requests loaded.
I would strongly suggest you to delegate all the logic as much as possible to ForestAdmin, so something like that works for me:

router.get('/lawyer/:lawyerId/relationships/requests', permissionMiddlewareCreator.list(), async (req, res) => {
  const { query } = req;
  const recordsGetter = new RecordsGetter(requests);
  // Add a filter to get only the requests linked to the current lawyer
  const requiredFilter = {
    field: 'lawyer:_id',
    operator: 'equal',
    value: req.params.lawyerId,
  };
  // If there is already a filter just aggregate both of the filters
  const filter = query.filters ? { aggregator: 'and', conditions: [requiredFilter, JSON.parse(query.filters)] } : requiredFilter
  const records = await recordsGetter.getAll({ ...query, filters: JSON.stringify(filter) });
  const recordsSerialized = await recordsGetter.serialize(records, { count: records.length });
  res.send(recordsSerialized);
});

@vince Thanks for the correction. So far, adapting what I have to what you have presented returns and empty array of Requests, which is wrong according to the data I have in my database. I would have 2 questions about your code so that I can adapt it to mine:

  1. Why does the path starts with /lawyer/:lawyerId/...? Is it because the collection name of the Lawyers is lawyer in your case?
  2. I see that the filter is using lawyer:_id. I am guessing that this is the way to get the lawyer._id field and match it to what is found in the req.params.lawyerId. Where can I find more documentation about this and how the querying is done in Forest?

Thanks again for the support!

  1. Yes sorry mine is called lawyer, keep your url
  2. Exactly, it will filter all the request based on the current lawyer. The best way to know which query is executed is to actually test it :wink:. For the documentation, you can find it here but it’s not really well documented. Our new agent is much more powerful to use and well documented :slight_smile:
1 Like

The exact implementation given by @vince wasn’t working for me initially. The reason is that in the Requests view, I manually create a Smart Field (reference to my collectionLawyers._id field) named Lawyer (with capital ‘L’). This was important because this was the way it was referenced down the way. Basically, the filter for the RecordsGetter had to be adapted from lawyer:_id (which is in the Model itself) to Lawyer:_id. Thanks again @vince for the support