Change value in nested document in MongoDB-array - mongodb

I have document:
{
"_id" : ObjectId("5152ba0708c164359d5ca616"),
"info": "Some interesting information.",
"elements" : [
{
"status" : "accepted",
"id" : "10"
},
{
"status" : "waiting",
"id" : "20"
},
{
"status" : "accepted",
"id" : "30"
}
]
}
How can I modify "status" to "accepted" where "status" is "waiting" and "id" is 20?

use positional ("$") operator.
// single
db.collection.update({ _id: ..., "elements.id": "20" }, { $set: { "elements.$.status": "status" } })
// all
db.collection.update({ "elements.id": "20" }, { $set: { "elements.$.status": "status" } }, { multi: true })

You have to use elemMatch (http://docs.mongodb.org/manual/reference/projection/elemMatch/) . To match both the items of the array like status and id 20 , then you can use the $set command. The reason behind using elemMatch is that it will make sure that both the criteria is matched on the same subdocument before returning the result
db.CollectionName.update({"_id" : ObjectId("5152ba0708c164359d5ca616") , "elements" : { "$elemMatch : {"status" : "waiting" , "Id" : "20"}}},{ $set : {"elements.$.status" : "accepted"}});

Related

Mongodb $nin not working with nested array

I can't make $nin work with nested arrays, can you guys spot any issue with this query?
I'm basically trying to update the status of every document under items to "closed" in case their hash field is not in a list of hashes provided.
db.getCollection('projects').update({
name: 'test',
'issues.hash': { $nin: [
'8ff28fcc9cbf10c9b690bb331e5609efbd3c526be4f536ebca02cc51bd63eac7',
'd5368ad5658ec11103796255d127d26da7f3324cdedbd124bdd5db50812d588e',
'37298229097785ebc9d419cc1a3f13e0d090a15ceb9a8e6bea3505366902556d',
'fad290f2ddd0e097e4098c3b2c3d65611406cf208a3f86924d45c7736393b44b'
]}
},
{
$set: { "issues.$.status": "closed" }
}
)
This is the data:
{
"_id" : ObjectId("611bb4d2ee06769a5f6d906d"),
"name" : "test",
"issues" : [
{
"_id" : ObjectId("611bb4d20b2fb200167aa588"),
"status" : "open",
"hash" : "8ff28fcc9cbf10c9b690bb331e5609efbd3c526be4f536ebca02cc51bd63eac7"
},
{
"_id" : ObjectId("611bb4d20b2fb200167aa589"),
"status" : "open",
"hash" : "3b83e469049e46b16d3471a188d3f5e3ddbf6b296995a71765bbf17b7289e6ea"
},
{
"_id" : ObjectId("611bb4d20b2fb200167aa58a"),
"status" : "open",
"hash" : "bef5f50628b669b9930b89cdc040361b9c8cc2b4aab3c2059c171786d38d507e"
},
{
"_id" : ObjectId("611bb4d20b2fb200167aa58b"),
"status" : "open",
"hash" : "1b4a91eb5de97d6ad7493b6e1ffa48a2a648084b4af7b37916c723533a07c37c"
},
{
"_id" : ObjectId("611bb4d20b2fb200167aa58c"),
"status" : "open",
"hash" : "bb64ba7b2612856dcd95c3ac2fad3f7368e5d463168545b12f4c869af56b55b7"
},
{
"_id" : ObjectId("611bb4d20b2fb200167aa58d"),
"status" : "open",
"hash" : "1d5fc04739b10414dea8d327998df4f200f47ce57da243bd578d4ae102f2d670"
},
{
"_id" : ObjectId("611bb4d20b2fb200167aa58e"),
"status" : "open",
"hash" : "d5368ad5658ec11103796255d127d26da7f3324cdedbd124bdd5db50812d588e"
},
{
"_id" : ObjectId("611bb4d20b2fb200167aa58f"),
"status" : "open",
"hash" : "37298229097785ebc9d419cc1a3f13e0d090a15ceb9a8e6bea3505366902556d"
},
{
"_id" : ObjectId("611bb4d20b2fb200167aa590"),
"status" : "open",
"hash" : "fad290f2ddd0e097e4098c3b2c3d65611406cf208a3f86924d45c7736393b44b"
}
]
}
And the is my result:
Updated 0 record(s) in 12ms
Thank you!
You have to use arrayFilters in this way:
db.collection.update({
"name": "test"
},
{
"$set": {
"issues.$[element].status": "closed"
}
},
{
"arrayFilters": [
{
"element.hash": {
"$nin": [
"8ff28fcc9cbf10c9b690bb331e5609efbd3c526be4f536ebca02cc51bd63eac7",
"d5368ad5658ec11103796255d127d26da7f3324cdedbd124bdd5db50812d588e",
"37298229097785ebc9d419cc1a3f13e0d090a15ceb9a8e6bea3505366902556d",
"fad290f2ddd0e097e4098c3b2c3d65611406cf208a3f86924d45c7736393b44b"
]
}
}
]
})
Example here.
Note that update query has the format: update(query, update, options) (Check the docs).
So with your find query mongo doesn't find anything. Check this example.
This is why you are telling mongo: "Give me a DOCUMENT where name is test and issues array NOT contains a field called hash with these values".
So, as mongo search by the whole document, there is no any document where hash value is not on the $nin array.
As another example to exaplain better: Check this example where hash value is 1. In this case, find query works because it matches two conditions:
There is a field name with value test
There is not any field hash into issues with values into $nin array.
You can use arrayFilters, like this:
db.collection.update({
"name": "test"
},
{
"$set": {
"issues.$[elem].status": "closed"
}
},
{
"multi": true,
"arrayFilters": [
{
"elem.hash": {
"$nin": [
"8ff28fcc9cbf10c9b690bb331e5609efbd3c526be4f536ebca02cc51bd63eac7",
"d5368ad5658ec11103796255d127d26da7f3324cdedbd124bdd5db50812d588e",
"37298229097785ebc9d419cc1a3f13e0d090a15ceb9a8e6bea3505366902556d",
"fad290f2ddd0e097e4098c3b2c3d65611406cf208a3f86924d45c7736393b44b"
]
}
}
]
})
Here is the working example: https://mongoplayground.net/p/8wZkmlBgKiq

MongoDB Convert sigle object to array of object based on key:value

Is there a way by using mongodb queries, convert a sigle object field to an array of object but key:value based?
Before
"fields" : {
"field1" : 1,
"field2" : true,
"field3" : false,
"field4" : "any string value"
}
After
"fields" : [
{
"name" : "field1",
"value": 1
},
{
"name" : "field2",
"value" : true
},
{
"name" : "field3",
"value" : false
},
{
"name" : "field4",
"value" : "any string value"
}
]
You can use $objectToArray in an aggregation pipeline:
db.collection.aggregate([
{
"$set": {
"fields": {
"$objectToArray": "$fields"
}
}
}
])
Example here

MongoDB update :- update multiple objects nested inside array (in multiple documents)

My collection is as follows :
{
"_id" : "",
"team" : "avenger",
"persons" : [
{
"name" : "ABC",
"class" : "10",
"is_new" : true
},
{
"name" : "JHK",
"class" : "12",
"is_new" : true
},
{
"name" : "BNH",
"class" : "10",
"is_new" : true
}
]
},
{
"_id" : "",
"team" : "adrenaline",
"persons" : [
{
"name" : "HTU",
"class" : "11",
"is_new" : true
},
{
"name" : "NVG",
"class" : "10",
"is_new" : true
},
{
"name" : "SED",
"class" : "8",
"is_new" : true
}
]
}
My Goal is to find for all such documents which contain such nested objects in "persons" where "class" is "10" and update "is_new" field to "false"
I am using mongoose update with "multi" set to true
Query :
{
persons: { $elemMatch: { class: "10" } }
}
Options:
{
multi : true
}
Update :
First one I have tried is :
{
$set : {
"persons.$.is_new" : false
}
}
Second one is :
{
$set : {
"persons.$[].is_new" : false
}
}
The issue is : using $ in update operation updates only first match in the "persons" array for all matched documents.
If I use $[], it updates all the objects of "persons" array in the matched documents.
Workaround can be to use a loop for update operation but i believe that should not be the ideal case.
I see nothing in the docs, though have found people reporting this update operation issue.
Is this can't be done using a single query ?? Is looping through documents my only option ?
You need to set the filtered positional operator $[<identifier>] identifier
Model.update(
{ "persons": { "$elemMatch": { "class": "10" }}},
{ "$set" : { "persons.$[e].is_new" : false }},
{ "arrayFilters": [{ "e.class": "10" }], "multi" : true }
)

Mongo aggregation, project a subfield of the first element in the array

I have a sub collection of elements and I want to project a certain subfield of the FIRST item in this collection. I have the following but it only projects the field for ALL elements in the array.
Items is the subcollection of Orders and each Item object has a Details sub object and an ItemName below that. I want to only return the item name of the FIRST item in the list. This returns the item name of every item in the list.
How can I tweak it?
db.getCollection('Orders').aggregate
([
{ $match : { "Instructions.1" : { $exists : true }}},
{ $project: {
_id:0,
'UserId': '$User.EntityId',
'ItemName': '$Items.Details.ItemName'
}
}
]);
Updated:
{
"_id" : "order-666156",
"State" : "ValidationFailed",
"LastUpdated" : {
"DateTime" : ISODate("2017-09-26T08:54:16.241Z"),
"Ticks" : NumberLong(636420128562417375)
},
"SourceOrderId" : "666156",
"User" : {
"EntityId" : NumberLong(34450),
"Name" : "Bill Baker",
"Country" : "United States",
"Region" : "North America",
"CountryISOCode" : "US",
},
"Region" : null,
"Currency" : null,
"Items" : [
{
"ClientOrderId" : "18740113",
"OrigClientOrderId" : "18740113",
"Quantity" : NumberDecimal("7487.0"),
"TransactDateTime" : {
"DateTime" : Date(-62135596800000),
"Ticks" : NumberLong(0)
},
"Text" : null,
"LocateRequired" : false,
"Details" : {
"ItemName" : "Test Item 1",
"ItemCost" : 1495.20
}
},
{
"ClientOrderId" : "18740116",
"OrigClientOrderId" : "18740116",
"Quantity" : NumberDecimal("241.0"),
"TransactDateTime" : {
"DateTime" : Date(-62135596800000),
"Ticks" : NumberLong(0)
},
"Text" : null,
"LocateRequired" : false,
"Details" : {
"ItemName" : "Test Item 2",
"ItemCost" : 2152.64
}
}
]
}
In case your are using at least MongoDB v3.2 you can use the $arrayElemAt operator for that. The below query does what you want. It will, however, not return any data for the sample you provided because the "Instructions.1": { $exists: true } filter removes the sample document.
db.getCollection('Orders').aggregate([{
$match: {
"Instructions.1": {
$exists: true
}
}
}, {
$project: {
"_id": 0,
"UserId": "$User.EntityId",
"ItemName": { $arrayElemAt: [ "$Items.Details.ItemName", 0 /* first item! */] }
}
}])

MongoDB query with multiple conditions

I have data with multiple documents :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e487"),
"empId" : "2"
"type" : "Admin",
"city" : "Mumbai"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e488"),
"empId" : "3"
"type" : "Admin",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e489"),
"empId" : "4"
"type" : "User",
"city" : "Mumbai"
}
I want to get data according to my multiple conditions :
condition 1:- {"type" : "WebUser", "city" : "Pune"}
condition 2:- {"type" : "WebUser", "city" : "Pune"} & {"type" : "User", "city" : "Mumbai"}
I want below result when run condition 1 :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
When I run second condition :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e489"),
"empId" : "4"
"type" : "User",
"city" : "Mumbai"
}
I want above result by one query,
Currently I am using below aggregate query,
db.emp.aggregate([
{ $match: { '$and': [
{"type" : "WebUser", "city" : "Pune"},
{"type" : "User", "city" : "Mumbai"}
] } },
{ $group: { _id: 1, ids: { $push: "$empId" } } }
])
Above query work for first condition & fails for other. Please help me.
For the second condition, you can use the $in operator in your query as:
db.emp.find({
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
})
If you want to use in aggregation:
db.emp.aggregate([
{
"$match": {
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
}
},
{ "$group": { "_id": null, "ids": { "$push": "$empId" } } }
])
or simply use the distinct() method to return an array of distinct empIds that match the above query as:
var employeeIds = db.emp.distinct("empId", {
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
});
If you are looking for the AND operator
This example checks if a field exists AND is null
db.getCollection('TheCollection').find({
$and: [
{'the_key': { $exists: true }},
{'the_key': null}
]
})
This example checks if a field has 'value1' OR 'value2'
db.getCollection('TheCollection').find({
$or: [
{'the_key': 'value1'},
{`the_key': 'value2'}
]
})
When just checking for null, the return contains non-existing fields plus fields with value null
db.getCollection('TheCollection').find({'the_key': null})
You can use mongo db $or operator.
db.emp.find({ $or: [
{ "type": "WebUser", "city": "Pune" },
{ "type": "user", "city": "Mumbai"}
]})
You can pass conditions in the array.
For more reference see mongo docs
Display the document where in the “StudName” has value “Ajay Rathod”.
db.Student.find({name:"ajay rathod"})
{ "_id" : ObjectId("5fdd895cd2d5a20ee8cea0de"), "
Retrieve only Student Name and Grade.
db.Student.find({},{name:1,grade:1,_id:0})
{ "name" : "dhruv", "grade" : "A" }
{ "name" : "jay", "grade" : "B" }
{ "name" : "abhi", "grade" : "C" }
{ "name" : "aayush", "grade" : "A" }
{ "name" : "sukhdev", "grade" : "B" }
{ "name" : "dhruval", "grade" : "B" }
{ "name" : "ajay rathod", "grade" : "D" }