Collection filter for cache results

In order to improve performance on a larger collection with complex business rules for loading items, we thought about introducing in-memory cache.

But there’s a problem with frequent filter condition updates: any time user adds and/or removes search conditions, that changes results of SQL and leads to the database queries.

Is there’s a way to somehow translate collection filter into predicate usable in Array.filter() or something like that?

Hello @Vasilii !

Really great initiative. I’m currently making a Woodshop article on Elasticsearch. We had a similar use case: How to translate filters generated from the Front to some filters comestible by Elasticsearch queries.

In you’re case you will have use our custom filterTranslator by changing the Elasticsearch logic on you’re own logic (function comestible by Array.filter() I presume).

Since the article is not released yet. I propose you to update this thread once it’s released. Or let me know if you need a code snippet really soon I will be able to give you that.

Have a nice day
Morgan.

2 Likes

Hey @Vasilii,

We just release the example on the filter translator. I let you check this.

I just made this little example. It’s more of a pseudo-code but you get the idea.

function equal(fieldDefinition, oneFilter) {
 return (record) => record[oneFilter.field] === oneFilter.value;
}

function greaterThan(fieldDefinition, oneFilter) {
 return (record) => record[oneFilter.field] > oneFilter.value;
}

function lessThan(fieldDefinition, oneFilter) {
 return (record) => record[oneFilter.field] < oneFilter.value;
}

const MAPPING = {
  equal,
  not_equal: not.bind(undefined, equal),
  greater_than: greaterThan,
  less_than: lessThan,
};

function mapFilter(fieldDefinitions, oneFilter) {
  const mapper = MAPPING[oneFilter.operator];
  const fieldDefinition = fieldDefinitions[oneFilter.field];

  if (!mapper) { throw new Error(`Unknown operator ${oneFilter.operator}, you need to define it !`); }
  if (!fieldDefinition) { throw new Error(`Unknown field ${oneFilter.field}, your field hasn't any field definition.`); }

  return mapper(fieldDefinition, oneFilter);
}

function translateFilter(fieldDefinitions, filters) {
  const noFilterFunction = (record) => record;
  if (!filters) { return noFilterFunction }

  if (!filters.aggregator) {
    return [mapFilter(fieldDefinitions, filters)];
  }

  const mapped = filters.conditions.map(
    mapFilter.bind(undefined, fieldDefinitions),
  );

  if (filters.aggregator === 'and') {
    return (record) => {
      return mapped.reduce((accumulator, currentValue) => {
        return accumulator && currentValue(record);
      }, record);
    }
  }

  return (record) => {
    return mapped.reduce((accumulator, currentValue) => {
      return accumulator || currentValue(record);
    }, record);
  }
}

@morganperre Hi, Morgan! Thanks for sharing, that’s a valuable piece of code :handshake:

1 Like