The Missing $some Operator in MongoDB

The Missing $some Operator in MongoDB

There is a void between $in and $all where a new array query operator would come in handy. In MongoDB, querying documents where the value of a field is an array can be straight forward. Using the $in comparison operator on an array field will give you documents where the field’s array holds at least one of your provided values.

There is a void between $in and $all where a new array query operator would come in handy

In MongoDB, querying documents where the value of a field is an array can be straight forward. Using the $in comparison operator on an array field will give you documents where the field’s array holds at least one of your provided values.

You also have the array query operator $all that works the same way, but where the array needs to hold all of your provided values.

These two operators cover a lot of ground, but when you want a query for documents where the array holds at least four values, or exactly eight, then MongoDB is missing a useful operator.

Let’s take a simple example. You have a collection of customers, and each customer document has a purchasedProducts field, which is an array of ObjectIds (references to all products the customer has purchased).

Now, for a marketing campaign, you want to find all your customers who have purchased at least three out of a set of ten selected products, to send a message to these customers.

In this scenario, the $in operator won’t do because it would give you all customers who have purchased at least one of the products. The $all operator won’t work either, because it would only give you the customers who have purchased all ten products in the set.

In this scenario, a $some operator would come in handy.

The $some operator could take an array with two items; the first item being an array of values to match against and the second item being an object declaring the condition for a match.

/*
 * Example of a $some operator where the document would match if purchasedDocuments
 * contain at least two of the three provided ObjectIds
 */
db.collection.find({
  purchasedProducts: {
    $some: [[ObjectId('ID1'), ObjectId('ID2'), ObjectId('ID3')], { $gte: 2 }]
  }
})

While the $some operator doesn’t exist, it is still possible to achieve the result. Using an aggregation pipeline, we get access to the tools we need to create the equivalent of a $some operator.

In theory, what the $some operator would do is that it would create an intersection between an array field on the document and the provided array of values to match against.

The size (array length) of this intersection would then be compared against the provided condition, and if the size is at least two (in the example above), then the document is a match.

To transform this into an aggregation pipeline, we will need four stages:

  1. Narrow down the number of documents to those that match at least one of the provided values.
  2. Create an intersection between the array field on the document and the provided array of values. Get the size of the intersection and compare it with your match condition, and save the result as a temporary field on the document.
  3. Based on the temporary field, filter out any document that didn’t match.
  4. Clean up the documents by removing the temporary field from each document
/*
 * Workaround using aggregation pipeline stages to solve what a
 * $some operator could have done.
 *
 * Inspired by this SO answer: https://stackoverflow.com/a/28161130/678801
 */

const purchasedProducts = [[ObjectId('ID1'), ObjectId('ID2'), ObjectId('ID3')];

db.collection.aggregate([
  // Only include those that match at least one of the values
  // to narrow down the result 
  {
    $match: {
      purchasedProducts: { $in: purchasedProducts },
    },
  },

  // Add a temporary field that holds a boolean value of whether the
  // document matches the conditions
  {
    $addFields: {
      matched: {
        $gte: [
          {
            $size: {
              $setIntersection: ['$purchasedProducts', purchasedProducts]
            }
          },
          2
        ]
      }
    }
  },

  // Filter out anything that did not match
  { $match: { matched: true } },

  // Remove the temporary field from all documents
  { $project: { matched: 0 } }
])

With the_ $expr operator, it is actually possible to make this both shorter and available in the regular collection methods. 👍

/*
 * Example of a $some operator workaround using $expr. The document would match if
 * purchasedProducts contain at least two of the three provided ObjectIds
 */

const purchasedProducts = [[ObjectId('ID1'), ObjectId('ID2'), ObjectId('ID3')]

db.collection.find({
  purchasedProducts: { $in: purchasedProducts },
  $expr: {
    $gt: [
      { $size: { $setIntersection: [‘$purchasedProducts’, purchasedProducts] } },
      2
    ]
  }
})

Even though it is possible to work around the lack of a $some operator, it gets a bit verbose, and a $some operator would make a nice addition to the $in and $all operators.

Let me know what your thoughts are on a $some operator and if you happen to have contacts on the MongoDB dev team, feel free to share the article with them as a feature request.

Thank you and see you!

Mongodb JavaScript Database Nodejs

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

MongoDB Database and java applications

Enroll for free demo to acquire the best knowledge on the schema-less database from live industry experts through MongoDB training

How to Use MongoDB as a Real-time database with Node.js

Have you ever run into a scenario where you wanted to update your UI as soon as there is some change to your Mongo database

Node.js REST API with Express & MongoDB - Using a MongoDB Database with Node.js

Node.js REST API with Express & MongoDB - Using a MongoDB Database with Node.js. Learn Node.js framework in simple and easy steps starting from basic to advanced concepts with examples including Introduction, Environment Setup, Node Package Manager, Node Callbacks Concept, Node Buffers Module, Node Streams, Node File System, Node Utility Modules, Node Web Module, Node Express Application, Node RESTFul API, Node Scaling Application.

MongoDB – Database, Collection, and Document

Our MongoDB Online Training provide you to learn about MongoDB strategies with realty. Our MongoDB Online Training also includes live sessions, live Projects, and much

Hire NodeJs Developer

Looking to build dynamic, extensively featured, and full-fledged web applications? **[Hire NodeJs Developer](https://hourlydeveloper.io/hire-dedicated-node-js-developer/ "Hire NodeJs Developer")** to create a real-time, faster, and scalable...