Have records in my db with such structure:
{
"_id" : "YA14163134",
"discount" : "",
"retail" : "115.0000",
"cost" : "",
"description" : "Caterpillar Mens Big Twist Analog Watch",
"stock_update" : "05",
"brand" : "Kronos",
"img_url" : "image2342000.jpg",
"UPC" : "4895053708012",
"stock" : [ [ "1611292138", "5" ], [ "1612032232", "4" ], [ "1612050918", "0" ] ]
}
and looking for query to get all records that have in "stock" "1612050918" value. That is update id.
Trying something like:
db.vlc.find({stock: {$elemMatch:{$all:["1612050918"]}}})
or
db.vlc.find({stock: { $in : ['1611292138']}})
or
db.vlc.find({stock: { $all : [[1611292138]]}})
with no result. It works only if I include in request second array element like here
db.vlc.find({stock: { $all : [['1611292138', '7']]}})
but that limit my request to all items from update with qnty 7 when I need with any qnty. Thank you in advance!
use this query:
{
"stock" : {
"$elemMatch" : {
"$elemMatch" : {
"$eq" : "1611292138"
}
}
}
}
Explanation:
The first $elemMatch allows you to scan all three arrays under stock
The nex $elemMatch allows you to scan the two elements in the sub-arrays
since $elemMatch requires a query object, the $eq notation is used for a literal match.
If you know that "1611292138" will always be the first element of the sub-array, your query becomes simpler:
{ "stock" : { "$elemMatch" : { "0" : "1611292138" } } }
Explanation:
Scan all arrays under stock
Look for "1611292138" in the first slot of each sub-array
Use nested $elemMatch as below :
db.vlc.find({stock: { "$elemMatch":{"$elemMatch":{"$all":["1612050918"]}}}})
Or
db.vlc.find({stock: {"$elemMatch":{ "$elemMatch":{"$in" : ["1612050918"]}}}})
Related
I have a collection setup with documents that look like :
{
"_id" : ObjectId("5c786d9486c1140b1452d777"),
"code" : "TEST-123",
"owner" : "John",
"cars" : [
{
"carPlate" : "QPZ-756",
"carColor" : "blue"
},
{
"carPlate" : "REF-473",
"carColor" : "red"
}
],
}
I'm looking for an mongo aggregate query that grabs each carPlate and outputs the following for every document in the collection
{
"carPlate" : "QPZ-756",
"owner" : "John",
"code" : "TEST-123",
},
{
"carPlate" : "REF-473",
"owner" : "John",
"code" : "TEST-123",
},
I had a look at the $map operator, would this be a good place to start?
I would use $unwind to flatten the array followed by $mergeObjects to combine keys along with $replaceRoot to promote the merge documents to the top.
Something like
db.colname.aggregate([
{$unwind:"$cars"},
{$replaceRoot:{newRoot:{$mergeObjects:[{owner:"$owner"}, "$cars"]}}}
])
I have documents like this in a collection called 'variants':
{
"_id" : "An_FM000900_Var_10_100042505_100042505_G_A",
"analysisId" : "FM000900",
"chromosome" : 10,
"start" : 100042505,
"end" : 100042505,
"size" : 1,
"reference" : "G",
"alternative" : "A",
"effects" : [
{
"_id" : "Analysis:FM000900-Variant:An_FM000900_Var_10_100042505_100042505_G_A-Effect:0",
"biotype" : "protein_coding",
"impact" : "LOW",
},
{
"_id" : "Analysis:FM000900-Variant:An_FM000900_Var_10_100042505_100042505_G_A-Effect:1",
"biotype" : "protein_coding",
"impact" : "MODERATE",
}
]
}
I want to find documents in that collection that meet some criteria ("analysisId":"FM000900"), and after that I want to project over 'effects' array field to bring just the first element in 'effects' array that meet some criteria ("biotype" : "protein_coding" and "impact" : "MODERATE").
The thing is that I just want to show the main 'variant' document if and only if at least one element in the 'effects' array has meet the criteria.
With the following query I get the expected result except that I get 'variant' documents with 'effects' array field empty.
db.getCollection('variants').find(
{
"analysisId":"FM000900"
}
,
{
"effects":{
"$elemMatch" : {
"biotype" : "protein_coding",
"impact" : "MODERATE"
}
}
}
).skip(0).limit(200)
Can somebody transform this query to only get 'variant' documents with some element in 'effect' array after the projection if possible?
Can it be done in another way, without using aggregation framework if possible? as the collection has millions of documents and it has to be performant.
Thanks a lot, guys!!!
Simply use $elemMatch as query operator in addition of your projection, it will filter variants that have at least one effects array element that match all conditions.
So your query will be :
db.getCollection('variants').find(
{
"analysisId":"FM000900",
"effects":{
"$elemMatch" : {
"biotype" : "protein_coding",
"impact" : "MODERATE"
}
}
}
,
{
"effects":{
"$elemMatch" : {
"biotype" : "protein_coding",
"impact" : "MODERATE"
}
}
}
).skip(0).limit(200)
In addition, a compound multikey index that covers both query and projection can improve reading performance, but use it carefully as it can drastically reduce writing performances.
I have a collection of persons whose schema looks like the collection of following documents.
Document: {
name:
age:
educations:[{
title:xyz,
passed_year:2005,
univercity:abc},
{
title:asd
passed_year:2007,
univercity:mno
}],
current_city:ghi
}
Now I wanna show all the persons who has not done xyz education from abc university in year 2005.
I think two possible queries for this need but not sure which one to use as both of them are giving me the output
Query 1:
db.persons.find({"education":{$ne:{$elemMatch:{"title":"xyz","passed_year":2005,"univercity":"abc"}}}})
Query 2:
db.persons.find({"education":{$not:{$elemMatch:{"title":"xyz","passed_year":2005,"univercity":"abc"}}}})
I'm quite confused about operator $ne and $not, which one should I use with $elemMatch as both of them are giving me the output.
Given this $elemMatch: {"title":"xyz","passed_year":2005,"univercity":"abc"} I think you want to exclude any documents which contain an sub document in the educations array which contains all of these pairs:
"title" : "xyz"
"passed_year" : 2005
"univercity" : "abc"
This query will achieve that:
db.persons.find({
"educations": {
$not: {
$elemMatch:{"title": "xyz", "passed_year": 2005, "univercity": "abc"}
}
}
})
In your question you wrote:
both of them are giving me the output
I suspect this is because your query is specifying education whereas the correct attribute name is educations. By specifying education you are adding a predicate which cannot be evaluated since it references a non existent document attribute so regardless of whether that predicate uses $ne or $not it will simply not be applied.
In answer to the question of which operator to use: $not or $ne: if you run the above query with .explain(true) you'll notice that the parsed query produced by Mongo is very different for each of these operators.
Using $ne
"parsedQuery" : {
"$not" : {
"educations" : {
"$eq" : {
"$elemMatch" : {
"title" : "xyz",
"passed_year" : 2005,
"univercity" : "abc"
}
}
}
}
}
Using $not:
"parsedQuery" : {
"$not" : {
"educations" : {
"$elemMatch" : {
"$and" : [
{
"passed_year" : {
"$eq" : 2005
}
},
{
"title" : {
"$eq" : "xyz"
}
},
{
"univercity" : {
"$eq" : "abc"
}
}
]
}
}
}
}
So, it looks like use of $ne causes Mongo to do something like this psuedo code ...
not educations equalTo "$elemMatch" : {"title" : "xyz", "passed_year" : 2005, "univercity" : "abc"}
... i.e. it treats the elemMatch clause as if it is the RHS of an equality operation whereas use of $not causes Mongo to actually evaluate the elemMatch clause.
In a find query projection, fields I specify after the positional operator are ignored and the whole document is always returned.
'myArray.$.myField' : 1 behave exactly like 'myArray.$' : 1
the positional operator selects the right document. But this document is quite big. I would like to project only 1 field from it.
Exemple:
db.getCollection('match').find({"participantsData.id" : 0001}, { 'participantsData.$.id': 1, })
here the response I have
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001,
"plenty" : "of",
"other" : "fields",
"and" : "subdocuments..."
}
]
}
This is what I want
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001
}
]
}
Is it possible with mongo?
Yes it can be done in mongo
Please try the below query
db.getCollection('match').find(
{"participantsData.id" : 0001},
{"participantsData.id": 1, "matchCreation": 1 })
This will give you the below result
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 1
}
]
}
Let's say I have a mongo db restaurant collection that has an array of different foods, and I want to average the price of the "sandwich" and the "burger" for each restaurant i.e. to not include the steak. How do I match 2 out of the 3 types in this situation i.e. or, in other words, filter out the steak?
For example, for the match operator, I can (assuming I have already unwound the array) do something like this
{ $match : { foods : "burger" } }
but I want to do something more like this (which leaves out steak)
{ $match : { foods : ["burger", "sandwich" ]} }
except that code doesn't work.
Can you explain?
"_id" : ObjectId("50b59cd75bed76f46522c34e"),
"restaurant_id" : 0,
"foods" : [
{
"type" : "sandwich",
"price" : 6.99
},
{
"type" : "burger",
"price" : 5.99
},
{
"type" : "steak"
"price" : 9.99
}
]
Use $in to match one of multiple values:
{ $match : { foods : { $in: ["burger", "sandwich" ]}}}
JohnyHK's answer is right and concise.
For the "Can you explain?" part, when you specified the match as follows:
{ $match : { foods : ["burger", "sandwich" ]} }
You are requiring the document to have a field "foods" containing an array with "burger" and "sandwich" as elements. This is an equals comparison.
The operator $in is not directly explained with the $match, see here:
http://docs.mongodb.org/manual/reference/aggregation/match/
since $in is a query operator, which is explained here (linked from $match):
http://docs.mongodb.org/manual/tutorial/query-documents/#read-operations-query-argument