Customising search fields for BelongsTo Typeahead fields

I have two models in my app: Book and BookFamily. There are about 20 million records in each.

A BookFamily has a main book.

Via Forest Admin, it’s impossible for us to edit the main book of a BookFamily. The BelongsTo Typeahead field times out.

I’ve noticed it’s because although we type in the Book ID, the query is doing a LIKE operation against all of the book fields, and it can take over 100 seconds.

When I manually run a query against my database only searching via the ID field, it returns in less than a millisecond.

Is there a way to configure the BelongsTo Typeahead fields to only search via ID?

Thank you!

Hello @Nadia,

Thank you for using Forest Admin with such a big number of data!!!

By definition the BelongsTo Typeahead will execute a query against all your fields.
However if you want to modify this behaviour, you will have to override your route and modify how forest-express-sequelize works internally.

Please read this part of the documentation for knowing how to override your routes.

You could then write something like:

function ParamsFieldsDeserializer(paramsFields) {
  this.perform = () => {
    if (paramsFields) {
      return Object.keys(paramsFields).reduce((fields, modelName) => {
        fields[modelName] = paramsFields[modelName].split(',');
        return fields;
      }, {});
    }
    return null;
  };
}

router.get('/myCollection', permissionMiddlewareCreator.list(), (request, response, next) => {
  // limit it to searchToEdit or whatever you like
  if (request.query['searchToEdit'] === 'true') {
    const recordsGetter = new RecordsGetter(myCollection);
    recordsGetter.getAll = function (params) {
      this.searchValue = params.search;
      this.fieldsPerModel = new ParamsFieldsDeserializer(params.fields).perform();

      const resourceGetter = new this.Implementation.ResourcesGetter(this.model, this.lianaOptions, params);
      resourceGetter._getRecords = async function _getRecords() {
        const [model, options] = await this._buildQueryOptions();
        // THIS IS THE HACK, only use the first part of the OR (i.e id)
        options.where = options.where[model.sequelize.constructor.Op.or][0];
        const records = await model.findAll(options);
        return records;
      }
      resourceGetter.perform = async function perform() {
        return [await this._getRecords(), await this._getFieldsSearched()];
      }

      return resourceGetter
          .perform()
          .then(([records, fieldsSearched]) => {
            this.fieldsSearched = fieldsSearched;
            return records;
          });
    }
    return recordsGetter.getAll(request.query)
        .then(records => recordsGetter.serialize(records))
        .then(recordsSerialized => response.send(recordsSerialized))
        .catch(next);
  }
  next();
});

I hope it will help you and skyrocket your edition process :rocket:

Let me know :slight_smile:

Hi @Guillaume_Cisco,

Thank you for pointing me in the right direction.

I’ve managed to find this, which is what I think I need given I’m using a Rails app: Restrict the search on specific fields - Documentation.

Hopefully this will do what I need, without preventing us from doing filters on other Book properties when navigating books in general.

Will report back.

UPDATE: That has worked. Forest just breaks if I try and put a title in the search field now, which isn’t ideal. But we can now do what we need to do in most cases!