{"id":16222135,"url":"https://github.com/hugodf/mongo-query-clause-modification","last_synced_at":"2026-01-21T02:04:21.605Z","repository":{"id":48271649,"uuid":"186132461","full_name":"HugoDF/mongo-query-clause-modification","owner":"HugoDF","description":"Real-world recursion: MongoDB nested query clause addition and removal","archived":false,"fork":false,"pushed_at":"2021-08-03T19:48:06.000Z","size":865,"stargazers_count":1,"open_issues_count":16,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-07T19:51:56.370Z","etag":null,"topics":["jest","mongodb","nodejs","recursion","recursion-basics"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HugoDF.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-11T12:51:13.000Z","updated_at":"2020-11-03T12:02:37.000Z","dependencies_parsed_at":"2022-08-23T15:01:14.022Z","dependency_job_id":null,"html_url":"https://github.com/HugoDF/mongo-query-clause-modification","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/HugoDF/mongo-query-clause-modification","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HugoDF%2Fmongo-query-clause-modification","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HugoDF%2Fmongo-query-clause-modification/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HugoDF%2Fmongo-query-clause-modification/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HugoDF%2Fmongo-query-clause-modification/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HugoDF","download_url":"https://codeload.github.com/HugoDF/mongo-query-clause-modification/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HugoDF%2Fmongo-query-clause-modification/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28622473,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T23:49:58.628Z","status":"online","status_checked_at":"2026-01-21T02:00:08.227Z","response_time":86,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jest","mongodb","nodejs","recursion","recursion-basics"],"created_at":"2024-10-10T12:11:21.746Z","updated_at":"2026-01-21T02:04:21.587Z","avatar_url":"https://github.com/HugoDF.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MongoDB query clause modification\n\n\u003e Real-world recursion: MongoDB nested query clause addition and removal\n\n## Add an $or query clause to an existing MongoDB query\n\nSee the code at [./src/add-or-clause.js](./src/add-or-clause.js).\n\nThe parameters are `query` and `orClause`.\n\n`query` is a MongoDB query which might or might not already contain an `$or` and/or `$and` clause.\n\n`orClause` is an object containing and `$or` clause (it's a fully-fledged MongoDB query in its own right) eg.\n\n```js\nconst orClause = {\n  $or: [\n    {createdAt: {$exists: false}},\n    {createdAt: someDate}\n  ]\n};\n```\n\nThere is initially just 1 thing to look out for:\n1. the query does not contain an $or clause\n2. the query contains an $or clause\n\n### When there's no $or clause in the query\n\nIf there is no `$or` clause, we can simply spread our `orClause` query and the `query` parameter, ie.\n\n```js\nconst newQuery = {\n  ...query,\n  ...orClause\n};\n```\n\nThat is unless there's and `$and` in there somewhere, in which case we want to add our `orClause` to the `$and`:\n\n```js\nconst newQuery = {\n  ...query,\n  $and: [...query.$and, orClause]\n};\n```\n\n### When there's an $or clause in the query\n\nIf there is an `$or` clause, we can't just overwrite it, we need to `$and` the two `$or` queries.\n\nWe should also keep existing `$and` clause contents which yields:\n\n```js\nconst newQuery = {\n  ...queryWithoutOrRemoved,\n  $and: [\n    ...(query.$and || []),\n    { $or: query.$or },\n    orClause\n  ]\n};\n```\n\n## Remove references to a field in an MongoDB query (potentially) using $or and $and\n\nIn this case we're creating a function that takes 2 parameters: `query` (MongoDB query as above) and `fieldName` (name of the field we want to remove references to).\n\n### Remove top-level fields\n\nThe simplest thing to do is remove references to the field at the top-level of the object.\n\nWe can create a simple `omit` function using destructuring and recursion\n\n```js\nconst omit = (obj, [field, ...nextFields]) =\u003e {\n  const {[field]: ignore, ...rest} = obj;\n  return nextFields.length \u003e 0 ? omit(rest, nextFields) : rest;\n};\n```\n\nAnd use it:\n\n```js\nconst newQuery = omit(query, [fieldName]);\n```\n\n### Remove fields in any $or clause\n\nTo remove fields in an $or clause (which is a fully-fledged query) is as simple as taking the $or value (which is an array) and running a recursion of the function onto it.\n\nThis will remove fields at the top-level of the `$or` sub-queries _and_ in nest `$or` fields' sub-queries.\n\nWe want to make sure to remove empty $or sub-queries, since `{ $or: [ { }, {} ]}` is an invalid query.\n\nWe default the query's `$or` to an empty array and check length before spreading it back into the newQuery. This is because `{ $or: [] }` is an invalid query.\n\nWe're also careful to remove the top-level `$or` when spreading `filteredTopLevel` so that if the new `$or` is an empty array, the old `$or` is ommitted.\n\n```js\nfunction removeFieldReferences (query, fieldName) {\n  const filteredTopLevel = omit(query, [fieldName]);\n\n  const newOr = (filteredTopLevel.$or || [])\n    .map(q =\u003e removeFieldReferences(q, fieldName))\n    .filter(q =\u003e Object.keys(q).length \u003e 0);\n\n  return {\n    ...omit(filteredTopLevel, ['$or']),\n    ...(newOr.length \u003e 0 ? {$or: newOr} : {})\n  };\n}\n```\n\n### Remove fields in any $and clause\n\nThe rationale for the `$and` solution is the same as for the $or solution.\n\nWe recurse and check that we're not generating an invalid query by omitting empty arrays and objects:\n\n```js\nfunction removeFieldReferences (query, fieldName) {\n  const filteredTopLevel = omit(query, [fieldName]);\n\n  const newAnd = (filteredTopLevel.$and || [])\n    .map(q =\u003e removeFieldReferences(q, fieldName))\n    .filter(q =\u003e Object.keys(q).length \u003e 0);\n\n  return {\n    ...omit(filteredTopLevel, ['$and']),\n    ...(newAnd.length \u003e 0 ? {$and: newAnd} : {})\n  };\n}\n```\n\n### Check that we're not likely to bust the stack\n\nThe actual implementation has a `maxDepth` 3rd parameter defaulted to 5.\n\nWhen `maxDepth` is equal to `0`, we return the query without any treatment (arguably we should run the top-level filter).\n\nOn recursive calls to `removeFieldReferences` we pass `(q, fieldName, maxDepth - 1)` so that we're not going any deeper than we need to by accident.\n\nThis avoids `RangeError: Maximum call stack size exceeded`. \n\n\n## Tests\n\nTests are in `.test.js` files co-located with the modules they're testing.\n\nSee [./src/add-or-clause.test.js](./src/add-or-clause.test.js) and [./src/remove-field-references.test.js](./src/remove-field-references.test.js)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhugodf%2Fmongo-query-clause-modification","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhugodf%2Fmongo-query-clause-modification","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhugodf%2Fmongo-query-clause-modification/lists"}