For a given document, I am attempting to set a field (string) as the first element of an array of strings. Consider a collection myPlaces with the following document:
{
"name" : "Place Q",
"images" : [
"foo-1",
"foo-2",
"foo-3"
]
}
I am looking to create the following:
{
"name" : "Place Q",
"images" : [
"foo-1",
"foo-2",
"foo-3"
],
"image" : "foo-1"
}
So in the mongo shell, I execute:
db.myPlaces.update(
{
name: "Place Q"
},
{
$set:
{
image: images.0
}
}
)
This throws the error SyntaxError: missing } after property list which I don't understand. If I enclose the array in quotes "images.0" the value of image is set as "images.0", not "foo-1".
I've been reviewing the Mongodb documentation and see a lot of different examples but not something that makes clear to me what is probably a simple syntax for accessing an array value.
The following query works on MongoDB 4.2, It might not work on previous versions.
db.myPlaces.update(
{
name: "Place Q"
},
[
{
$set: {
image: {
$arrayElemAt: ["$images",0]
}
}
}
]
)
It's also very efficient as it is using $set pipeline provided in MongoDB 4.2+ versions.
Can you try this please :
db.myPlaces.update(
{
name: "Place Q"
},{
$set:
{
image: db.myPlaces.find( { "images.0" } )
}
}
)
Here what you can do is:
Find the document first.
Iterate on that (You must have only one value, but it will work for multiple documents as well)
Update document.
Note:- Efficient for the small records. Bulk record will have lots of traffic on DB.
===== Will work for all version of MongoDB ======
Data Before:
{
"_id" : ObjectId("5d53fda5f5fc3d6486b42fec"),
"name" : "Place Q",
"images" : [
"foo-1",
"foo-2",
"foo-3"
]
}
Query:
db.collection.find({ name: "Place Q" }).forEach(function(document) {
db.collection.update(
{},
{ "$set": { "image": document.images[0] } }
);
})
Data After
{
"_id" : ObjectId("5d53fda5f5fc3d6486b42fec"),
"name" : "Place Q",
"images" : [
"foo-1",
"foo-2",
"foo-3"
],
"image" : "foo-1"
}
Related
I have a Meteor Mongo document as shown below
{
"_id" : "zFndWBZTvZPgSKXHP",
"activityId" : "aRDABihAYFoAW7jbC",
"activityTitle" : "Test Mongo Document",
"users" : [
{
"id" : "b1#gmail.com",
"type" : "free"
},
{
"id" : "JqKvymryNaCjjKrAR",
"type" : "free"
},
],
}
I want to update a specific array element's email with custom generated id using Meteor query something like the below.
for instance, I want to update the document
if 'users.id' == "b1#gmail.com" then update it to users.id = 'SomeIDXXX'
So updated document should looks like below.
{
"_id" : "zFndWBZTvZPgSKXHP",
"activityId" : "aRDABihAYFoAW7jbC",
"activityTitle" : "Test Mongo Document",
"users" : [
{
"id" : "SomeIDXXX",
"type" : "free"
},
{
"id" : "JqKvymryNaCjjKrAR",
"type" : "free"
},
],
}
I have tried the below but didnt work.
Divisions.update(
{ activityId: activityId, "users.id": emailId },
{ $set: { "users": { id: _id } } }
);
Can someone help me with the relevant Meteor query ? Thanks !
Your query is actually almost right except for a small part where we want to identify the element to be updated by its index.
Divisions.update({
"activityId": "aRDABihAYFoAW7jbC",
"users.id": "b1#gmail.com"
}, {
$set: {"users.$.id": "b2#gmail.com"}
})
You might need the arrayFilters option.
Divisions.update(
{ activityId: activityId },
{ $set: { "users.$[elem].id": "SomeIDXXX" } },
{ arrayFilters: [ { "elem.id": "b1#gmail.com" } ], multi: true }
);
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/
You need to use the $push operator instead of $set.
{ $push: { <field1>: <value1>, ... } }
Here is my news document structure
{
"_id" : ObjectId("5bff0903bd9a221229c7c9b2"),
"title" : "Test Page",
"desc" : "efdfr",
"mediaset_list" : [
{
"_id" : ObjectId("5bfeff94bd9a221229c7c9ae"),
"medias" : [
{
"_id" : ObjectId("5bfeff83bd9a221229c7c9ac"),
"file_type" : "png",
"file" : "https://aws.com/gg.jpg",
"file_name" : "edf.jpg"
},
{
"_id" : ObjectId("5bfeff83bd9a221229c7c9ad"),
"file_type" : "mov",
"file" : "https://aws.com/gg.mov",
"file_name" : "abcd.mov"
}
]
}
]}
The queries that i've tried are given below
Approach 1
db.news.find_and_modify({},{'$pull': {"mediaset_list": {"medias": {"$elemMatch" : {"_id": ObjectId('5bfeff83bd9a221229c7c9ac')}} }}})
Approach 2
db.news.update({},{'$pull': {"mediaset_list.$.medias": {"_id": ObjectId('5bfeff83bd9a221229c7c9ac')}} })
Issue we are facing
The above queries are removing entire elements inside 'mediaset_list' . But i only want to remove the element inside 'medias' matching object ID.
Since you have two nested arrays you have to use arrayFilters to indicate which element of outer array should be modified, try:
db.news.update({ _id: ObjectId("5bff0903bd9a221229c7c9b2") },
{ $pull: { "mediaset_list.$[item].medias": { _id: ObjectId("5bfeff83bd9a221229c7c9ad") } } },
{ arrayFilters: [ { "item._id": ObjectId("5bfeff94bd9a221229c7c9ae") } ] })
So item is used here as a placeholder which will be used by MongoDB to determine which element of mediaset_list needs to be modified and the condition for this placeholder is defined inside arrayFilters. Then you can use $pull and specify another condition for inner array to determine which element should be removed.
From #micki's mongo shell query (Answer above) , This is the pymongo syntax which will update all news document with that media id .
db.news.update_many({},
{
"$pull":
{ "mediaset_list.$[item].medias": { "_id": ObjectId("5bfeff83bd9a221229c7c9ad") } } ,
},
array_filters=[{ "item._id": ObjectId("5bfeff94bd9a221229c7c9ae")}],
upsert=True)
This question already has answers here:
Mongodb $push in nested array
(4 answers)
Closed 4 years ago.
I've got a document in a collection 'events' with the following structure:
{
"_id" : "abc",
"text" : "some text",
}
I wish to update the document by inserting an array named guestOwner. The result I want would update the document like this:
{
"_id" : "abc",
"text" : "some text",
"guestOwner" : [
{
"name" : "JACK BLACK",
"guests" : [
"GUEST1",
"GUEST2"
]
}
]
}
So I tried an mongo update with the following:
db.events.update({ _id: eventid }, { $push: { guestOwner: { name: username, guests: allGuests } } });
where 'username' is a string, and 'allGuests' is an array of names ["Guest1", "Guest2"]
The issue for me is when a subsequent update to the document occurs, I would want to push a the new 'allGuests' array into the existing one if the name is the same. For example, if a second update occurs with 'allGuests' = ["GUEST3"], and with the same name = "JACK BLACK", I would want the document to be:
{
"_id" : "abc",
"text" : "some text",
"guestOwner" : [
{
"name" : "JACK BLACK",
"guests" : [
"GUEST1",
"GUEST2"
"GUEST3"
]
}
]
}
BUT, if the document were updated with a different name = 'JOHN SMITH' where allGuests array = ["GUEST3"], it would create:
{
"_id" : "abc",
"text" : "some text",
"guestOwner" : [
{
"name" : "JACK BLACK",
"guests" : [
"GUEST1",
"GUEST2"
]
},
{
"name" : "JOHN SMITH",
"guests" : [
"GUEST3"
]
}
]
}
Would I need conditional statements surrounding the mongo update to check for guestOwner[0].name? Not sure if mongo could do this on its own, or if a bunch of logic is going to be necessary.
You could simply do an update where in the query section you would specify the name:
db.events.update({
"_id" : ObjectId("someId"), "guestOwner.name": "JACK BLACK"}, {
$push: { "guestOwner.$.guests": "GUEST11" // etc }
})
If this returns the number of updated elements to be 1 you are good to go since the name exists etc.
If it returns 0 then that name does not exists so you can run:
db.events.update({"_id" : ObjectId("someId")}, {
$addToSet: { guestOwner: { name: "JACK BLACK" // etc }}
})
It would save you one call since if you have to check if the record exists you would always do 2 rounds. One to check and another to take action based on the result. Here if the records is already there you only do one update.
I'm quite new to mongodb and there is one thing I can't solve right now:
Let's pretend, you have the following document structure:
{
"_id": ObjectId("some object id"),
name: "valueName",
options: [
{idOption: "optionId", name: "optionName"},
{idOption: "optionId", name: "optionName"}
]
}
And each document can have multiples options that are already classified.
I'm trying to get all the documents in the collection that have, at least one, of the multiples options that I pass for the query.
I was trying with the operator $elemMatch something like this:
db.collectioName.find({"options.name": { $elemMatch: {"optName1","optName2"}}})
but it never show me the matches documents.
Can someone help and show me, what I'm doing wrong?
Thanks!
Given a collection which contains the following documents:
{
"_id" : ObjectId("5a023b8d027b5bd06add627a"),
"name" : "valueName",
"options" : [
{
"idOption" : "optionId",
"name" : "optName1"
},
{
"idOption" : "optionId",
"name" : "optName2"
}
]
}
{
"_id" : ObjectId("5a023b9e027b5bd06add627d"),
"name" : "valueName",
"options" : [
{
"idOption" : "optionId",
"name" : "optName3"
},
{
"idOption" : "optionId",
"name" : "optName4"
}
]
}
This query ...
db.collection.find({"options": { $elemMatch: {"name": {"$in": ["optName1"]}}}})
.. will return the first document only.
While, this query ...
db.collection.find({"options": { $elemMatch: {"name": {"$in": ["optName1", "optName3"]}}}})
...will return both documents.
The second example (I think) meeets this requirement:
I'm trying to get all the documents in the collection that have, at least one, of the multiples options that I pass for the query.
Followup Question
Thanks #4J41 for your spot on resolution. Along the same lines, I'd also like to validate one other thing.
I have a mongo document that contains an array of Strings, and I need to convert this particular array of strings into an array of object containing a key-value pair. Below is my curent appraoch to it.
Mongo Record:
Same mongo record in my initial question below.
Current Query:
templateAttributes.find({platform:"V1"}).map(function(c){
//instantiate a new array
var optionsArray = [];
for (var i=0;i< c['available']['Community']['attributes']['type']['values'].length; i++){
optionsArray[i] = {}; // creates a new object
optionsArray[i].label = c['available']['Community']['attributes']['type']['values'][i];
optionsArray[i].value = c['available']['Community']['attributes']['type']['values'][i];
}
return optionsArray;
})[0];
Result:
[{label:"well-known", value:"well-known"},
{label:"simple", value:"simple"},
{label:"complex", value:"complex"}]
Is my approach efficient enough, or is there a way to optimize the above query to get the same desired result?
Initial Question
I have a mongo document like below:
{
"_id" : ObjectId("57e3720836e36f63695a2ef2"),
"platform" : "A1",
"available" : {
"Community" : {
"attributes" : {
"type" : {
"values" : [
"well-known",
"simple",
"complex"
],
"defaultValue" : "well-known"
},
[......]
}
I'm trying to query the DB and retrieve only the value of defaultValue field.
I tried:
db.templateAttributes.find(
{ platform: "A1" },
{ "available.Community.attributes.type.defaultValue": 1 }
)
as well as
db.templateAttributes.findOne(
{ platform: "A1" },
{ "available.Community.attributes.type.defaultValue": 1 }
)
But they both seem to retrieve the entire object hirarchy like below:
{
"_id" : ObjectId("57e3720836e36f63695a2ef2"),
"available" : {
"Community" : {
"attributes" : {
"type" : {
"defaultValue" : "well-known"
}
}
}
}
}
The only way I could get it to work was with find and map function, but it seems to be convoluted a bit.
Does anyone have a simpler way to get this result?
db.templateAttributes.find(
{ platform: "A1" },
{ "available.Community.attributes.type.defaultValue": 1 }
).map(function(c){
return c['available']['Community']['attributes']['type']['defaultValue']
})[0]
Output
well-known
You could try the following.
Using find:
db.templateAttributes.find({ platform: "A1" }, { "available.Community.attributes.type.defaultValue": 1 }).toArray()[0]['available']['Community']['attributes']['type']['defaultValue']
Using findOne:
db.templateAttributes.findOne({ platform: "A1" }, { "available.Community.attributes.type.defaultValue": 1 })['available']['Community']['attributes']['type']['defaultValue']
Using aggregation:
db.templateAttributes.aggregate([
{"$match":{platform:"A1"}},
{"$project": {_id:0, default:"$available.Community.attributes.type.defaultValue"}}
]).toArray()[0].default
Output:
well-known
Edit: Answering the updated question: Please use aggregation here.
db.templateAttributes.aggregate([
{"$match":{platform:"A1"}}, {"$unwind": "$available.Community.attributes.type.values"},
{$group: {"_id": null, "val":{"$push":{label:"$available.Community.attributes.type.values",
value:"$available.Community.attributes.type.values"}}}}
]).toArray()[0].val
Output:
[
{
"label" : "well-known",
"value" : "well-known"
},
{
"label" : "simple",
"value" : "simple"
},
{
"label" : "complex",
"value" : "complex"
}
]