MongoDB pull element from array two levels deep - mongodb

I have a collection with the following items :
{ "Queries" : [ { "Results" : [ { "id" : 1 }, { "id" : 2 } ] } ], "_id" : ObjectId("51ddb6f9b18996be485cba6f") }
{ "Queries" : [ { "Results" : [ { "id" : 0 }, { "id" : 3 } ] } ], "_id" : ObjectId("51ddb701b18996be485cba70") }
{ "Queries" : [ { "Results" : [ { "id" : 1 }, { "id" : 2 } ] } ], "_id" : ObjectId("51ddb705b18996be485cba71") }
{ "Queries" : [ { "Results" : [ { "id" : 1 }, { "id" : 2 }, { "id" : 4 } ] } ], "_id" : ObjectId("51ddb70db18996be485cba72") }
{ "Queries" : [ { "Results" : [ { "id" : 1 }, { "id" : 2 }, { "id" : null } ] } ], "_id" : ObjectId("51ddb7e4b18996be485cba73") }
The "Queries" field on my documents contains an array of subdocuments. These subdocuments contain an array of another subdocument.
I want to remove all entries in the "Results" field where the documents "id" field is 1.
I tried the following without success :
update({}, {$pull :{"Queries.Results": {"id":1}}}, {"multi":true})
update({}, {$pull :{"Queries.Results.id":1}}, {"multi":true})
How would I achieve this in MongoDB?
EDIT: Here is the expected result of find() after the update
{ "Queries" : [ { "Results" : [ { "id" : 2 } ] } ], "_id" : ObjectId("51ddb6f9b18996be485cba6f") }
{ "Queries" : [ { "Results" : [ { "id" : 0 }, { "id" : 3 } ] } ], "_id" : ObjectId("51ddb701b18996be485cba70") }
{ "Queries" : [ { "Results" : [ { "id" : 2 } ] } ], "_id" : ObjectId("51ddb705b18996be485cba71") }
{ "Queries" : [ { "Results" : [ { "id" : 2 }, { "id" : 4 } ] } ], "_id" : ObjectId("51ddb70db18996be485cba72") }
{ "Queries" : [ { "Results" : [ { "id" : 2 }, { "id" : null } ] } ], "_id" : ObjectId("51ddb7e4b18996be485cba73") }

This is the query you have to use:
db.collection.update( { "Queries.Results.id":1 }, { $pull: { "Queries.$.Results": {"id":1} } } )
You need to specify the "where" clause in order to find the document to update.
You are also missing the positional operator $, you have to use it because Queries can have multiple Results.

Related

How to write mongo query

How I can get the total number of seats available for a particular movie (seats present in all the theatres for that movie) from the mongodb schema below.
I need to write a mongo query to get the results
{
"_id" : ObjectId("5d637b5ce27c7d60e5c42ae7"),
"name" : "Bangalore",
"movies" : [
{
"name" : "KGF",
"theatres" : [
{
"name" : "PVR",
"seats" : 45
},
{
"name" : "IMAX",
"seats" : 46
}
]
},
{
"name" : "Avengers",
"theatres" : [
{
"name" : "IMAX",
"seats" : 50
}
]
}
],
"_class" : "com.BMS_mongo.ZZ_BMS_mongo_demo.Entity.CityInfo"
}
I have written this code :
db.cities.aggregate( [
{ "$unwind" : "$movies" }, { "$unwind" : "$theatres" } ,
{ "$group" : { _id : "$movies.theatre`enter code here`s.seats" ,
total : { "$sum" : "$seats" } }
}
] )
My schema:
The following query can get us the expected output:
db.collection.aggregate([
{
$unwind:"$movies"
},
{
$unwind:"$movies.theatres"
},
{
$group:{
"_id":"$movies.name",
"movie":{
$first:"$movies.name"
},
"totalSeats":{
$sum:"$movies.theatres.seats"
}
}
},
{
$project:{
"_id":0
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5d637b5ce27c7d60e5c42ae7"),
"name" : "Bangalore",
"movies" : [
{
"name" : "KGF",
"theatres" : [
{
"name" : "PVR",
"seats" : 45
},
{
"name" : "IMAX",
"seats" : 46
}
]
},
{
"name" : "Avengers",
"theatres" : [
{
"name" : "IMAX",
"seats" : 50
}
]
}
],
"_class" : "com.BMS_mongo.ZZ_BMS_mongo_demo.Entity.CityInfo"
}
Output:
{ "movie" : "Avengers", "totalSeats" : 50 }
{ "movie" : "KGF", "totalSeats" : 91 }
Query:
db.movie.aggregate([{ $unwind: { path: "$movies",} },
{ $unwind: { path: "$movies.theatres",} },
{ $group: { _id: "$movies.name", "moviename": { $first: "$movies.name" },
"totalSeats": { $sum: "$movies.theatres.seats" }} }])
I got the answer using this query ...
db.cities.aggregate( [
{ "$match" : { "name" : "Bangalore" } },
{ "$unwind" : "$movies" } ,
{ "$match" : {"movies.name" : "KGF"} },
{ "$unwind" : "$theatres" },
{ "$group" : { _id : "$movies.name", total : { "$sum" : "$movies.theatres.seats"
} } }
] )

update specific map with multiple condition in mongo array

{
"dictID" : "37528e10-6344-4d93-8a57-35af0bdb6b34",
"dictVersion" : 1,
"addMeasures" : [
{
"measureId" : "f229ba18-0de8-47a1-8a87-88c95edd536a",
"userId" : "3966C3DD-8328-4F18-A061-02A01111763A",
"created" : "2017-01-07T05:47:22.512Z",
"ownerAction" : "NA"
}
],
"deleteMeasures" : [
{
"measureId" : "0b701469-1502-4de4-95de-1ee70ad6c577",
"userId" : "3966C3DD-8328-4F18-A061-02A01111763A",
"created" : "2017-01-07T05:47:35.193Z",
"ownerAction" : "NA"
},
{
"measureId" : "443d1b97-95ae-4410-9302-da3edbad4004",
"userId" : "3966C3DD-8328-4F18-A061-02A01111763A",
"created" : "2017-01-07T05:47:36.062Z",
"ownerAction" : "NA"
},
{
"measureId" : "aa1689c9-8df6-4bff-88a4-274a4f2dc2aa",
"userId" : "3966C3DD-8328-4F18-A061-02A01111763A",
"created" : "2017-01-07T05:47:37.075Z",
"ownerAction" : "NA"
}
]
}
Above is my mongo collection and i want to do the following query:
db.getCollection('DataDictionaryReview').update(
{
$and: [
{ dictID: "37528e10-6344-4d93-8a57-35af0bdb6b34" },
{ dictVersion: 1 },
{ "deleteMeasures.measureId": "aa1689c9-8df6-4bff-88a4-274a4f2dc2aa" },
{ "deleteMeasures.ownerAction": "NA" }
]
},
{
$set: {
"deleteMeasures.$.created": "2017-01-07T06:51:56.983Z",
"deleteMeasures.$.ownerAction": "Reject"
}
}
)
I want to update the last map of deleteMeasures but every time the query is updating first map of deleteMeasures.
This is strange, the query looks okay to me, however I think it needs $elemMatch to get the correct index value.
db.getCollection('DataDictionaryReview').update(
{
dictID: "37528e10-6344-4d93-8a57-35af0bdb6b34",
dictVersion: 1 ,
"deleteMeasures" : { $elemMatch : {"measureId": "aa1689c9-8df6-4bff-88a4-274a4f2dc2aa", "ownerAction": "NA" }}
},
{
$set: {
"deleteMeasures.$.created": "2017-01-07T06:51:56.983Z",
"deleteMeasures.$.ownerAction": "Reject"
}
}
)

Create object in aggregation

I have this result after group by UserId with Aggregation Framework. I want create a single Document with "merge" array foo and ability
My Aggregation
{
"result" : [
{
"_id" : {
"userId" : ObjectId("53bab268ceee750615240269")
},
"foo" : [
"0.109",
"0.105",
"0.50",
"0.1",
"foo"
],
"ability" : [
"Power",
"Energy",
"ReactiveEnergy",
"Stamina",
"bar"
]
}
],
"ok" : 1
}
I like to have
{
"result" : [
{
"_id" : {
"userId" : ObjectId("53bab268ceee750615240269")
},
"fooFinal" : [
{"0.109",power}
{"0.105",Energy }
{"0.50",ReactiveEnergy }
{"0.1",Stamina }
{"foo",bar}
],
}
],
"ok" : 1
}
I want to use like similar $each, but i can't use it inside aggregate
Try to do This in your $group
$group:{
_id:{'userId':'$userId'},
'fooFinal' : {$push:{
foo:'$foo',
ability:'$ability'}
}
}

Update Array Fields

I have a Document Structure as shown below
let's say for a tree structure like ORDERS.MAIN_ORDERS.P1, this ;basically created 3 document showing parents for each node
{
"_id" : ObjectId("5362a7fd300400ffbabc3754"),
"categoryid" : "ORDERS",
"ancestors" : [],
"availableLocales" : [
{
"locale" : "en_US",
"count" : 1
}
],
"rolledUpCount" : 1
}
{
"_id" : ObjectId("5362a7fd300400ffbabc3755"),
"categoryid" : "MAIN_ORDERS",
"ancestors" : [
"ORDERS"
],
"availableLocales" : [
{
"locale" : "en_US",
"count" : 1
}
],
"rolledUpCount" : 1
}
{
"_id" : ObjectId("5362a7fd300400ffbabc3756"),
"categoryid" : "P1",
"ancestors" : [
"ORDERS",
"MAIN_ORDERS"
],
"availableLocales" : [
{
"locale" : "en_US",
"count" : 1
}
],
"rolledUpCount" : 1
}
When a new tree comes in(e;g ORDERS.MAIN_ORDERS.P2) i need to increment the count of all the parents for that categoryid( P2) for that particular locale. how do i do that with mongoQuery?
You can try to update using the following:
db.mycollection.update(
{ "availableLocales.locale" : { "$in": ["en_US"] } },
{ "$inc" : { "availableLocales.count" : 1 } }
})
This update uses the $in and $inc operators.
Find collection where availableLocales holds an array containing locale with "en_US" value.
{ "availableLocales.locale" : { "$in": ["en_US"] } }
Increment the value of the count field by 1. Note that you can pass multi:true to update all matching documents, not just one.
{ "$inc" : { "availableLocales.count" : 1 } }
Please see the docs for more info on how these works.
--- more info:
Update ORDERS:
db.mycollection.update(
{
"categoryid" : "ORDERS",
"availableLocales.locale" : { "$in": ["en_US"] }
},
{ "$inc" : { "availableLocales.count" : 1 } }
})
Update MAIN_ORDERS:
db.mycollection.update(
{ "categoryid" : "MAIN_ORDERS", "availableLocales.locale" : { "$in": ["en_US"] } },
{ "$inc" : { "availableLocales.count" : 1 } }
})
Hope that this clarifies things further. It should point you in the right direction!

Aggregate of different subtypes in document of a collection

abstract document in collection md given:
{
vals : [{
uid : string,
val : string|array
}]
}
the following, partially correct aggregation is given:
db.md.aggregate(
{ $unwind : "$vals" },
{ $match : { "vals.uid" : { $in : ["x", "y"] } } },
{
$group : {
_id : { uid : "$vals.uid" },
vals : { $addToSet : "$vals.val" }
}
}
);
that may lead to the following result:
"result" : [
{
"_id" : {
"uid" : "x"
},
"vals" : [
[
"24ad52bc-c414-4349-8f3a-24fd5520428e",
"e29dec2f-57d2-43dc-818a-1a6a9ec1cc64"
],
[
"5879b7a4-b564-433e-9a3e-49998dd60b67",
"24ad52bc-c414-4349-8f3a-24fd5520428e"
]
]
},
{
"_id" : {
"uid" : "y"
},
"vals" : [
"0da5fcaa-8d7e-428b-8a84-77c375acea2b",
"1721cc92-c4ee-4a19-9b2f-8247aa53cfe1",
"5ac71a9e-70bd-49d7-a596-d317b17e4491"
]
}
]
as x is the result aggregated on documents containing an array rather than a string, the vals in the result is an array of arrays. what i look for in this case is to have a flattened array (like the result for y).
for me it seems like that what i want to achieve by one aggegration call only, is currently not supported by any given operation as e.g. a type conversion cannot be done or unwind expectes in every case an array as input type.
is map reduce the only option i have? if not ... any hints?
thanks!
You can use the aggregation to do the computation you want without changing your schema (though you might consider changing your schema simply to make queries and aggregations of this field easier to write).
I broke up the pipeline into multiple steps for readability. I also simplified your document slightly, again for readability.
Sample input:
> db.md.find().pretty()
{
"_id" : ObjectId("512f65c6a31a92aae2a214a3"),
"uid" : "x",
"val" : "string"
}
{
"_id" : ObjectId("512f65c6a31a92aae2a214a4"),
"uid" : "x",
"val" : "string"
}
{
"_id" : ObjectId("512f65c6a31a92aae2a214a5"),
"uid" : "y",
"val" : "string2"
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a6"),
"uid" : "y",
"val" : [
"string3",
"string4"
]
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a7"),
"uid" : "z",
"val" : [
"string"
]
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a8"),
"uid" : "y",
"val" : [
"string1",
"string2"
]
}
Pipeline stages:
> project1 = {
"$project" : {
"uid" : 1,
"val" : 1,
"isArray" : {
"$cond" : [
{
"$eq" : [
"$val.0",
[ ]
]
},
true,
false
]
}
}
}
> project2 = {
"$project" : {
"uid" : 1,
"valA" : {
"$cond" : [
"$isArray",
"$val",
[
null
]
]
},
"valS" : {
"$cond" : [
"$isArray",
null,
"$val"
]
},
"isArray" : 1
}
}
> unwind = { "$unwind" : "$valA" }
> project3 = {
"$project" : {
"_id" : 0,
"uid" : 1,
"val" : {
"$cond" : [
"$isArray",
"$valA",
"$valS"
]
}
}
}
Final aggregation:
> db.md.aggregate(project1, project2, unwind, project3, group)
{
"result" : [
{
"_id" : "z",
"vals" : [
"string"
]
},
{
"_id" : "y",
"vals" : [
"string1",
"string4",
"string3",
"string2"
]
},
{
"_id" : "x",
"vals" : [
"string"
]
}
],
"ok" : 1
}
If you modify your schema using always "vals.val" field as an array field (even when the record contains only one element) you can do it easily as follows:
db.test_col.insert({
vals : [
{
uid : "uuid1",
val : ["value1"]
},
{
uid : "uuid2",
val : ["value2", "value3"]
}]
});
db.test_col.insert(
{
vals : [{
uid : "uuid2",
val : ["value4", "value5"]
}]
});
Using this approach you only need to use two $unwind operations: one unwinds the "parent" array and the second unwinds every "vals.val" value. So, querying like
db.test_col.aggregate(
{ $unwind : "$vals" },
{ $unwind : "$vals.val" },
{
$group : {
_id : { uid : "$vals.uid" },
vals : { $addToSet : "$vals.val" }
}
}
);
You can obtain your expected value:
{
"result" : [
{
"_id" : {
"uid" : "uuid2"
},
"vals" : [
"value5",
"value4",
"value3",
"value2"
]
},
{
"_id" : {
"uid" : "uuid1"
},
"vals" : [
"value1"
]
}
],
"ok" : 1
}
And no, you can't execute this query using your current schema, since $unwind fails when the field isn't an array field.