跳到主要内容

👐 Searching inside Objects in arrays

In our books, we're using the Attribute Pattern to have different attributes in our documents. As we can see in the sample doc, we have an attributes array, containing several objects, each with the same structure:

attributes: [
{
key: 'edition',
value: '1st'
},
{
key: 'dimensions',
value: 'Height: 1.111 Inches, Length: 1.111 Inches, Weight: 0.2 Pounds, Width: 1.111 Inches'
},
{
key: 'isbn13',
value: '9780765342508'
},
{
key: 'msrp',
value: 6.99
},
{
key: 'isbn',
value: '0765342502'
},
{
key: 'isbn10',
value: '0765342502'
}
],

Matching object fields, the simplest way

How do we search for all the books that have an msrp of 9.99? We want books that, inside attributes, have an object with key msrp and value 9.99?

[
{ $match: {"attributes.key": "msrp", "attributes.value": 9.99} },
{ $project: {_id: 0, title: 1, year: 1, totalInventory: 1, available: 1, "attributes": 1} }
]

Above example is using the shorthand $and operator, that we can also explicitly write:

[ 
{ $match: { $and: [{"attributes.key": "msrp", "attributes.value": 9.99}] } },
{ $project: {_id: 0, title: 1, year: 1, totalInventory: 1, available: 1, "attributes": 1} }
]

Find all the books with an MSPR of 9.99 and that have been reprinted (hint: edition is Reprint)

Answer
[
{ $match:
{$and: [
{"attributes.key": "msrp", "attributes.value": 9.99},
{"attributes.key": "edition", "attributes.value": "Reprint"}]
}
},
{ $project: {_id: 0, title: 1, year: 1, totalInventory: 1, available: 1, "attributes": 1} }
]

$unwind

This is OK, but we get all attributes, although we're only interested in the MSRP!

{
"_id": "0002005018",
"title": "Clara Callan: A novel",
"attributes": [
{
"key": "edition",
"value": "1st"
},
{
"key": "dimensions",
"value": "Height: 11.11 Inches, Length: 6.11 Inches, Weight: 1 Pounds, Width: 1.11 Inches"
},
{
"key": "isbn13",
"value": "9780002005012"
},
{
"key": "msrp",
"value": "0.00"
},
{
"key": "isbn",
"value": "0002005018"
},
{
"key": "isbn10",
"value": "0002005018"
}
]
}

To improve, we'll use $unwind:

[
{ $unwind : "$attributes" },
{ $match: {"attributes.key": "msrp", "attributes.value": 9.99} },
{ $project: {title: 1, attributes: 1} }
]

Here we're getting a copy of each book for each object inside the $attributes array. This "flattens" the array and returns many copies of the same documents, one for each different attribute that we have.

👐 To better understand $unwind, run this aggregation.

You need to select the books collection.

[
{ $match: {_id: "0395623650"} },
{ $unwind : "$attributes" },
]

You should get one document per attribute of the original book. All fields in these returned documents should be the same, except the ones in attributes.