25
loading...
This website collects cookies to deliver better user experience
products
collection which has these 2 documents:{
"_id": {
"$oid": "55c30ff62cfa09af198b465a"
},
"name": "Awesome Tshirt",
"currency": "usd",
"one_size": "xl",
"variants": [
{
"type": "color",
"base_sku": 132145,
"items": [
{
"color": "Grey Melange",
"price": 80,
"sku": 1243252369
},
{
"color": "Bottle Green",
"price": 90,
"sku": 1243252368
},
{
"color": "Deep Charcoal Grey",
"price": 80,
"sku": 1243252376
},
{
"color": "White",
"price": 80,
"sku": 1243252363
},
{
"color": "Black",
"price": 80,
"sku": 1243252362
}
]
}
]
},
{
"_id": {
"$oid": "55c30ff62cfa09af198b465c"
},
"name": "Hacker Tshirt",
"currency": "usd",
"one_size": false,
"variants": [
{
"type": "color",
"base_sku": 132155,
"items": [
{
"color": "Black",
"price": 100,
"sku": 87987963
}
]
},
{
"type": "size",
"base_sku": 342434,
"items": [
{
"size": "sm",
"price": 100,
"sku": 97896796
},
{
"size": "xl",
"price": 100,
"sku": 43534534
},
{
"size": "xxl",
"price": 100,
"sku": 76576532
}
]
}
]
}
item
object. Yes in this collection its so silly that somehow you have to choose of either having black T-shirt or XXl tshirt, but not both :Dmatch
your query, this to narrow down querying the whole collection to just a limited number of documents that match your criteriaYou always want to narrow down the number of documents you're searching within at the very beginning of the aggregation pipeline, this will lead to faster queries
color
variant, and it has also grey color inside it's variant items. So this is how we're translating this:{
'$match': {
'variants': {
'$elemMatch': {
'type': 'color',
'items': {
'$elemMatch': {
'color': /grey/i
}
}
}
}
}
}
$elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
type
color first, and then we're using it again to find an element of color that contains grey and its case insensitive - notice the regex /grey/i
- 55c30ff62cfa09af198b465a
, since it is the only one with variants of type color that has grey color55c30ff62cfa09af198b465c
it has variants of type color, but it has only black colorvariants -> items -> {color, price}
$unwind
$unwind Deconstructs an array field from the input documents to output a document for each element
path
which is the path of the array you want to flatten{
'$unwind': {
'path': '$variants'
}
}
variants
, we have to prefix the field name with it so Mongo can interpret it, it just tells Mongo to inject the actual value of variants
variants
array has only 1 elementvariants
is now object instead of arrayvariants.items
elements which is the same case as variants before we unwind it. So i guess we will have to flatten variants.items
too, so next stage will be{
'$unwind': {
'path': '$variants.items'
}
}
items
with dot notation, since variants
is an object and not an array anymore, now these are the the new documents returned after this stageitems
is an object now with 1 document per items
element which is exactly what we need, but did you notice something strange?items.color
value that does not contains grey
we have Black, White, and Bottle Green as well, why is that?$match
stage was only getting the documents that have items with grey color, having this does not necessary means that it will magically just filter other colors from items
, this our job to do nowvariants.items.color
with greyish color, looks like another $match
query, right?{
'$match': {
'variants.type': 'color',
'variants.items.color': /grey/i
}
color
and any item that has grey
with case insensitive, that will return us these documents:variants -> items
and we only care about price
and color
properties, then we need to focus/project these properties only and ignore any other stuff we don't need$project Passes along the documents with the requested fields to the next stage in the pipeline.
_id
is shown by default, unless you specify to hide it{
'_id': 0,
'color': '$variants.items.color',
'price': '$variants.items.price'
}
'_id': 0
because we don't really care about the document ID - at least not in this example, normally you'll need it though - so we just hid itdb.getCollection('products').aggregate([
{
'$match': {
'variants': {
'$elemMatch': {
'type': 'color',
'items': {
'$elemMatch': {
'color': new RegExp('grey', 'i')
}
}
}
}
}
},
{
'$unwind': {
'path': '$variants'
}
},
{
'$unwind': {
'path': '$variants.items'
}
},
{
'$match': {
'variants.type': 'color',
'variants.items.color': new RegExp('grey', 'i')
}
},
{
$project: {
'_id': 0,
'color': '$variants.items.color',
'price': '$variants.items.price'
}
}
])