Failed to instantiate condition tree after replaceFieldOperator

Feature(s) impacted

Search from ForestAdmin search bar

Observed behavior

Error 500 : Failed to instantiate condition tree from json

Expected behavior

Search works

Failure Logs

api       | Error [500] GET /User/count - 50ms
api       | 
api       | ===== An exception was raised =====
api       | GET /User/count?{
api       |  fields[User]: status,fullName,email,age,CurrentAccommodation,CurrentContract,rent,
api       |  fields[CurrentAccommodation]: name,
api       |  fields[CurrentContract]: type,
api       |  search: 12,
api       |  searchExtended: 0,
api       |  isSearchExtended: false,
api       |  timezone: Europe/Paris
api       | }
api       | 
api       |  Failed to instantiate condition tree from json 
api       | 
api       | Error: Failed to instantiate condition tree from json
api       |     at ConditionTreeFactory.fromPlainObject (/usr/src/app/node_modules/@forestadmin/datasource-toolkit/dist/src/interfaces/query/condition-tree/factory.js:50:15)
api       |     at OperatorsEmulateCollectionDecorator.computeEquivalent (/usr/src/app/node_modules/@forestadmin/datasource-customizer/dist/decorators/operators-emulate/collection.js:90:140)
api       |     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
api       | ===================================

Context

  • Project name: Citydays
  • Team name: Managers
  • Environment name: Production
  • Agent technology: node.js
  • Agent (forest package) name & version: “@forestadmin/agent”: “^1.58.2” and “@forestadmin/datasource-sequelize”: “^1.11.5”
  • Database type: Sequelize & Postgres
  • Recent changes made on your end if any: …

I am migrating from the old sequelize agent. I am getting the error when I add the “replaceField Operator” as described here : Smart Relationships | Node.js Developer Guide

// Assuming the following structure:
// User    { id, firstName, lastName, ...., reservationId }
// Reservation    { id, startDate, ...., userId, accommodationId }
// Accommodation    { id, ....., propertyId }
// Property { id, name .... }
  users.addField('currentPropertyId', {
    columnType: 'Uuid',
    dependencies: ['id'],
    getValues: async (records) => {
      const recordIds = records.map((r) => r.id);
      const contracts = await db.contract.findAll({
        where: {
          userId: recordIds,
          status: 'ACTIVE',
        },
        include: [
          {
            model: db.accommodation,
            as: 'Accommodation',
          },
        ],
      });

      return records.map((record) => {
        const contract = contracts.find(
          (element: any) => element.userId === record.id
        );
        return contract?.Accommodation?.propertyId ?? null;
      });
    },
  });
  users.replaceFieldOperator('currentPropertyId', 'In', (value) => value);
  users.addManyToOneRelation('CurrentProperty', 'Property', {
    foreignKey: 'currentPropertyId',
  });

A complex logic is needed here to build the relationship, therefore I assume the import field feature can not be used.
But using the replaceFieldOperator leads to an error 500 when a search is performed.
It seems some operators may be missing, but it is not clear what needs to be changed from the documentation.

Can you help me on that please ?

Hello @Fred and welcome to the community!

Happy to see that you’re taking the big step forward with the new @forestadmin/agent.

As described by the documentation, you would need to implement the reverse-lookup logic inside of the replacer function given to the replaceFieldOperator. The expected return type of this function is a condition-tree, thus leading to the error you observe.

If it is not the case, I would suggest you to use TypeScript with our agent, as we try to provide a fully typed experience on our interfaces, in hopes to improve developer experience.

users.replaceFieldOperator('currentPropertyId', 'In', (value) => {
  const reservations = await db.reservation.findAll({
    attributes: ['id'],
    include: [
      {
          model: db.accommodation,
          required: true,
          attributes: [],
          where: {
            proptertyId: value,
          },
      },
    ],
  });

  return {
    field: 'reservationId',
    operator: 'In',
    value: reservations.map(({ id }) => id),
});

You might need to adapt some things for your models but I hope you get the idea with this :slight_smile:

Best regards,

Thank you for the clarification on the reverse-lookup strategy, it works now :+1:

1 Like