My collection coll is
/* 1 */
{
"_id" : ObjectId("566121aa4b88d840eb7d1c50"),
"batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"),
"array" : [
{
"id" : 1
},
{
"id" : 2
},
{
"id" : 3
},
{
"id" : 4
}
]
}
/* 2 */
{
"_id" : ObjectId("5661224a4b88d840eb7d1c51"),
"batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"),
"array" : [
{
"id" : 1
},
{
"id" : 7
},
{
"id" : 3
},
{
"id" : 5
}
]
}
what i need is to pull objects in array 'array' where
batchCourseId = ObjectId("566122ab94b792fbdf81bcf3")
and 2<array.id<=5
expected output is
/* 1 */
{
"_id" : ObjectId("566121aa4b88d840eb7d1c50"),
"array" : [
{
"id" : 3
},
{
"id" : 4
}
]
}
/* 2 */
{
"_id" : ObjectId("5661224a4b88d840eb7d1c51"),
"array" : [
{
"id" : 3
},
{
"id" : 5
}
]
}
already tried
db.coll.find({"batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3")},
{ array: { $elemMatch: { id: { $gt: 2,$lte: 5} } } })
the output is like
/* 1 */
{
"_id" : ObjectId("566121aa4b88d840eb7d1c50"),
"array" : [
{
"id" : 3
}
]
}
/* 2 */
{
"_id" : ObjectId("5661224a4b88d840eb7d1c51"),
"array" : [
{
"id" : 3
}
]
}
close but only the first matching object in array is in result
FYI this only a sample set of data the original data is more complex and big in count
so pls let me know the best practice to do this, performance is also important
thanks in advance
You can use aggregation for achieving the same. A sample is shown below:
db.coll.aggregate(
{$match: {"batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3")}},
{$unwind: '$array'},
{$match: {'array.id': { $gt: 2,$lte: 5}}},
{$group: {_id: '$_id', array: {$push : '$array'}}}
)
Result:
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "array" : [ { "id" : 3 }, { "id" : 5 } ] }
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "array" : [ { "id" : 3 }, { "id" : 4 } ] }
In MongoDB aggregation $unwind creates Cartesian_product problem so in large data set is good way to avoid $unwind.
Let's check with your example if you use $unwind in aggregation then result looks like this
db.collectionName.aggregate([
{ "$match": { "batchCourseId": ObjectId("566122ab94b792fbdf81bcf3") }},
{ "$unwind": "$array" }
])
so result of above query is :
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 1 } }
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 2 } }
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 3 } }
{ "_id" : ObjectId("566121aa4b88d840eb7d1c50"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 4 } }
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 1 } }
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 7 } }
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 3 } }
{ "_id" : ObjectId("5661224a4b88d840eb7d1c51"), "batchCourseId" : ObjectId("566122ab94b792fbdf81bcf3"), "array" : { "id" : 5 } }
this create multiple documents and in large documents in collections it slow the performance and increase processing time.
Instead of $unwind use $map in aggregation with aggregation-set operator and the query is as below :
db.collection.aggregate([{
"$match": {
"batchCourseId": ObjectId("566122ab94b792fbdf81bcf3")
}
}, {
"$project": {
"array": {
"$setDifference": [{
"$map": {
"input": "$array",
"as": "el",
"in": {
"$cond": {
"if": {
"$and": [{
"$gt": ["$$el.id", 2]
}, {
"$lte": ["$$el.id", 5]
}]
},
"then": "$$el",
"else": false
}
}
}
},
[false]
]
}
}
}])
Related
I am trying to update data of "array1.array2._id": ObjectId("627a6fab60dc3c523b396af1") and Set Name to John But it's updating in all array2's first element's name to John.
db.getCollection('tests')
.updateOne({ "array1.array2._id": ObjectId("627a6fab60dc3c523b396af1") },{ $set: { "array1.$[].array2.$.name" : "John" } })
{
"_id" : ObjectId("627a6fab60dc3c523b396aec"),
"array1" : [
{
"array2" : [
{
"_id" : ObjectId("627a6fab60dc3c523b396af1"),
"name" : "test"
},
{
"_id" : ObjectId("627a6fab60dc3c523b396af2"),
"name" : "ABC"
}
],
"_id" : ObjectId("627a6fab60dc3c523b396aed")
},
{
"array2" : [
{
"_id" : ObjectId("627a6fab60dc3c523b396af3"),
"name" : "XYZ"
},
{
"_id" : ObjectId("627a6fab60dc3c523b396af4"),
"name" : "Testing"
}
],
"_id" : ObjectId("627a6fab60dc3c523b396aee")
}
]
}
Based on this great answer by #R2D2, you can do:
db.collection.update({
"array1.array2._id": ObjectId("627a6fab60dc3c523b396af1")
},
{
$set: {
"array1.$[].array2.$[y].name": "John"
}
},
{
arrayFilters: [
{
"y._id": ObjectId("627a6fab60dc3c523b396af1")
}
]
})
As you can see on this playground example
The data is like this:
{
"_id" : ObjectId("5ae9f2188857ce20f516315c"),
"meta" : {
"participants" : [
{
"gender" : [
{
"text" : "weiblich",
"id" : "LABEL.FEMALE"
}
]
},
{
"gender" : [
{
"text" : "männlich",
"id" : "LABEL.MALE"
}
]
},
{
"gender" : [
{
"text" : "weiblich",
"id" : "LABEL.FEMALE"
}
]
}
]
}
}
{
"_id" : ObjectId("5af00e1070bb5a707634cb12"),
"meta" : {
"participants" : [
{
"gender" : [
{
"text" : "männlich",
"id" : "LABEL.MALE"
}
]
},
{
"gender" : [
{
"text" : "männlich",
"id" : "LABEL.MALE"
}
]
}
]
}
}
{
"_id" : ObjectId("5af1ef01cfd317006694a6e6"),
"meta" : {
"participants" : [
{
"gender" : [
{
"text" : "weiblich",
"id" : "LABEL.FEMALE"
}
]
},
{
"gender" : [
{
"text" : "weiblich",
"id" : "LABEL.FEMALE"
}
]
}
]
}
}
So meta.participants contains some items with different properties e.g. gender with is also an array of one item (don't ask, historic reasons; it's always one item, never two and never empty).
I need a query which returns the documents which contains only male participants (the second doc 5af00e1070bb5a707634cb12).
I already tried my luck but I can't get it right.
These queries give me every doc which has a male participant:
{'meta.participants.gender.id': 'LABEL.MALE'}
{'meta.participants.gender': { $elemMatch: {id: 'LABEL.MALE'}}}
These queries give me 0 results..
{'meta.participants.gender': {id: 'LABEL.MALE'}}
{'meta.participants.gender[0]': { $elemMatch: {id: 'LABEL.MALE'}}}
Try the below:
db.collection.find({
"meta.participants": {
"$not": {
"$elemMatch": {
"gender.id": {
"$nin": [
"LABEL.MALE"
]
}
}
}
}
})
The query to retrieve documents with gender.id field do not have LABEL.FEMALE.
db.test.find( { "meta.participants.gender.id": { $ne: "LABEL.FEMALE" } } )
As such you don't need to use the $elemMatch for Single Query Condition (but, it not an error and the results will be same).
{
"_id" : NumberLong(107),
"cnic" : NumberLong(098765),
"recordsOFGt" : [
{
"records" : [
{
"_id" : NumberLong(1),
"contactIds" : [
NumberLong(303)
],
},
{
"_id" : NumberLong(2),
"contactIds" : [
NumberLong(303),
NumberLong(304)
],
},
{
"_id" : NumberLong(3),
"contactIds" : [
NumberLong(309),
NumberLong(304)
],
},
{
"_id" : NumberLong(4),
"contactIds" : [
NumberLong(303),
NumberLong(304)
],
},
{
"_id" : NumberLong(5),
"contactIds" : [
NumberLong(303),
NumberLong(304)
],
},
]
},
"records2" : {
...
...
}
]
}
I want only record element that will be matched via _id I have tried this
db.getCollection('tempCollection').findOne(
{ 'recordsOFGt': {$elemMatch: {'records._id': 1} }},
{'recordsOFGt.$': 1}
)
this returns all elements of records array need just matching element.
this is what I want as output :
{
"records" :
{
"_id" : NumberLong(1),
"contactIds" : [
NumberLong(303)
],
}
}
My schema is as follows
{
product:[{
structure :[{
version:[{
type:[{
values:[{}]
}]
}]
}]
}]
}
Need to search fields in values array.
Actually I need to update the fields in values array. I'm unable to write query to get sub array values. How can I do that. Kindly let me know, as soon as possible.
You can use this types of query:
// match a one name
db.getCollection('test').find({"product.structure.version.type.values.name":'test'})
// match some value from an array
db.getCollection('test').find({"product.structure.version.type.values.name": {$in: [1, 'test']}})
Last query will find 3 items in the next collection:
/* 1 */
{
"_id" : ObjectId("595b9e17c482ca1b99db23a6"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : 1
}
]
}
]
}
]
}
]
}
]
}
/* 2 */
{
"_id" : ObjectId("595b9e68f9f2fe8d79ca4c82"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : 3
},
{
"name" : 4
},
{
"name" : 1
}
]
}
]
}
]
}
]
}
]
}
/* 3 */
{
"_id" : ObjectId("595b9e71f9f2fe8d79ca4c84"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : 5
}
]
}
]
}
]
}
]
}
]
}
/* 4 */
{
"_id" : ObjectId("595ba08af9f2fe8d79ca4cd9"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : 6
}
]
}
]
}
]
}
]
}
]
}
/* 5 */
{
"_id" : ObjectId("595ba0f1f9f2fe8d79ca4d00"),
"product" : [
{
"structure" : [
{
"version" : [
{
"type" : [
{
"values" : [
{
"name" : "test"
}
]
}
]
}
]
}
]
}
]
}
UPDATE:
To do an update query, you need to do something like this:
db.getCollection('test').update({"product.structure.version.type.values.name": {$in: [1, 'test']}}, {$set: {"product.$.testProp": 'some test string'}}, {multi: true})
so i have a bunch of simple documents like
{
"foos": [
ObjectId("5105862f2b5e30877c685c58"),
ObjectId("5105862f2b5e30877c685c57"),
ObjectId("5105862f2b5e30877c685c56"),
],
"typ": "Organisation",
}
and i want to find out the overall size of associated foos to documents of type "Organisation"
so i have this aggregate query
db.profil.aggregate(
[
{
$match:{
"typ":"Organisation"
}
},
{
$project: {
fooos: { $size: "$foos" }
}
}
]
)
this returns the count of all foos for each document
like :
{ "_id" : ObjectId("50e577602b5e05e74b38a6c8"), "foooos" : 1 }
{ "_id" : ObjectId("51922170975a09f363e3eef5"), "foooos" : 3 }
{ "_id" : ObjectId("51922170975a09f363e3eef8"), "foooos" : 2 }
{ "_id" : ObjectId("5175441d975ae346a3a8dff2"), "foooos" : 0 }
{ "_id" : ObjectId("5192216f975a09f363e3eee9"), "foooos" : 2 }
{ "_id" : ObjectId("5192216f975a09f363e3eeeb"), "foooos" : 3 }
{ "_id" : ObjectId("5192216f975a09f363e3eee4"), "foooos" : 2 }
{ "_id" : ObjectId("5192216f975a09f363e3eee6"), "foooos" : 2 }
{ "_id" : ObjectId("5192216f975a09f363e3eedb"), "foooos" : 2 }
{ "_id" : ObjectId("51922174975a09f363e3ef4a"), "foooos" : 1 }
{ "_id" : ObjectId("5192216f975a09f363e3eee1"), "foooos" : 1 }
{ "_id" : ObjectId("5192216e975a09f363e3eed7"), "foooos" : 2 }
{ "_id" : ObjectId("5192216f975a09f363e3eeee"), "foooos" : 3 }
is there some query that would return the summed up count for foos of all documents ?
i played arround with $sum but dont know how to combine with my query, i only do get syntax errors, it would be cool to know if this is possible
Include the $group operator pipeline stage after the $project step as follows:
db.profil.aggregate([
{ "$match":{ "typ": "Organisation" } },
{ "$project": {
"fooos": { "$size": "$foos" }
} },
{ "$group": {
"_id": null,
"count": {
"$sum": "$fooos"
}
} }
])
This will group all the input documents from the previous $project stage and applies the accumulator expression $sum on the fooos field within the group to get the total (using your last example):
This can also be done by-passing the $project pipeline as:
db.profil.aggregate([
{ "$match": { "typ": "Organisation" } },
{ "$group": {
"_id": null,
"count": {
"$sum": { "$size": "$foos" }
}
} }
])
Output
/* 0 */
{
"result" : [
{
"_id" : null,
"count" : 24
}
],
"ok" : 1
}
I know this is an old question but you can bypass project altogether if you want, so how about this?
db.profil.aggregate([
{
"$match":{ "typ": "Organisation" }
},
{
"$group":
{
"_id": null,
"count":
{
"$sum": { "$size": "$foos" }
}
}
}])
The output remains the same and it seems it's (slightly) faster.