Projection on multiple criteria find query - mongodb

I would like to query documents based on several criteria and then keep only specific fields. The following query (mongoskin syntax) returns todos for the current user, and filtered by tag:
db.collection('users').find({
_id : db.bson_serializer.ObjectID.createFromHexString(req.user._id.toString())
}, {
todos : {
$elemMatch : {
tags : filterTag
}
}
}
Then I tried to add the projection but the filtering is not done anymore.
db.collection('users').find({
_id : db.bson_serializer.ObjectID.createFromHexString(req.user._id.toString()),
todos : {
$elemMatch : {
tags : filterTag
}
}
}, {
_id : 0,
'todos.value' : 1,
'todos._id' : 1
}

I actually found the solution with aggregate:
db.collection('users').aggregate({
$unwind : '$todos'
}, {
$match : {
_id : db.bson_serializer.ObjectID.createFromHexString(req.user._id.toString()),
'todos.tags' : filterTag
}
}, {
$project : {
_id : 0,
'todos.value' : 1,
'todos._id' : 1
}
}

Related

MongoDB querying nested documents

I have records like:
{
"_id" : ObjectId("5f99cede36fd08653a3d4e92"),
"accessions" : {
"sample_accessions" : {
"5f99ce9636fd08653a3d4e86" : {
"biosampleAccession" : "SAMEA7494329",
"sraAccession" : "ERS5250977",
"submissionAccession" : "ERA3032827",
"status" : "accepted"
},
"5f99ce9636fd08653a3d4e87" : {
"biosampleAccession" : "SAMEA7494330",
"sraAccession" : "ERS5250978",
"submissionAccession" : "ERA3032827",
"status" : "accepted"
}
}
}
}
How do I query by the mongo id in sample_accessions? I thought this should work but it doesn't. What should I be doing?
db.getCollection('collection').find({"accessions.sample_accessions":"5f99ce9636fd08653a3d4e86"})
The id is a key and check whether key is exists or not use $exists, customize response using project to get specific object
db.getCollection('collection').find(
{
"accessions.sample_accessions.5f99ce9636fd08653a3d4e86": {
$exists: true
}
},
{ sample_doc: "$accessions.sample_accessions.5f99ce9636fd08653a3d4e86" }
)
Playground

mongoDB distict problems

It's one of my data as JSON format:
{
"_id" : ObjectId("5bfdb412a80939b6ed682090"),
"accounts" : [
{
"_id" : ObjectId("5bf106eee639bd0df4bd8e05"),
"accountType" : "DDA",
"productName" : "DDA1"
},
{
"_id" : ObjectId("5bf106eee639bd0df4bd8df8"),
"accountType" : "VSA",
"productName" : "VSA1"
},
{
"_id" : ObjectId("5bf106eee639bd0df4bd8df9"),
"accountType" : "VSA",
"productName" : "VSA2"
}
]
}
I want to make a query to get all productName(no duplicate) of accountType = VSA.
I write a mongo query:
db.Collection.distinct("accounts.productName", {"accounts.accountType": "VSA" })
I expect: ['VSA1', 'VSA2']
I get: ['DDA','VSA1', 'VSA2']
Anybody knows why the query doesn't work in distinct?
Second parameter of distinct method represents:
A query that specifies the documents from which to retrieve the distinct values.
But the thing is that you showed only one document with nested array of elements so whole document will be returned for your condition "accounts.accountType": "VSA".
To fix that you have to use Aggregation Framework and $unwind nested array before you apply the filtering and then you can use $group with $addToSet to get unique values. Try:
db.col.aggregate([
{
$unwind: "$accounts"
},
{
$match: {
"accounts.accountType": "VSA"
}
},
{
$group: {
_id: null,
uniqueProductNames: { $addToSet: "$accounts.productName" }
}
}
])
which prints:
{ "_id" : null, "uniqueProductNames" : [ "VSA2", "VSA1" ] }

Exclude 0 values from mongodb $avg but keeping other fields

I run some aggregation queries on MongoDB 3.2.
I would like to group documents by a field with an average on another numeric field.
I need the average to ignore the 0 values.
The problem is I can't entirely filter the document, cause there is another field I need for a count.
Let's illustrate :
This is the structure of my documents:
{"stringToGroupByOn":"foo", "valueToAvg":42, "valueToSum":21}
{"stringToGroupByOn":"foo", "valueToAvg":0, "valueToSum":13}
I can't just filter like this:
db.foobar.aggregate([
{
$match : { valueToAvg : { $gt : 0 } }
},
{
$group : {
_id : '$stringToGroupByOn',
avg : { $avg : '$valueToAvg' },
count : { $sum : '$valueToSum' }
}
}
])
Because I lose the value 13 for the count.
Do you think there is a way to do it in only one query ?
You can use $cond in projection to set null instead of 0, as null is not considered when using average.
db.avg.aggregate([
{$project:{
_id:1,
valueToSum:1,
stringToGroupByOn:1,
valueToAvg:{$cond:
{ if: { $eq: [ "$valueToAvg", 0 ] },
then: null,
else: "$valueToAvg" }}
}},
{
$group : {
_id : '$stringToGroupByOn',
avg : { $avg : '$valueToAvg' },
count : { $sum : '$valueToSum' }
}
}
output:
{
"_id" : "foo",
"avg" : 42.0,
"count" : 34.0
}

Nested mongo query

Here is what the data looks like:
{
"_id" : {
"item" : "1",
"state" : "something"
},
"things" : {
"ordered" : 2,
"cost" : 123
}
}
I try to query for all doc of item 1, there are many state for that item. I know i can get that record using db.orders.find({_id:{item:"1", state: "something"}}). But I would like to get all states I try something like db.orders.find({_id:{item:"1", state: {$exists: true}}})
But that doesn't seem to work. What am i doing wrong?
If you want to get the list of all the different states you could use.
db.orders.distinct("_id.state");
If you want to get the list of all the states in your collection
db.orders.find({}, {"_id.state": 1});
I really want to get the things.cost for all the states for a given
item
db.orders.aggregate([
{ $group : {
_id : { item : "$_id.item" , state : "$_id.state"},
cost: { $push : "$things.cost" }
}
}
]);
If you want the sum instead of the elements of the group by use $sum instead of $push
How do i get for certain item?
db.orders.aggregate([
{ $match : { "_id.item" : "YOUR_ID" }},
{ $group : {
_id : { item : "$_id.item" , state : "$_id.state"},
cost: { $push : "$things.cost" }
}
}
]);

array projection with findandmodify

I'm using findAndModify command to maintain a list of undo/redo commands. I'm using "fields" tag to specify what the "before" values were. This is then used to build undo command. ie:
cmd:
{
findAndModify : "aaa",
query : { _id: ObjectId('5215f7d1fe789bb17427bde9') },
update : { "$set" : { "v1" : 200 } },
fields : { v1 : 1, _id : 0 }
}
built result:
"Do" : { "$set" : { "v1" : 200 } },
"Undo" : { "$set" : { v1" : 100 } }
However, I cannot make this work for arrays. I do a set command like this:
{ $set : "myArrayVar.3" : 100 }
I've tried using projections like:
{ "myArrayVar.3" : 1 }
{ "myArrayVar.$" : 1 }
but both return just empty array braces:
{ myArrayVar : [] }
What am I missing?
You can't use numeric array indexes in projections, but you can use $slice instead:
fields: { myArrayVar: { $slice: [2, 1] } }
That would include just the third element (skip 2, take 1).