Mapping Search Options in MongoDb

Hi
Is there a way in MongoDb to store the mapping of Search criteria.
Here is a background of my query !

There is a mapping (some business logic) involved when searching a field in a collection.
So when one selects ABC, he should also see records of DEF & EGE.

SearchOption NewSearchList (Also Include in search criteria)
ABC ABC, DEF, EGE
XYZ XYZ, TQR, PRT
and like wise…
Currently I am keeping this mapping in my code, (C#) and when querying MongoDB, I map the options and send request like…

Builders<Entities.Dto.TDocument>.Filter.ElemMatch(
x => x.Unit,
y => NewSearchList .Contains(y.Output))

I have been told that this can be achieved inside mongo itself and I don’t have to do this mapping in my code.
Any help would be greatly appriciated !

To include/exclude properties from read operation result, you need to use $projection.

Currently (in MongoDB v4.2), there is not built-in mechanism, that would allow you to conditionally project (include/exclude) fields, based the props, that are in your query, so you will have to build the projection object on the application side.

Let me show you on example (every example below works fine in mongo-shell).

Imagine we have this dataset:

db.test1.insertMany([
  {
    a: 'x1',
    y: 'y1',
    z: 'z1',
  }
]);

Then we have 3 functions somewhere in our application:

  • to build projection object
function calcProjectionForQuery(query) {
  // by default include only document ids in the output
  let projection = { _id: true };
  // get list of queried properties
  const keysAsked = Object.keys(query);
  // include queried properties to the output
  keysAsked.forEach(key => {
    projection[key] = true;
  });
  // add additional fields to the query output,
  // based on queried properties
  if (keysAsked.includes('x') && keysAsked.includes('y')) {
    projection.z = true;
  }
  return projection;
}
  • to run a query, using that projection object
function runQuery(query) {
  const projection = calcProjectionForQuery(query);
  return db.test1.find(query, projection);
}
  • to run an aggregation, using that projection object (if aggregations are used in your project)
function runAggregation(query) {
  const projection = calcProjectionForQuery(query);
  return db.test1.aggregate([
    {
      $match: query,
    },
    {
      $project: projection,
    }
  ]);
}

Execution results:

> runQuery({ x: 'x1' })
{ "_id" : ObjectId("..."), "x" : "x1" }
> runQuery({ y: 'y1' })
{ "_id" : ObjectId("..."), "y" : "y1" }
> runQuery({ x: 'x1', y: 'y1' })
{ "_id" : ObjectId("..."), "x" : "x1", "y" : "y1", "z" : "z1" }
> runQuery({})
{ "_id" : ObjectId("...") }
>

You can achieve the same, if you would store that mapping object in the database. It may look like this:

db.test2.insertMany([
  {
    queryName: 'query1',
    variants: [
      {
        keysAsked: ['x', 'y'],
        addKeysToProjection: ['z'],
      },
      {
        keysAsked: ['y', 'z'],
        addKeysToProjection: ['x'],
      }
    ]
  },
  {
    queryName: 'query2',
    variants: [
      {
        keysAsked: ['a', 'b'],
        addKeysToProjection: ['c'],
      },
    ],
  },
]);

But, the projection building mechanism can be even more complex, than on the example above, because:

  • You need to take care, that those mapping objects exist in your databases (local, dev, preprod, prod) in actual state (so, you may need update your databases, if your code changes)
  • You need to handle the situations, if someone accidentally deletes/modifies those objects if using shared db

You will also need to plan those mappings fetching strategy:

  • fetch it when application starts (app will not be able to use latest mappings, until next restart-refetch)
  • fetch it with every request (can drastically increase the number of your read-requests to db)
  • fetch it with interval (additional logic)

Summary
It is much more rational and easier to store projection mappings and build projection objects with your application code.

PS: There is also $redact aggregation pipeline stage, that allows you to include/exclude nested objects or entire documents, based on your conditions.

Thanks Slava for your answer.
I am looking for custom synonyms dictionary but i gues you answered my query that its not avaiable in Mongo.