Aggregate using $group twice - mongodb

I've read SO and questions like this one. However I'm not able to build the query I want...
Let's say I have the following data structure:
{
"CAUG" : "id1",
"action" : "actionA",
"date" : ISODate("2017-01-01"),
"hp" : 16
}
{
"CAUG" : "id1",
"action" : "actionB",
"date" : ISODate("2017-01-01"),
"hp" : 17
}
{
"CAUG" : "id1",
"action" : "actionC",
"date" : ISODate("2017-02-10"),
"hp" : 18
}
{
"CAUG" : "id2",
"action" : "actionX",
"date" : ISODate("2018-01-01"),
"hp" : 20
}...
The desired output is something like (not sure about brackets and other stuff...):
{
"CAUG" : "id1",
"timeline" : [
ISODate ("2017-01-01) {
{ "action" : "ActionA", hp : "..." }
{ "action" : "ActionB", hp : "..." }
},
ISODate ("2017-02-10) {
{ "action" : "ActionC", hp : "..." }
}
]
}
{
"CAUG" : "id2",
"timeline" : [
ISODate ("2018-01-01) {
{ "action" : "ActionX", hp : "..." }
}
]
}
At this time my (very limited) query is:
(I've tried many things like composite _id, but I'm alway stucked at some point).
db.aggregate(
[
{ $match: { something } },
{ $project: { something } },
{ $group: {
_id: '$CAUG',
"timeline": { "$push": "$$ROOT" }
}
}
]
)
The problem is I do not know how to do another $group inside timeline array... I'm stucked with the output below... Any clue please? Have a nice weekend.
{
"_id" : "1",
"timeline" : [
{
"CAUG" : "ca220491-ug43816",
"action" : "actionA",
"date" : ISODate("2016-12-21T23:00:00.000+0000")
},
{
"CAUG" : "ca220491-ug43816",
"action" : "actionB",
"date" : ISODate("2016-12-21T23:00:00.000+0000")
},
{
"CAUG" : "ca220491-ug43816",
"action" : "actionC",
"date" : ISODate("2017-02-21T23:00:00.000+0000")
}
]
}

Try running the following aggregate operation:
db.collection.aggregate([
{
"$group": {
"_id": {
"CAUG": "$CAUG",
"date": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$date"
}
}
},
"docs": {
"$push": {
"action" : "$action",
"hp" : "$hp"
}
}
}
},
{
"$group": {
"_id": "$_id.CAUG",
"timeline": {
"$push": {
"date": "$_id.date",
"docs": "$docs"
}
}
}
}
])
which gives the sample output
/* 1 */
{
"_id" : "id1",
"timeline" : [
{
"date" : "2017-02-10",
"docs" : [
{
"action" : "actionC",
"hp" : 18.0
}
]
},
{
"date" : "2017-01-01",
"docs" : [
{
"action" : "actionA",
"hp" : 16.0
},
{
"action" : "actionB",
"hp" : 17.0
}
]
}
]
}
/* 2 */
{
"_id" : "id2",
"timeline" : [
{
"date" : "2018-01-01",
"docs" : [
{
"action" : "actionX",
"hp" : 20.0
}
]
}
]
}

Related

how to sort an array in a nested array which is located under an object in mongodb

I have a collection data like below.
{
"name": "Devices",
"exten": {
"parameters": [{
"name": "Date",
"value": ["5","2"]
}, {
"name": "Time",
"value": ["2"]
}, {
"name": "Season",
"value": ["6"]
}
]
}
}
I want to take all data which is name "Devices" and sort by first index of "Value" which is parameter name is "Date"
ex: mongo will get
name = "devices"
exten.parameters.name = "Date"
will sort it by
exten.parameters.value[0]
in this example it will be sorted by "5".
below query returns 0 record.
db.brand.aggregate(
{ $match: {
"name" : "Devices"
}},
{ $unwind: "$exten.parameters" },
{ $match: {
'exten.parameters.name': 'Date'
}},
{ $sort: {
'exten.parameters.value': -1
}}
)
The following query can get us the expected output:
db.collection.aggregate([
{
$match:{
"name":"Devices"
}
},
{
$unwind:"$exten.parameters"
},
{
$match:{
"exten.parameters.name":"Date"
}
},
{
$project:{
"name":1,
"exten":1,
"firstParam":{
$arrayElemAt:["$exten.parameters.value",0]
}
}
},
{
$sort:{
"firstParam":1
}
},
{
$project:{
"firstParam":0
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5da02fb86472ba670fd8c159"),
"name" : "Devices",
"exten" : {
"parameters" : [
{
"name" : "Date",
"value" : [
"5",
"2"
]
},
{
"name" : "Date",
"value" : [
"2",
"7"
]
},
{
"name" : "Time",
"value" : [
"2"
]
},
{
"name" : "Season",
"value" : [
"6"
]
}
]
}
}
Output:
{
"_id" : ObjectId("5da02fb86472ba670fd8c159"),
"name" : "Devices",
"exten" : {
"parameters" : {
"name" : "Date",
"value" : [
"2",
"7"
]
}
}
}
{
"_id" : ObjectId("5da02fb86472ba670fd8c159"),
"name" : "Devices",
"exten" : {
"parameters" : {
"name" : "Date",
"value" : [
"5",
"2"
]
}
}
}

How to write mongo query

How I can get the total number of seats available for a particular movie (seats present in all the theatres for that movie) from the mongodb schema below.
I need to write a mongo query to get the results
{
"_id" : ObjectId("5d637b5ce27c7d60e5c42ae7"),
"name" : "Bangalore",
"movies" : [
{
"name" : "KGF",
"theatres" : [
{
"name" : "PVR",
"seats" : 45
},
{
"name" : "IMAX",
"seats" : 46
}
]
},
{
"name" : "Avengers",
"theatres" : [
{
"name" : "IMAX",
"seats" : 50
}
]
}
],
"_class" : "com.BMS_mongo.ZZ_BMS_mongo_demo.Entity.CityInfo"
}
I have written this code :
db.cities.aggregate( [
{ "$unwind" : "$movies" }, { "$unwind" : "$theatres" } ,
{ "$group" : { _id : "$movies.theatre`enter code here`s.seats" ,
total : { "$sum" : "$seats" } }
}
] )
My schema:
The following query can get us the expected output:
db.collection.aggregate([
{
$unwind:"$movies"
},
{
$unwind:"$movies.theatres"
},
{
$group:{
"_id":"$movies.name",
"movie":{
$first:"$movies.name"
},
"totalSeats":{
$sum:"$movies.theatres.seats"
}
}
},
{
$project:{
"_id":0
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5d637b5ce27c7d60e5c42ae7"),
"name" : "Bangalore",
"movies" : [
{
"name" : "KGF",
"theatres" : [
{
"name" : "PVR",
"seats" : 45
},
{
"name" : "IMAX",
"seats" : 46
}
]
},
{
"name" : "Avengers",
"theatres" : [
{
"name" : "IMAX",
"seats" : 50
}
]
}
],
"_class" : "com.BMS_mongo.ZZ_BMS_mongo_demo.Entity.CityInfo"
}
Output:
{ "movie" : "Avengers", "totalSeats" : 50 }
{ "movie" : "KGF", "totalSeats" : 91 }
Query:
db.movie.aggregate([{ $unwind: { path: "$movies",} },
{ $unwind: { path: "$movies.theatres",} },
{ $group: { _id: "$movies.name", "moviename": { $first: "$movies.name" },
"totalSeats": { $sum: "$movies.theatres.seats" }} }])
I got the answer using this query ...
db.cities.aggregate( [
{ "$match" : { "name" : "Bangalore" } },
{ "$unwind" : "$movies" } ,
{ "$match" : {"movies.name" : "KGF"} },
{ "$unwind" : "$theatres" },
{ "$group" : { _id : "$movies.name", total : { "$sum" : "$movies.theatres.seats"
} } }
] )

MongoDB Keep path where a criteria is met

I'm new to MongoDB.
In the find query I'm using the following structure:
db.report.find({'accountList.transactionList.description': /.*aear.*/i})
However, accountList contains multiple values, and so does transaction list, the exact query would be:
db.report.find({'accountList[0].transactionList[4].description': /.*aear.*/i})
The problem is that accountList has multiple accounts, and only one of them has the value 'aear' in the description. When I'm executing the query it returns me both accounts, and I'd like to keep only the account where aear is in its description. Also, this MUST be iterable over many files, since it file has different transactionLists, therefore in some documents aear will not appear at all, and in others it might appear multiple types, always in different positions. I believe something must be done in projection, but setting it like this doesn't work:
.projection({"accountList.id":1,"accountList.transactionList.description":1})
Here's the output:
"accountList" : [
{
"id" : "1",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-25T21:00:00.000-03:00"),
"description" : "aear"
},
{
"onDate" : ISODate("2019-08-25T21:00:00.000-03:00"),
"description" : "bb"
},
{
"onDate" : ISODate("2019-08-25T21:00:00.000-03:00"),
"description" : "cc"
}
]
},
{
"id" : "2",
"type" : "xD",
"currency" : "USD",
"transactionList" : [
{
"onDate" : ISODate("2019-08-15T21:00:00.000-03:00"),
"description" : "aa",
},
{
"onDate" : ISODate("2019-08-14T21:00:00.000-03:00"),
"description" : "ee"
}
]
}
]
And I'd like something like this, where I''m only getting the path to where the condition is met:
"accountList" : [
{
"id" : "1",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-25T21:00:00.000-03:00"),
"description" : "aear"
},
To accomplish that you need to use aggregate. I believe this code will work in your case:
db.report.aggregate([
{ "$match": { "accountList.transactionList.description": { $regex: "aear", $options: "i"} } },
{ "$unwind": "$accountList" },
{ "$unwind": "$accountList.transactionList" },
{ "$match": { "accountList.transactionList.description": { $regex: "aear", $options: "i"} } },
{ "$group": {
"_id": {
"_id": "$_id",
"accountListId": "$accountList.id",
"accountListType": "$accountList.type",
"accountListCurrency": "$accountList.currency",
},
"transactionList": { "$push": "$accountList.transactionList" }
}},
{ "$group": {
"_id": "$_id._id",
"accountList": {
"$push": {
"id": "$_id.accountListId",
"type": "$_id.accountListType",
"currency": "$_id.accountListCurrency",
"transactionList": "$transactionList"
}
}
}}
])
Updating my answer as this question got updated with new required o/p :
Answer for New Question :
If you've only one transaction matching to given criteria /.*aear.*/i, let's say description is unique across accountList array of report document(exact for provided sample):
db.report.aggregate([{
$match: {
'accountList.transactionList.description': /.*aear.*/i
}
},{ $unwind: '$accountList' },{ $unwind: '$accountList.transactionList' },{$match :{ 'accountList.transactionList.description': /.*aear.*/i}}, { $project: { 'accountList': 1, _id: 0 } }])
But, if you've multiple descriptions (across multiple objects in accountsList array of a report document) matches to given criteria in accountList :
db.report.aggregate([{
$match: {
'accountList.transactionList.description': /.*aear.*/i
}
}, { $unwind: '$accountList' }, { $unwind: '$accountList.transactionList' }, { $match: { 'accountList.transactionList.description': /.*aear.*/i } },
{ $group: { _id: '$_id', accountList: { $push: '$accountList' }, data: { $first: '$$ROOT' } } }
, { $addFields: { 'data.accountList': '$accountList' } }, { $replaceRoot: { 'newRoot': '$data' } }, { $project: { 'accountList': 1, _id: 0 } }
])
Output :
/* 1 */
{
"accountList" : [
{
"id" : "1100",
"type" : "xD",
"currency" : "EUR",
"transactionList" : {
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
},
{
"id" : "1200",
"type" : "xD",
"currency" : "USD",
"transactionList" : {
"onDate" : ISODate("2019-08-16T00:00:00.000Z"),
"description" : "aear"
}
}
]
}
/* 2 */
{
"accountList" : [
{
"id" : "1",
"type" : "xD",
"currency" : "EUR",
"transactionList" : {
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
}
]
}
If in case you've multiple matching descriptions in transaction array & also in other objects of accounts array (this will work for all above scenarios as well but it might not be needed as per requirement, it can be bulky, Check document#3 in Output for clarification) :
db.report.aggregate([
{ "$match": { "accountList.transactionList.description": /.*aear.*/i } },
{ "$unwind": "$accountList" },
{ "$unwind": "$accountList.transactionList" },
{ "$match": { "accountList.transactionList.description": /.*aear.*/i } },
{
"$group": {
"_id": {
"docId": "$_id",
"accountsListObjId": "$accountList.id"
},
"transactionList": { "$push": "$accountList.transactionList" },
"accountList": { "$first": '$accountList' }
}
}
, { $addFields: { 'accountList.transactionList': '$transactionList' } },
{
"$group": {
"_id": "$_id.docId",
"accountList": { $push: '$accountList' }
}
}, { $project: { 'accountList': 1, _id: 0 } }
])
Output :
/* 1 */
{
"accountList" : [
{
"id" : "1100",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
]
},
{
"id" : "1200",
"type" : "xD",
"currency" : "USD",
"transactionList" : [
{
"onDate" : ISODate("2019-08-16T00:00:00.000Z"),
"description" : "aear"
}
]
}
]
}
/* 2 */
{
"accountList" : [
{
"id" : "1",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
]
}
]
}
/* 3 */
{
"accountList" : [
{
"id" : "00",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
},
{
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
]
},
{
"id" : "100",
"type" : "xD",
"currency" : "USD",
"transactionList" : [
{
"onDate" : ISODate("2019-08-16T00:00:00.000Z"),
"description" : "aear"
}
]
}
]
}
If you're looking for exact text, you can do this as well(cause regex is not allowed in cond) :
db.report.aggregate([
{
$match: {
'accountList.transactionList.description': 'aear'
}
}, { $unwind: '$accountList' }, {
$addFields: {
'accountList.transactionList': {
$filter: {
input: '$accountList.transactionList',
as: 'eachTransaction',
cond: { $eq: ["$$eachTransaction.description", 'aear'] }
}
}
}
}, { $match: { 'accountList.transactionList': { $ne: [] } } }, { $group: { _id: '$_id', accountList: { $push: '$accountList' }, data: { $first: '$$ROOT' } } }
, { $addFields: { 'data.accountList': '$accountList' } }, { $replaceRoot: { 'newRoot': '$data' } }, { $project: { 'accountList': 1, _id: 0 } }])
Output : Same as above.
Answer for Old Question :
Ok you've two options here, Please try these :
If you've only one object in accountList which does matches with the given filter then you can simply do this:
db.report.find({'accountList.transactionList.description': /.*aear.*/i}, {'accountList.$': 1})
Output :
/* 1 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df0c7"),
"accountList" : [
{
"id" : "4474",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 2 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df0d7"),
"accountList" : [
{
"id" : "4400",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 3 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df077"),
"accountList" : [
{
"id" : "0000",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 4 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df1c7"),
"accountList" : [
{
"id" : "0101",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
Downside of above .find () query is it would get only first matching object in accountList, If you've multiple matching objects for given filter in accountList then you need to use aggregation (this aggregation query can be used for earlier scenario as well, Please check output for diff) :
db.report.aggregate([
{
$match: {
"accountList.transactionList.description": /.*aear.*/i
}
},
{ $unwind: "$accountList" },
{
$match: {
"accountList.transactionList.description": /.*aear.*/i
}
}, { $group: { _id: '$_id', accountList: { $push: '$accountList' }, doc: { $first: '$$ROOT' } } }, { $addFields: { 'doc.accountList': '$accountList' } },
{ $replaceRoot: { 'newRoot': '$doc' } }
])
Output :
// This first object is best example where you need aggregation
/* 1 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df1c7"),
"accountList" : [
{
"id" : "0101",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
},
{
"id" : "1111",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 2 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df0d7"),
"accountList" : [
{
"id" : "4400",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 3 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df077"),
"accountList" : [
{
"id" : "0000",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 4 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df0c7"),
"accountList" : [
{
"id" : "4474",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
Try this query:
db.report.find({'accountList[0].transactionList[4].description': { $regex: /.*aear.*/i} })
OR - Which will return only the first matching document:
db.report.find({'accountList[0].transactionList[4].description': /.*aear.*/i}).limit(1)

mongodb aggregation $group and then $push a object

this is my data :
> db.bookmarks.find({"userId" : "56b9b74bf976ab70ff6b9999"}).pretty()
{
"_id" : ObjectId("56c2210fee4a33579f4202dd"),
"userId" : "56b9b74bf976ab70ff6b9999",
"items" : [
{
"itemId" : "28",
"timestamp" : "2016-02-12T18:07:28Z"
},
{
"itemId" : "29",
"timestamp" : "2016-02-12T18:07:29Z"
},
{
"itemId" : "30",
"timestamp" : "2016-02-12T18:07:30Z"
},
{
"itemId" : "31",
"timestamp" : "2016-02-12T18:07:31Z"
},
{
"itemId" : "32",
"timestamp" : "2016-02-12T18:07:32Z"
},
{
"itemId" : "33",
"timestamp" : "2016-02-12T18:07:33Z"
},
{
"itemId" : "34",
"timestamp" : "2016-02-12T18:07:34Z"
}
]
}
I want to have something like (actually i hope the _id can become userId too) :
{
"_id" : "56b9b74bf976ab70ff6b9999",
"items" : [
{ "itemId": "32", "timestamp": "2016-02-12T18:07:32Z" },
{ "itemId": "31", "timestamp": "2016-02-12T18:07:31Z" },
{ "itemId": "30", "timestamp": "2016-02-12T18:07:30Z" }
]
}
What I have now :
> db.bookmarks.aggregate(
... { $match: { "userId" : "56b9b74bf976ab70ff6b9999" } },
... { $unwind: '$items' },
... { $sort: { 'items.timestamp': -1} },
... { $skip: 2 },
... { $limit: 3},
... { $group: { '_id': '$userId' , items: { $push: '$items.itemId' } } }
... ).pretty()
{ "_id" : "56b9b74bf976ab70ff6b9999", "items" : [ "32", "31", "30" ] }
i tried to read the document in mongo and find out i can $push, but somehow i cannot find a way to push such object, which is not defined anywhere in the whole object. I want to have the timestamp also.. but i don't know how should i modified the $group (or others??) to do so. thanks for helping!
This code, which I tested in the MongoDB 3.2.1 shell, should give you the output format that you want:
> db.bookmarks.aggregate(
{ "$match" : { "userId" : "Ursula" } },
{ "$unwind" : "$items" },
{ "$sort" : { "items.timestamp" : -1 } },
{ "$skip" : 2 },
{ "$limit" : 3 },
{ "$group" : { "_id" : "$userId", items: { "$push" : { "myPlace" : "$items.itemId", "myStamp" : "$items.timestamp" } } } } ).pretty()
Running the above will produce this output:
{
"_id" : "Ursula",
"items" : [
{
"myPlace" : "52",
"myStamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"myPlace" : "51",
"myStamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"myPlace" : "50",
"myStamp" : ISODate("2016-02-13T18:07:30Z")
}
]
}
In MongoDB version 3.2.x, you can also use the $out operator in the very last stage of the aggregation pipeline, and have the output of the aggregation query written to a collection. Here is the code I used:
> db.bookmarks.aggregate(
{ "$match" : { "userId" : "Ursula" } },
{ "$unwind" : "$items" },
{ "$sort" : { "items.timestamp" : -1 } },
{ "$skip" : 2 },
{ "$limit" : 3 },
{ "$group" : { "_id" : "$userId", items: { "$push" : { "myPlace" : "$items.itemId", "myStamp" : "$items.timestamp" } } } },
{ "$out" : "ursula" } )
This gives me a collection named "ursula":
> show collections
ursula
and I can query that collection:
> db.ursula.find().pretty()
{
"_id" : "Ursula",
"items" : [
{
"myPlace" : "52",
"myStamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"myPlace" : "51",
"myStamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"myPlace" : "50",
"myStamp" : ISODate("2016-02-13T18:07:30Z")
}
]
}
>
Last of all, this is the input document I used in the aggregation query. You can compare this document to how I coded the aggregation query to see how I built the new items array.
> db.bookmarks.find( { "userId" : "Ursula" } ).pretty()
{
"_id" : ObjectId("56c240ed55f2f6004dc3b25c"),
"userId" : "Ursula",
"items" : [
{
"itemId" : "48",
"timestamp" : ISODate("2016-02-13T18:07:28Z")
},
{
"itemId" : "49",
"timestamp" : ISODate("2016-02-13T18:07:29Z")
},
{
"itemId" : "50",
"timestamp" : ISODate("2016-02-13T18:07:30Z")
},
{
"itemId" : "51",
"timestamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"itemId" : "52",
"timestamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"itemId" : "53",
"timestamp" : ISODate("2016-02-13T18:07:33Z")
},
{
"itemId" : "54",
"timestamp" : ISODate("2016-02-13T18:07:34Z")
}
]
}

mongodb aggregation match multiple $and on the same field

i have a document like this :
{
"ExtraFields" : [
{
"value" : "print",
"fieldID" : ObjectId("5535627631efa0843554b0ea")
},
{
"value" : "14",
"fieldID" : ObjectId("5535627631efa0843554b0eb")
},
{
"value" : "POLYE",
"fieldID" : ObjectId("5535627631efa0843554b0ec")
},
{
"value" : "30",
"fieldID" : ObjectId("5535627631efa0843554b0ed")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627631efa0843554b0ee")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627731efa0843554b0ef")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627831efa0843554b0f0")
},
{
"value" : "42",
"fieldID" : ObjectId("5535627831efa0843554b0f1")
},
{
"value" : "30",
"fieldID" : ObjectId("5535627831efa0843554b0f2")
},
{
"value" : "14",
"fieldID" : ObjectId("5535627831efa0843554b0f3")
},
{
"value" : "19",
"fieldID" : ObjectId("5535627831efa0843554b0f4")
}
],
"id" : ObjectId("55369e60733e4914550832d0"), "title" : "A product"
}
what i want is to match one or more sets from the ExtraFields array. For example, all the products that contain the values print and 30. Since a value may be found in more than one fieldID (like 0 or true) we need to create a set like
WHERE (fieldID : ObjectId("5535627631efa0843554b0ea"), value : "print")
Where i'm having problems is when querying more than one fields. The pipeline i came up with is :
db.products.aggregate([
{'$unwind': '$ExtraFields'},
{
'$match': {
'$and': [{
'$and': [{'ExtraFields.value': {'$in': ["A52A2A"]}}, {
'ExtraFields.fieldID': ObjectId("5535627631efa0843554b0ea")
}]
}
,
{
'$and': [{'ExtraFields.value': '14'}, {'ExtraFields.fieldID': ObjectId("5535627631efa0843554b0eb")}]
}
]
}
},
]);
This returns zero results, but this is what i want to do in theory. Match all items that contain set 1 AND all that contain set 2.
The end result should look like a faceted search output :
[
{
"_id" : {
"values" : "18",
"fieldID" : ObjectId("5535627831efa0843554b0f3")
},
"count" : 2
},
{
"_id" : {
"values" : "33",
"fieldID" : ObjectId("5535627831efa0843554b0f2")
},
"count" : 1
}
]
Any ideas?
You could try the following aggregation pipeline
db.products.aggregate([
{
"$match": {
"ExtraFields.value": { "$in": ["A52A2A", "14"] },
"ExtraFields.fieldID": {
"$in": [
ObjectId("5535627631efa0843554b0ea"),
ObjectId("5535627631efa0843554b0eb")
]
}
}
},
{
"$unwind": "$ExtraFields"
},
{
"$match": {
"ExtraFields.value": { "$in": ["A52A2A", "14"] },
"ExtraFields.fieldID": {
"$in": [
ObjectId("5535627631efa0843554b0ea"),
ObjectId("5535627631efa0843554b0eb")
]
}
}
},
{
"$group": {
"_id": {
"value": "$ExtraFields.value",
"fieldID": "$ExtraFields.fieldID"
},
"count": {
"$sum": 1
}
}
}
])
With the sample document provided, this gives the output:
/* 1 */
{
"result" : [
{
"_id" : {
"value" : "14",
"fieldID" : ObjectId("5535627631efa0843554b0eb")
},
"count" : 1
}
],
"ok" : 1
}