Performing a MongoDB aggregate operation to swap out a set of IDs with their corresponding objects from an array located within the same document

In my collection, I have a variety of documents that look like this:

[{
    id: 1,
    name: 'My document 1',
    allItems: [{
        id: 'item1',
        name: 'My item 1'
    }, {
        id: 'item2',
        name: 'My item 2'
    }, {
        id: 'item3',
        name: 'My item 3'
    }],
    containers: [{
        id: 'container1',
        selectedItems: [
            'item3',
            'item1'
        ]
    }, {
        id: 'container2',
        selectedItems: [
            'item3'
        ]
    }]
},
...]

Now, I am tasked with writing a query to extract a specific document based on its container ID (e.g. container1). The goal is to return the document while updating the selectedItems array to display the corresponding values from the allItems array within the same document.

The desired output should resemble this:

{
    id: 1,
    name: 'My document 1',
    container: {
        id: 'container1',
        selectedItems: [{
            id: 'item3',
            name: 'My item 3'
        }, {
            id: 'item1',
            name: 'My item 1'
        }
    }
}

In my current Node.js aggregation setup, I have implemented the following logic:

db.collection.aggregate([{
    $match: {'containers.id': 'container1'},
    $addFields: {
        container: {
            $filter: {
                input: '$containers', 
                as: 'container', 
                cond: {
                    $eq: ['$$container.id', 'container1']
                }
            }
        }
    },
    $project: {containers: 0},
    $unwind: {path: '$container'}

    // At this stage, the "container" property contains the correct subdocument from the array
    // and the original array with all containers has been removed.
    // Next, a step is needed to populate the container subdocuments in the `selectedItems` array

    $project: {allItems: 0} // Remove the list of all items when no longer necessary for future steps
}]);

I am aware of the existence of $lookup, but given that I want to fill out the matching array items from the same document (rather than a different collection), I believe it may not be required. Even if I were to specify the same collection in a $lookup, it would populate the array with the root document of my collection, potentially leading to complexity when unwinding & matching the required properties of the subdocument.

One option I considered was creating a separate items collection, but this could result in slower lookups and unnecessary relational data for my particular dataset.

I trust that my query is clear, and I am grateful for any assistance provided!

While additional transformations are being applied to my real data, such as copying certain properties from the original root document and concealing others, these details should not impact the core question at hand.

Thank you once again!

Answer №1

  • $match conditions that suit your criteria
  • $filter to go through the loop of the containers array and filter based on id
  • $arrayElemAt used to pick out the first element from the filtered results above
  • $let allows you to declare a variable, perform the previous actions, and store the outcome
  • $filter for iterating through the loop of the allItems array and filtering items according to the result above
db.collection.aggregate([
  { $match: { "containers.id": "container1" } },
  {
    $project: {
      name: 1,
      container: {
        $let: {
          vars: {
            container: {
              $arrayElemAt: [
                {
                  $filter: {
                    input: "$containers",
                    cond: { $eq: ["$$this.id", "container1"] }
                  }
                },
                0
              ]
            }
          },
          in: {
            id: "$$container.id",
            selectedItems: {
              $filter: {
                input: "$allItems",
                cond: { $in: ["$$this.id", "$$container.selectedItems"] }
              }
            }
          }
        }
      }
    }
  }
])

Interactive playground


A modified version where you can tackle the steps individually:

  • $match conditions as required
  • $filter for going through the containers array and filtering by id
  • $arrayElemAt selecting the first element from the aforementioned filtered results
  • $filter for iterating through the allItems array and filtering items by selectedItems
  • $$REMOVE to eliminate specific fields
db.collection.aggregate([
  { $match: { "containers.id": "container1" } },
  {
    $addFields: {
      containers: "$$REMOVE",
      container: {
        $arrayElemAt: [
          {
            $filter: {
              input: "$containers",
              cond: { $eq: ["$$this.id", "container1"] }
            }
          },
          0
        ]
      }
    }
  },
  {
    $addFields: {
      allItems: "$$REMOVE",
      "container.selectedItems": {
        $filter: {
          input: "$allItems",
          cond: { $in: ["$$this.id", "$container.selectedItems"] }
        }
      }
    }
  }
])

Play around with it here

Answer №2

Using a combination of $map and $filter expressions is sufficient in this case, no need for $lookup.

The process is straightforward: first filter the necessary container, then iterate through the selected container's selectedItems using $map to retrieve relevant fields from the allItems field.

  {
    $match: {
      id: 1,
      
    }
  },
  {
    $project: {
      name: 1,
      container: {
        "$arrayElemAt": [
          {
            $map: {
              input: {
                $filter: {
                  input: "$containers",
                  as: "container",
                  cond: {
                    $eq: [
                      "$$container.id",
                      "container1"
                    ]
                  }
                },
                
              },
              as: "container",
              in: {
                "$mergeObjects": [
                  {
                    id: "$$container.id"
                  },
                  {
                    selectedItems: {
                      $map: {
                        input: "$$container.selectedItems",
                        as: "item",
                        in: {
                          "$arrayElemAt": [
                            {
                              $filter: {
                                input: "$allItems",
                                as: "filter",
                                cond: {
                                  $eq: [
                                    "$$filter.id",
                                    "$$item"
                                  ]
                                }
                              }
                            },
                            0
                          ]
                        }
                      }
                    }
                  }
                ]
              }
            }
          },
          0
        ]
      }
    }
  }
])

Check it out on Mongo Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to implement jQuery after new elements have been added to the DOM?

I'm currently working on a Sentence Generator project. The program is designed to take a list of words and retrieve sentences from sentence.yourdictionary.com based on those words. The first sentence fetched from the website is displayed using the cod ...

Accessing the system through a pop-up window with the help of AJAX and

As a newcomer to the world of AJAX, I must admit that I may have jumped into this field with too much enthusiasm. For several days now, I have been struggling to integrate AJAX with my PHP script. Before dismissing this question as a duplicate, please unde ...

Toggle the status of active to inactive instantaneously with the click of a

Incorporating DataTables with ajax using PHP CodeIgniter Framework has presented me with a challenge. I am struggling to toggle between Active and Inactive buttons seamlessly. My desired outcome: When the Active button is clicked, it should transition ...

What is the best way to create text boxes that can expand and collapse within a UIWebView?

I am looking to present text in a UIWebView with expandable or collapsible boxes that contain titles. The user should be able to tap the bar of a collapsed box to expand it. Is there a simple solution available that can be easily integrated into a local ...

Flask - Refreshing Forms Dynamically

In an effort to enhance the responsiveness of my app, I am looking for a way to prevent the page from reloading every time a POST request is sent. My current setup includes a dynamically generated form with input fields designed like this: <div class=&q ...

Shifting JSON Arrays in JavaScript - Changing Order with Ease

Consider the following JSON data: [ { "name": "Lily", "value": 50 }, { "name": "Sophia", "value": 500 }, { "name": "Ethan", "value": 75 } ] I am looking to verify and organize it in ...

Uniting 2 streams to create a single observable

I am in the process of merging 2 different Observables. The first Observable contains a ShoppingCart class, while the second one holds a list of ShoppingItems. My goal is to map the Observable with shopping cart items (Observable<ShoppingItems) to the i ...

Retrieving CSS properties of an element using JavaScript

How can I efficiently retrieve all CSS rules associated with a specific element using JavaScript? I am not seeking a particular solution, just looking to capture all CSS rules for the given element. For example, consider the following HTML code: <div ...

To ensure responsiveness in JavaScript, adjust the width to 100%

Recently, I came across this piece of code: <div style="text-align:center; max-width:500px; width:100%; margin:auto"> <script type="text/javascript" src="transition_example.js"></script></div> Here is the content of transition ...

Experiencing Difficulty Retaining Checkbox State Using LocalStorage Upon Page Reload

At the moment, I have a script that automatically clicks on a random checkbox whenever the page loads or refreshes. Through localStorage, I can also view the value of the input assigned to the randomly selected checkbox. However, I'm facing an issue ...

Unable to upload file to firebase storage - encountering technical issues

After following the documentation on firebase storage, I still can't seem to get my file upload to work properly. Using react, my goal is to successfully upload a file to firebase storage. However, I keep encountering an error message: TypeError: th ...

What is the best way to smoothly insert an item into a nested object within the state of a React component

Trying to create a complex array using react hook: const [strategy, setStrategy] = useState([leg]); Here's how the `leg` object is defined: const leg = { entry: { conditions: [], actions: [""], }, exit: { conditions: [], ...

Unveiling the Power of AngularJS for Parsing JSON Data

A list of images is being generated in a table-like structure using the code snippet below. Each image represents a cell in this table, with its ID specifying its row and column position. <ul> <li class="row"> <ul> & ...

The type 'number[]' is lacking the properties 0, 1, 2, and 3 found in the type '[number, number, number, number]'

type spacing = [number, number, number, number] interface ISpacingProps { defaultValue?: spacing className?: string disabled?: boolean min?: number max?: number onChange?: (value: number | string) => void } interface IFieldState { value: ...

A guide on transferring radio button values to another program and saving them into a database with the help of PHP and jQuery

I have encountered an issue with storing radio button values in a database. Specifically, I want to assign the value of 1 for "Yes" and 0 for "No". To accomplish this, I am utilizing two scripts - a.php and b.php. The former obtains the radio button values ...

Exploring the use of dynamic arrays in C through reading and storing data

I need assistance in implementing a dynamic array based on the content of a file, with the intention to use it with the qsort() method. Although I have managed to determine the number of lines that meet certain criteria in the file, I am struggling with ...

Converting an array of objects to an array of JSON objects in TypeScript

My dilemma lies in the data I have uploaded under the _attachments variable: https://i.sstatic.net/jnFNH.png My aim is to format this data for insertion in the following structure: "_attachments": [ { "container": "string", "fileName": "string" ...

Encountering an endless loop when utilizing cycle-idb with a text field for user input

Struggling to develop a basic test app that captures user input from a text field, displays it, and stores it using cycle-idb. Unfortunately, I've been stuck in an endless loop no matter what steps I take. Below is the complete main function: functi ...

Tips for retrieving the tenth document in Firestore Query using JavaScript

Specifically, a selection of the arranged files. Here's my thought, although I know it can be improved: firestore().collection("queue").orderBy("order_id", "asc").limit(3,5) If anyone has a better solution, I would appre ...

storing a unique identifier in the database and the photo directory

Currently, I am in the process of learning how to effectively rename an uploaded picture (or file) with a unique identifier to ensure it does not have the same name as any other uploaded files. This unique file should then be saved into a database where it ...