Query MongoDB Arrays

MongoDBBeginner
Practice Now

Introduction

In this lab, you will learn how to effectively query arrays in MongoDB. Arrays are a fundamental data structure in MongoDB, allowing you to store lists of values within a single document field. This lab will guide you through the essential techniques for working with array data, from simple element matching to more complex queries using specialized operators. You will learn how to find documents based on array content, size, and element position, which are crucial skills for any developer working with MongoDB.

Getting Started and Basic Array Matching

In this first step, you will connect to the MongoDB server, create a database, and insert some sample data. Then, you will perform basic queries to find documents by matching elements within an array.

First, open the MongoDB Shell (mongosh) to interact with your database. This command will connect you to the running MongoDB instance.

mongosh

Once inside the shell, you will see a test> prompt. Let's switch to a new database named arraylab. The use command creates the database if it does not already exist and switches the current context to it.

use arraylab

Now, let's insert some documents into a new collection called products. Each document represents a product and contains two array fields: tags and colors. Copy and paste the following command into your shell:

db.products.insertMany([
  {
    name: "Laptop",
    tags: ["electronics", "computer", "work"],
    colors: ["silver", "black", "blue"]
  },
  {
    name: "Smartphone",
    tags: ["electronics", "mobile", "communication"],
    colors: ["red", "blue", "green"]
  },
  {
    name: "Headphones",
    tags: ["electronics", "audio", "music"],
    colors: ["black", "white"]
  }
]);

To find a document that contains a specific element in an array, you can query for that value directly. This query finds all products that have the tag "mobile".

db.products.find({ tags: "mobile" });

Example Output:

[
  {
    _id: ObjectId("..."),
    name: 'Smartphone',
    tags: [ 'electronics', 'mobile', 'communication' ],
    colors: [ 'red', 'blue', 'green' ]
  }
]

You can also find documents where the array matches an exact sequence of elements. This query finds the product whose tags array is exactly ["electronics", "computer", "work"] in that specific order.

db.products.find({ tags: ["electronics", "computer", "work"] });

Example Output:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

You can remain in the mongosh shell for the next steps. To exit the shell at any time, type exit and press Enter.

Using Array Query Operators $all and $in

Simple matching is useful, but MongoDB provides powerful operators for more complex array queries. In this step, you will learn to use $all to match multiple elements regardless of order and $in to match any of a list of elements.

We will continue using the products collection from the previous step.

The $all operator selects documents where the array field contains all the specified elements. The order of elements in the query does not matter. Let's find products that are available in both "blue" and "silver".

db.products.find({ colors: { $all: ["blue", "silver"] } });

Example Output:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

The $in operator selects documents where the array field contains at least one of the specified values. It acts like an "OR" condition for array elements. Let's find products that have either a "music" tag or a "work" tag.

db.products.find({ tags: { $in: ["music", "work"] } });

Example Output:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

These operators provide much more flexibility when searching for documents based on the contents of their arrays.

Querying by Array Size and Position

Sometimes, the number of elements in an array or the position of a specific element is important. This step covers how to query based on array size using the $size operator and by element position using dot notation.

The $size operator matches documents that have an array with a specific number of elements. Let's find products that have exactly two colors available.

db.products.find({ colors: { $size: 2 } });

Example Output:

[
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

To query for an element at a specific position (index) in an array, you use dot notation in the format "fieldName.index". Remember that array indices are zero-based, so the first element is at index 0. This query finds all products where the first element in the tags array is "electronics".

db.products.find({ "tags.0": "electronics" });

Example Output:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Smartphone',
    tags: [ 'electronics', 'mobile', 'communication' ],
    colors: [ 'red', 'blue', 'green' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

Now, let's find a product where the second color (index 1) is "black".

db.products.find({ "colors.1": "black" });

Example Output:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

Querying Arrays of Embedded Documents

A common and powerful pattern in MongoDB is to have arrays that contain other documents. These are known as embedded or nested documents. Querying these requires a special operator, $elemMatch, to ensure all conditions apply to the same array element.

First, let's add some new products that have a specs array. Each element in the specs array is a document with ram, storage, and price fields.

db.products.insertMany([
  {
    name: "Gaming Laptop",
    tags: ["electronics", "computer", "gaming"],
    specs: [
      { ram: 16, storage: 512, price: 1200 },
      { ram: 32, storage: 1024, price: 2000 }
    ]
  },
  {
    name: "Office Laptop",
    tags: ["electronics", "computer", "work"],
    specs: [
      { ram: 8, storage: 256, price: 600 },
      { ram: 16, storage: 512, price: 800 }
    ]
  }
]);

Now, suppose we want to find a laptop that has a configuration with at least 16GB of RAM and a price less than $1000. A simple query might seem correct but can lead to wrong results. For example, db.products.find({ "specs.ram": { $gte: 16 }, "specs.price": { $lt: 1000 } }) would match a document if one embedded document has enough RAM and a different one has a low enough price.

To ensure that a single embedded document in the array satisfies all conditions, you must use the $elemMatch operator.

db.products.find({
  specs: {
    $elemMatch: { ram: { $gte: 16 }, price: { $lt: 1000 } }
  }
});

This query correctly finds only the "Office Laptop", because it has a spec element { ram: 16, storage: 512, price: 800 } which meets both conditions simultaneously.

Example Output:

[
  {
    _id: ObjectId("..."),
    name: 'Office Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    specs: [
      { ram: 8, storage: 256, price: 600 },
      { ram: 16, storage: 512, price: 800 }
    ]
  }
]

Projecting Array Fields

Often, you do not need the entire document returned from a query. Projection allows you to specify which fields to include or exclude. For arrays, MongoDB provides special projection operators like $slice to control which array elements are returned.

The $slice projection operator returns a subset of an array. You can specify a positive number to get the first N elements, or a negative number to get the last N elements.

Let's retrieve all products but only return the name and the first two tags for each one. The first argument to find, {}, is an empty query document that matches all documents. The second argument is the projection document. We use _id: 0 to exclude the default _id field.

db.products.find({}, { name: 1, tags: { $slice: 2 }, _id: 0 });

Example Output:

[
  { "name": "Laptop", "tags": ["electronics", "computer"] },
  { "name": "Smartphone", "tags": ["electronics", "mobile"] },
  { "name": "Headphones", "tags": ["electronics", "audio"] },
  { "name": "Gaming Laptop", "tags": ["electronics", "computer"] },
  { "name": "Office Laptop", "tags": ["electronics", "computer"] }
]

Another useful projection operator is the positional $ operator. When you query an array, this operator ensures that only the first element that matched the query condition is returned in the projected array field.

Let's find products with the "mobile" tag and return only the name and that specific matching tag from the tags array.

db.products.find({ tags: "mobile" }, { name: 1, "tags.$": 1, _id: 0 });

Example Output:

[{ "name": "Smartphone", "tags": ["mobile"] }]

This is very efficient for retrieving only the relevant parts of a large array without having to process the full array in your application.

Summary

In this lab, you have learned the fundamental techniques for querying arrays in MongoDB. You started with basic element matching and progressed to using powerful array operators like $all, $in, and $size. You also explored how to query by an element's position and how to correctly query arrays of embedded documents using $elemMatch. Finally, you learned how to shape the output of your queries by projecting specific array elements using $slice and the positional $ operator. These skills are essential for effectively managing and retrieving data from complex document structures in MongoDB.