How to Use $in and $all Operators in Mongoose Queries

How to Use $in and $all Operators in Mongoose Queries

Scenario

Let’s say we have a collection of documents representing books in a library. Each book document has a genres field, which is an array of strings representing the genres that the book belongs to. We want to find all books that belong to either the “Mystery” or “Thriller” genres.

Solution

In this case, we can use the $in operator to specify that we want to find books where the genres field contains at least one of the specified values. Here’s an example query using Mongoose:

Book.find({ genres: { $in: ["Mystery", "Thriller"] } });

This query will return all books that have either “Mystery” or “Thriller” (or both) in their genres field.

Now, let’s say we want to find all books that belong to both the “Mystery” and “Thriller” genres. In this case, we can use the $all operator to specify that we want to find books where the genres field contains all of the specified values. Here’s an example query using Mongoose:

Book.find({ genres: { $all: ["Mystery", "Thriller"] } });

This query will return all books that have both “Mystery” and “Thriller” in their genres field.


Let's Understand with another example:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const orderSchema = new Schema({
  products: [String]
});

const Order = mongoose.model('Order', orderSchema);

// Function to create new documents
async function run() {
  await Order.create([
    { products: ['product1', 'product2'] },
    { products: ['product1', 'product3'] },
    { products: ['product2', 'product3'] },
    { products: ['product4', 'product5'] },
  ]);

  // Using $in operator
  const inQuery = { products: { $in: ['product1', 'product2'] } };
  const inOrders = await Order.find(inQuery);
  console.log('Orders with product1 or product2:');
  console.log(inOrders);

  // Using $all operator
  const allQuery = { products: { $all: ['product1', 'product2'] } };
  const allOrders = await Order.find(allQuery);
  console.log('Orders with both product1 and product2:');
  console.log(allOrders);
}

run();

Order Schema and Order Model are created.

Now the function run() will create 4 documents of Order collections, where each document will carry a field called products which stores an array of strings.

And then we have two types of queries, the first one using the $in operator and the second one using the $all operator.

$in

We used the $in operator to find orders where the products array contains either 'product1' or 'product2'.

This query { products: { $in: ['product1', 'product2'] } } returns these orders:

Orders with product1 or product2:
[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "products": [
      "product1",
      "product2"
    ]
  },
  {
    "_id": ObjectId("5a934e000102030405000001"),
    "products": [
      "product1",
      "product3"
    ]
  },
  {
    "_id": ObjectId("5a934e000102030405000002"),
    "products": [
      "product2",
      "product3"
    ]
  }
]

If you see, only those orders are returned that have either product1 or product2 in their field products, except the only document that has products field that contains: ['product4', 'product5']

$all

Next, we used the $all operator to find orders where the products array contains both 'product1' and 'product2'.

This query { products: { $all: ['product1', 'product2'] } } returns only one order, the first one.

Orders with both product1 and product2:
[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "products": [
      "product1",
      "product2"
    ]
  }
]

So, the $all operator returns documents where the values of the array field products contains all the specified elements: product1 and product2.


Opposite of $in

The $nin operator matches documents where the value of a field is not in a specified array. For example, to find all documents in the User collection where the age field is not 25, 30, or 35, you can use the following query:

const users = await User.find({ age: { $nin: [25, 30, 35] } });

Opposite of $all

There isn’t a direct opposite of the $all operator in MongoDB, but you can achieve the same result by combining the $all and $not operators.

The $all operator matches documents where the value of a field contains all the values in the specified array. The $not operator inverts the effect of a query expression, returning documents that do not match the query criteria.

Here’s an example that demonstrates how to use these two operators together to find all documents in the Order collection where the products array does not contain all of the specified elements:

const productsToExclude = ['product1', 'product2'];
const query = {
  products: {
    $not: {
      $all: productsToExclude
    }
  }
};
const orders = await Order.find(query);

In this example, we use the $all operator to match Order documents where all elements in the productsToExclude array is present in the products array.

We then use the $not operator to invert the effect of the $all operator, returning documents where not all of the elements in the productsToExclude array is present in the products array.

In simple terms, combining $not and $all operators we get those Order documents that do not contain product1 and product2.

Output:

[
  {
    "_id": ObjectId("5a934e000102030405000001"),
    "products": [
      "product1",
      "product3"
    ]
  },
  {
    "_id": ObjectId("5a934e000102030405000002"),
    "products": [
      "product2",
      "product3"
    ]
  }
]

Conclusion

In summary, mongoose operators help narrow down the search for our convenience and thus reduce the retrieval time. I suggest experimenting with Mongoose operators or queries in the above code snippets using this playground I have found on the internet.

If you like this article, make sure to leave a reaction and comment if you have any doubts.

Also, follow me on Twitter :)

Did you find this article valuable?

Support Mayukh's Blog by becoming a sponsor. Any amount is appreciated!