MongoDB: query nested array by more than one condition - mongodb

Say this is one item from my users collection:
{
"_id" : "5545f4c4d0dd52c355a99fbe",
"name" : "Dollie James",
"device" : "iOS",
"gender" : "Female",
"wallet" : [
{
"store" : "All Saints",
"balance" : "$196.11",
"date" : "2014-02-22T22:09:38 -10:00",
"tags" : [
"Tshirt",
"Summer"
]
},
{
"store" : "Nike",
"balance" : "$367.76",
"date" : "2014-04-18T14:44:30 -10:00",
"tags" : [
"Shoes"
]
}
]
}
This record was returned from the following query:
db.users.findOne({$and:[{"wallet.tags" : "Tshirt"}, {"wallet.store":"Nike"}]})
However this is not the result I want because Tshirt and Nike are not in the same object in the wallet array. It seems that mongo is doing a query on the entire array. How can I target this query to only return my two conditions that are in the same object within the array?

You don't need $and (as it will apply conditions independently) but $elemMatch. From the doc:
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
In your specific case:
> db.wallet.find(
{wallet: { $elemMatch:{"tags" : "Tshirt", "store":"Nike"}}}
).pretty()
{
"_id" : "5545f4c4d0dd52c355a99f00",
"name" : "Working example",
"device" : "iOS",
"gender" : "Female",
"wallet" : [
{
"store" : "Nike",
"balance" : "$196.11",
"date" : "2014-02-22T22:09:38 -10:00",
"tags" : [
"Tshirt",
"Summer"
]
}
]
}

Related

Mongodb sorting issue

My mongodb collection:
[{
"_id" : ObjectId("5dd6598d55396f36052e347d"),
"isActive" : true,
"myarray" : [
{
"my_id" : "5d967d08821b4031a197b002",
"name" : "jack"
},
{
"my_id" : "5d967d2c821b4031a197b003",
"name" : "manison"
}
]
},
{
"_id" : ObjectId("5dd6598d55396f36052e347d"),
"isActive" : true,
"myarray" : [
{
"my_id" : "5d967d08821b4031a197b002",
"name" : "penelope"
},
{
"my_id" : "5d967d2c821b4031a197b003",
"name" : "cruz"
}
]
}]
Here i am trying to sort based on the name.
not expecting to sort inside the array but expecting outside.
Expecting result be like
[{
"_id" : ObjectId("5dd6598d55396f36052e347d"),
"isActive" : true,
"myarray" : [
{
"my_id" : "5d967d08821b4031a197b002",
"name" : "penelope"
},
{
"my_id" : "5d967d2c821b4031a197b003",
"name" : "cruz"
}
]
},{
"_id" : ObjectId("5dd6598d55396f36052e347d"),
"isActive" : true,
"myarray" : [
{
"my_id" : "5d967d08821b4031a197b002",
"name" : "jack"
},
{
"my_id" : "5d967d2c821b4031a197b003",
"name" : "manison"
}
]
}]
"name" : "cruz" coming first because alphabatically C comes fast than J AND M (which is in second json)
And Prenelop and cruz didn't switched just the main document json switched as per the name order
Query i am using
db.traffic.aggregate([
{$unwind: "$customFieldArray"},
{$sort: {"customFieldArray.field_value":1}},
{$group: {_id:"$_id", customFieldArray: {$push:"$customFieldArray"}}}
]);
But it is sorting inside like taking cruz to penelope and vice versa.
And main json staying stable.
Please have a look
You can do with simple find query with sort cursor
db.traffic.find({}).sort({ "myarray.name": -1 })
From the docs
With arrays, a less-than comparison or an ascending sort compares the
smallest element of arrays, and a greater-than comparison or a
descending sort compares the largest element of the arrays.

Mongo Query for 2 values in array list in a document field

I have a document in a collection that has a field called "myList", it has list items and I need to be able to query collection documents that have field "status" of "good" and "doneBy" with a value of "system" in the "myList" field:
[collection].myList
[
{
"location" : "3826487.pdf",
"status" : "good",
"time" : ISODate("2017-06-27T20:03:46.512Z"),
"reportIdx" : 0,
"doneBy" : "System"
},
{
"location" : "rt-0.pdf",
"status" : "bad",
"time" : ISODate("2017-06-28T16:24:16.559Z"),
"reportIdx" : 0,
"doneBy" : "System"
}
]
It should return all documents that have a list item qualified by the first one in this list. Even though the second list item has "bad", it should return this collection doc with "myList" having these 2 list items.
I figured out that a search for one of the fields would be this but how to do both , I'm not sure of the syntax.
db.getCollection('[collection]').find({myList : { $elemMatch : { "status" : "good" }}})
I believe I found it:
db.getCollection('[collection]')find({ myList:
{ $all: [
{$elemMatch : { "status" : "good" }},
{$elemMatch : {"doneBy" : "System"}}
]
} })

how to use aggregation of mongodb

From the data as given below, I want to sum all Values fields.
Please let me know how can I do it using aggregation functionality of mongodb.
{"MetricRecord":
{ "SchemaVersion" : "0.12",
"Product": {
"ProductName" : "abc",
"ProductVersion": "7.5.0.1" ,
"ProductId" : "1234567890ABDFGH12345",
"InstanceId" : "12345BA32",
"InstanceName" : "1234SS123",
"SystemId" : "somehost.com"
},
"Tenant" : {
"CustomerId" : "222-555-124",
"ServiceCode": "xyzxyzxyz12345yyy"
},
"Metrics" : [
{
"ReportType" :[
{ "report" : "billing" },
],
"LogTime" : "2013-12-08T12:34:56:01Z" ,
"Type" : "AuthorizedUsers",
"SubType" : "registered",
"Value" : "125",
"UnitOfMeasure": "USD",
"Period" : {
"StartTime" : "2013-12-07T00:00:00:01Z",
"EndTime" : "2013-12-08T00:00:00:01Z"
}
},
{
"ReportType" :[
{ "report" : "billing" }
],
"LogTime" : "2013-12-08T12:34:56:01Z" ,
"Type" : "NumberOfTickets",
"SubType" : "resolved",
"Value" : "430",
"UnitOfMeasure": "USD",
"Period" : {
"StartTime" : "2013-12-07T00:00:00:01Z",
"EndTime" : "2013-12-08T00:00:00:01Z"
}
}
]
}
}
So, results which I expect from summation of values is 430+125 i.e. 555
Your document contains string value for MetricRecord.Metrics[index].Value field and i am not sure why are you trying to sum up the string values. if it is a typo and your document contains numerical values for MetricRecord.Metrics[index].Value field then you can try the following query
db.metrics.aggregate([
{$unwind:"$MetricRecord.Metrics"},
{$group:{_id:"$_id",sum:{$sum:"$MetricRecord.Metrics.Value"}}}
])
In the above document posted, if your value field is like
MetricRecord.Metrics[0].Value is 125(not "125")
MetricRecord.Metrics[1].Value is 430(not "430")
you will get the following output
{
"result" : [
{
"_id" : ObjectId("xxxxxxxxxxxxxxxxxxxxxxxx"),
"sum" : 555
}
],
"ok" : 1
}
The above sample query is composed assuming you have the default mongodb "_id" field and you are using a metrics collection. You have to manipulate the query as per you requirements.

Using aggregation in MongoDB to select two or more fields of a document

I'm starting to work with MongoDB and I've a question about aggregation. I've a document that use a lot of different fields in different orders. For example:
db.my_collection.insert({ "answers" : [ { "id" : "0", "type" : "text", "value" : "A"}, { "id" : "1", "type" : "text", "value" : "B"}]})
db.my_collection.insert({ "answers" : [ { "id" : "0", "type" : "text", "value" : "C"}, { "id" : "1", "type" : "text", "value" : "A"}]})
I would to execute a query using "answers.id" with "answers.value" to obtain a result.
I tried but didn't get results, in my case, I executed the command:
db.my_collection.aggregate({$match: {"answers.id":"0", "answers.value": "A"}})
And the result was the two responses when I expected only:
{ "answers" : [ { "id" : "0", "type" : "text", "value" : "A"}, { "id" : "1", "type" : "text", "value" : "B"}]
Thank you!!!
You need to use the $elemMatch operator to match a single element of the answers array with both the specified 'id' and 'value'.
Something like this should work:
db.my_collection.aggregate( {
"$match" : {
"answers" {
"$elemMatch" : {
"id" : "0",
"value" : "A"
}
}
}
} )

Two MongoDB queries same result, what is the difference?

I'm playing around with mongoDB and I can't figure out what is the difference between my two queries.
I use following collection:
{
"_id" : ObjectId("520b79869971eb1a0fdd0ad4"),
"created" : 1376483718636,
"updated" : 1376483718636,
"firstName" : "Jakob",
"lastName" : "D",
"email" : "jakob.d#test.com",
"emailValidated" : false,
"phoneNumber" : "",
"lastLogin" : 1376483718624,
"linkedProviders" : [
{
"userId" : "1XXXXXXXX6",
"providerId" : "facebook",
"password" : "",
"salt" : "",
"authMethod" : "oauth2",
"avatarUrl" : ""
}
],
"userRoles" : [
"ADMIN"
]
},
{
"_id" : ObjectId("520b7dd09971ebcd35dd0ad6"),
"created" : 1376484816666,
"updated" : 1376484816666,
"firstName" : "Jakob",
"lastName" : "D",
"email" : "jakob.d#test.com",
"emailValidated" : false,
"phoneNumber" : "",
"lastLogin" : 1376484816666,
"linkedProviders" : [
{
"userId" : "jakob.d#test.com",
"providerId" : "userpass",
"password" : "7e4aff9e0d90db2318ffcc689c11b66d",
"salt" : "N1GgNvy3NnS0i5GFDyglQZ9s4CeFNndn",
"authMethod" : "userPassword",
"avatarUrl" : ""
}
],
"userRoles" : [
"ADMIN"
]
}
The two queries that gives me the correct and same result(the one with objectId 520b79869971eb1a0fdd0ad4) are:
db.users.find({"linkedProviders.userId":"1XXXXXXXX6","linkedProviders.providerId":"facebook"})
db.users.find({"linkedProviders": {"$elemMatch": {"userId":"1XXXXXXXX6" },"$elemMatch": {"providerId":"facebook" }}})
So what is the difference between these two?
The difference is that $elemMatch finds items for one single array element.
This solution:
db.users.find({
"linkedProviders.userId": "1XXXXXXXX6",
"linkedProviders.providerId": "facebook"
})
Finds any user that has that userId and that providerId, but possibly in different items in linkedProviders, e.g., if linkedProviders[0].userId matches the first and linkedProviders[1].providerId matches the second part of the query, the full document (i.e., the user) will match that query.
On the other hand,
db.users.find({
"linkedProviders": {
"$elemMatch": {
"userId": "1XXXXXXXX6",
"providerId": "facebook"
}
}
})
will match only if the index values (0 and 1 in the previous example) are the same in the document, i.e., only if one array element matches both.
When there's only one key-value mapping in the $elemMatch, it should do the same as the query with the key-value mapping applied directly.
More information:
http://docs.mongodb.org/manual/reference/operator/elemMatch/#op._S_elemMatch