update specific map with multiple condition in mongo array - mongodb

{
"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"
}
}
)

Related

How to search mongodb

I have two JSONs in a collection in mongodb and would like to write a bson.M filter to fetch the first JSON below.
I tried with the filter below to get the first JSON but got no result.
When the first JSON is in the collection, I get result when i use the filter
but when i have both JSONs, I do not get a result. Need help.
filter := bson.M{"type": "FPF", "status": "REGISTERED","fpfInfo.fpfInfoList.ssai.st": 1, "fpfInfo.fpfInfoList.infoList.dn": "sim"}
{
"_id" : "47f6ad68-d431-4b69-9899-f33d828f8f9c",
"type" : "FPF",
"status" : "REGISTERED",
"fpfInfo" : {
"fpfInfoList" : [
{
"ssai" : {
"st" : 1
},
"infoList" : [
{
"dn" : "sim"
}
]
}
]
}
},
{
"_id" : "347c8ed2-d9d1-4f1a-9672-7e8a232d2bf8",
"type" : "FPF",
"status" : "REGISTERED",
"fpfInfo" : {
"fpfInfoList" : [
{
"ssai" : {
"st" : 1,
"ds" : "000004"
},
"infoList" : [
{
"dn" : "sim"
}
]
}
]
}
}
db.collection.aggregate([
{
"$unwind": "$fpfInfo.fpfInfoList"
},
{
"$match": {
"fpfInfo.fpfInfoList.ssai.ds": {
"$exists": false
},
"fpfInfo.fpfInfoList.infoList.dn": "sim",
"fpfInfo.fpfInfoList.ssai.st": 1
}
}
])
Playground

Update a nested array objects in a different collection and position in MongoDB

I have a douments like as follows.
How do I update a skillcluster name. Suppose the other document has name :"c" in 4th position.
{
Job: {
post: { name:"x" }
skill: {
skillcluster: [
{name:"c++",id:"23"},
{name:"c",id:"898"}
]
}
}
}
{
Job: {
post: { name:"x" }
skill: {
skillcluster: [
{name:"c++",id:"23"},
{name:"java"},
{name:"python"},
{name:"c",id:"898"}
]
}
}
}
You need to query to match the "name" field at the embedded level of the document using "dot notation", and then pass that match with the positional $ operator within the update:
db.collection.update(
{ "Job.skill.skillcluster.name": "c" },
{ "$set": { "Job.skill.skillcluster.$.name": "Simple C"}},
{ "multi": true }
)
Also use the "multi" flag to match and update more than one document.
The result will be:
{
"_id" : ObjectId("55dbfd0ed96d655eb0ed2b4f"),
"Job" : {
"post" : {
"name" : "x"
},
"skill" : {
"skillcluster" : [
{
"name" : "c++",
"id" : "23"
},
{
"name" : "Simple C",
"id" : "898"
}
]
}
}
}
{
"_id" : ObjectId("55dbfd0ed96d655eb0ed2b50"),
"Job" : {
"post" : {
"name" : "x"
},
"skill" : {
"skillcluster" : [
{
"name" : "c++",
"id" : "23"
},
{
"name" : "java"
},
{
"name" : "python"
},
{
"name" : "Simple C",
"id" : "898"
}
]
}
}
}

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!

MongoDB pull element from array two levels deep

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.

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.