How to projection element in multi array field of MongoDb collection? - arrays

Is there a way to get only specific field values ​​from a multi array?
db.test.insert({'id':'stack', 'nums':[{'name':'Dave', 'num': 1, 'phones' :[{'id':1, 'number':'000-000'}, {'id':2, 'number':'000-001'}]}]})
{
"_id" : "63b3896a5b5d9de96b6277fa",
"id" : "stack",
"nums" : [
{
"name" : "Dave",
"num" : 1,
"phones" : [
{
"id" : 1,
"number" : 0
},
{
"id" : 2,
"number" : -1
}
]
}
]
}
the answer i want
{'id':1, 'number':'000-000'}
I want to print only the value with 'id' 1 in 'phones'.

db.collection.aggregate([
{
"$unwind": "$nums"
},
{
$project: {
"phones": {
$filter: {
input: "$nums.phones",
cond: {$eq: ["$$this.id",1]
}
}
}
}
}
])
Playground

Related

MongoDB: retrieve only certain properties of a document after matching

I have a MongoDB collection called books.
An example of a document is:
{
"_id" : ObjectId("62bf10951fecaed4dba275b1"),
"name" : "Library 1",
"positions" : [
{
"number" : 2,
"nodes" : [
{
"number" : 2,
"bookId" : "6254674d3711f90bd8e76036"
},
{
"number" : 1,
"bookId" : "621e9b5aa7951d0be4516c18"
}
]
},
{
"number" : 1,
"nodes" : [
{
"number" : 1,
"bookId" : "6254674d3711f90bd8e76037"
},
{
"number" : 3,
"bookId" : "6254674d3711f90bd8e76039"
},
{
"number" : 2,
"bookId" : "6254674d3711f90bd8e76035"
}
]
}
]
}
I need to run a query that, based on a book ID, returns the name, the positions.number and the positions.node.number.
For example, if I search for the ID 6254674d3711f90bd8e76035, it should return:
{
_id: ObjectId("62bf10951fecaed4dba275b1"),
name: "Library 1",
positions: {
number: 1,
nodes: {
number: 2
}
}
}
So far, this is what I came out with:
db.getCollection('books').aggregate([
{ $match: { "positions.nodes.bookId": "6254674d3711f90bd8e76035" } },
{ $project: { name: 1, "positions.number": 1, "positions.nodes.number": 1 } }
])
Unfortunately, this returns every single node. I might need something that says:
"Select name, position.number, position.nodes.number where bookId = 6254674d3711f90bd8e76035"
Any help is appreciated.
Thanks
Solved:
db.getCollection('books').aggregate([
{
$match: {"positions.nodes.books": "6254674d3711f90bd8e76035"}
},
{
$unwind: "$positions"
},
{
$unwind: "$positions.nodes"
},
{
$match: {"positions.nodes.books": "6254674d3711f90bd8e76035"}
},
])

How to update an array and pull a nested element from same array

With the following document:
{
"_id" : "123",
"firstArray" : [
{
"_id" : "456",
"status" : "open",
"nestedArray" : [
{
"_id" : "100",
"quantity" : 10
},
{
"_id" : "101",
"quantity" : 10
},
{
"_id" : "102",
"quantity" : 10
}
},
{
"_id" : "789",
"status" : "open",
"nestedArray" : [
{
"_id" : "200",
"quantity" : 10
},
{
"_id" : "201",
"quantity" : 10
},
{
"_id" : "202",
"quantity" : 10
}
}
]
}
How can I update the quantity by 20 of the nested ID 101 element and pull the one with the ID 201 from the same MongoDB query ?
I am trying to do that in Java with $set and $pull operator and I'm stuck with the following error:
[BulkWriteError{index=0, code=40, message='Update created a conflict
at 'firstArray.0.nestedArray'', details={}}]
MongoDB doesn’t allow multiple operations on the same property in the same update call. This means that the two operations must happen in two individual queries.
The first solution is you can write 2 seperate queries for both the operations.
The second solution is you can try update with aggregation pipeline, starting from MongoDB 4.2,
$map to iterate loop of firstArray
$filter to iterate loop of nestedArray and remove _id: "201" record
$map to iterate loop of above filtered nestedArray
$cond check condition if _id: "101" then return new quantity otherwise return current
$mergeObjects to merge current object with updated properties
db.collection.update(
{ "firstArray.nestedArray._id": "101" },
[{
$set: {
firstArray: {
$map: {
input: "$firstArray",
in: {
$mergeObjects: [
"$$this",
{
nestedArray: {
$map: {
input: {
$filter: {
input: "$$this.nestedArray",
cond: { $ne: ["$$this._id", "201"] }
}
},
in: {
_id: "$$this._id",
quantity: {
$cond: [
{ $eq: ["$$this._id", "101"] },
20,
"$$this.quantity"
]
}
}
}
}
}
]
}
}
}
}
}
])
Playground

Navigate thought a lots of array and update value of object with mongo query

Hey guys I'm trying to update a value that is in an array in MongoDB, am trying to use the mongo queries but is not working, am following the next documentation from Mongo doc
this one is the array:
{
"_id" : "605e3d9b9ef219de662113d0",
"distribution" : [
{
"floor" : 1,
"rooms" : [
{
"number" : 301,
"beds" : [
{
"number" : 818,
"status" : "Vacante Sucia"
},
{
"number" : 819,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
},
{
"number" : 302,
"beds" : [
{
"number" : 820,
"status" : "Vacante Sucia"
},
{
"number" : 821,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
},
{
"number" : 303,
"beds" : [
{
"number" : 822,
"status" : "Vacante Sucia"
},
{
"number" : 823,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
}
]
}
],
"name" : "Meteorologia",
"code" : "METEO"
}
this one is the query that is using in mongoDB to update the status from the bed 801, room 301, floor 1:
in the arrayFilters i specified the index 0 to get the first element of the arrays
db.getCollection('establishments_copy').findAndModify({query: { code: "METEO"}, update: { $set: { "distribution.$[i].rooms.$[i].beds.$[i].status": "TEST"}}, arrayFilters: [{"i.rooms": 0, "i.beds": 0, "i.status": 0}]})
they are returning me the collection but without changes, is possible to navigate validating not for the index just with the values.
for example using the next query:
db.getCollection('establishments_copy').findAndModify({query: { code: 'METEO', distribution: { $elemMatch: { floor: 1, 'rooms.number': 301, 'rooms.beds.number': 818}}}, update: { $set: { '...': 'CHANGED'}}})
thanks!
You just need to create separate condition as per sub document's field name,
f for floor field in distribution array
r for number field in rooms array
b for number field in beds array
db.getCollection('establishments_copy').findAndModify({
query: { code: "METEO"},
update: {
$set: {
"distribution.$[f].rooms.$[r].beds.$[b].status": "TEST"
}
},
arrayFilters: [
{ "f.floor": 1 },
{ "r.number": 301 },
{ "b.number": 818 }
]
})
Playground

How to match and retrieve specific Sub-document value from Mongo

How can I search and retrieve only "Stats.item.id" from this collection who have greater than zero "Stats.item.p" value. I am also facing problem in unwinding this collection.
{
"_id" : "8643",
"Stats" : [
{
"date" : ISODate("2014-02-01"),
"Stats" : {
"item" : [
{
"id" : "4356"
},
{
"id" : "9963",
"p" : NumberInt(1)
}
]
}
}
]
}
{
"_id" : "8643",
{
"date" : ISODate("2014-02-01"),
"Stats" : {
"item" : [
{
"id" : "9963",
"p" : NumberInt(1)
}
}
This is the output I expect. Can anyone help me write this aggregation? oooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooo oooooooooooooooooooo oooooooooooo oooooooo
Hope this aggregation query will work
db.collection.aggregate([
{$unwind:'$Stats'},
{$unwind:'$Stats.Stats'},
{$unwind:'$Stats.Stats.item'},
{$match:{
'Stats.Stats.item.p':{$gt:0}
}},
{$group:{
_id:{
date:'$Stats.date'
},
item:{$push:'$Stats.Stats.item'},
_ids:'$_id'
}},
{$group:{
_id:{_ids:'$_ids',
date:'$_id.date'},
Stats:{$push:{
item:'$item'
}},
}},
{$project:{
_id:'$_id._ids',
Stats:{
date:'$_id.date'
Stats:'$Stats'
}
}}
])
Use $elemMAtch for searching in the array.
db.getCollection('CollectionName').find({
"Stats": {
"$elemMatch": {
"Stats.item": {
"$elemMatch": {
"p": 1
}
}
}
}
})

Get Multiple Objects From Array in different Documents MongoDb

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]
]
}
}
}])

Resources