Quering nested objects

I am building up a side-project, below is a stripped down version of the dataset:

{
  "_id": 1234,
  "title": "A",
  "inventory": [
    {
	  "location":"1",
      "barcodes": [1000, 1006, 1012]
    },
    {
	  "location":"2",
      "barcodes": [1001, 1007, 1013]
    }
  ]
},
{
  "_id": 1235,
  "title": "B",
  "inventory": [
    {
	  "location":"1",
      "barcodes": [1002, 1008, 1014]
    },
    {
	  "location":"3",
      "barcodes": [1003, 1009, 1015]
    }
  ]
},
{
  "_id": 1236,
  "title": "C",
  "inventory": [
    {
	  "location":"2",
      "barcodes": [1004, 1010, 1016]
    },
    {
	  "location":"3",
      "barcodes": [1005, 1011, 1017]
    }
  ]
}

The inventory array has a list of documents, among the fields in those documents is an array of Strings, no value in that barcodes array will be duplicated throughout any other document. The collection has a series of these documents. What I am trying to do is set up a find where I provide the barcode(s) I am searching for and only the parent document and the matching documents in the inventory array will be returned. For example, if I search for barcodes 1001 and 1011, the only information to be returned should be

{
  "_id": 1234,
  "title": "A",
  "inventory": [
    {
	  "location":"2",
      "barcodes": [1001, 1007, 1013]
    }
  ]
},
{
  "_id": 1236,
  "title": "C",
  "inventory": [
    {
	  "location":"3",
      "barcodes": [1005, 1011, 1017]
    }
  ]
}

I know how to do this with aggregations but I’m trying to use a basic find

Hi @Sean_Hayes, aggregations are perfect for this use-case but if you still want to use .find(), then all you have to do is pass your filter or query into the find and then project what fields you actually need.

Now aggregation will get you exactly what you want but you’d still have to manually extract what you need when using find, however, at least you get lean documents after finding.

Dropping a code snippet to help -

db.collection.find(
  { "inventory.barcodes": { $in: [1001, 1011] } },
  { _id: 1, inventory: 1 }
)

Hope this helps - Project Fields to Return from Query - Database Manual - MongoDB Docs

1 Like

Thanks @Sean_Hayes for sample documents. It is a lot easier with such a level of details.

Just like @Michael_Nwuju, I would favor aggregation for such a use-case.

Michael’s solution is almost there but with the projection of inventory:1 the output is not quite the one desired. Because all elements will be part of the output.

The projection can be improved with $elemMatch.

I have created a playground with the shared data and Michael’s solution and one that uses $elemMatch to show the differences of output.

There are caveats with the $elemMatch solution too. First the $in is repeated in the query and in the projection. The other issue is that only the first matching element is projected. It is not a problem with your sample data but in the playground I added data {location:1 for _id:1236}that illustrates this issue. The third playground, which is more complex uses aggregation and $filter to cover the case that $elemMatch of find() does not.

3 Likes

Thank you both for your input. Now to figure out the next problem

2 Likes