How do I add an array of elements in MongoDB to an array in an existing document? - mongodb

In MongoDB, I'm trying to write a query to add elements from an array to an existing document, but instead of adding the elements as objects:
property: ObjectID(xxx)
the elements are getting added as just
ObjectID(xxx)
Forgive me if I get the terminology wrong. I'm completely new to MongoDB; I normally only work with relational databases. How do I properly add these new elements?
I have a collection called auctions which has two fields: ID and properties. Properties is an array of objects named property. Here's an example with two auction documents:
** I changed the object IDs to make them easier to reference in our discussion
Collection db.auctions
{
"_id" : ObjectId("abc"),
"properties" : [
{
"property" : ObjectId("prop1")
},
{
"property" : ObjectId("prop2")
},
{
"property" : ObjectId("prop3")
}]
}
{
"_id" : ObjectId("def"),
"properties" : [
{
"property" : ObjectId("prop97")
},
{
"property" : ObjectId("prop98")
}]
}
I want to add 3 new properties to auction "abc". How do I do this?
Here's is what I tried:
I have an array of properties that looks like this:
Array PropsToAdd
[
ObjectId("prop4"),
ObjectId("prop5"),
ObjectId("prop6")
]
I wrote an update query to push these properties into the properties array in auctions:
db.auctions.update(
{"_id": "abc"}
,
{ $push: { properties: { $each: PropsToAdd } } }
);
This query gave the result below. Notice that instead of adding elements named property with a value from my array, it's just added my values from my array. I obviously need to add that "property" part, but how do I do that?
Collection db.auctions (_id "abc" only)
{
"_id" : ObjectId("abc"),
"properties" : [
{
"property" : ObjectId("prop1")
},
{
"property" : ObjectId("prop2")
},
{
"property" : ObjectId("prop3")
},
ObjectId("prop4"),
ObjectId("prop5"),
ObjectId("prop6"),
ObjectId("prop7")]
}
The result I'm looking for is this:
Collection db.auctions (_id "abc" only)
{
"_id" : ObjectId("abc"),
"properties" : [
{
"property" : ObjectId("prop1")
},
{
"property" : ObjectId("prop2")
},
{
"property" : ObjectId("prop3")
},
{
"property" : ObjectId("prop4")
},
{
"property" : ObjectId("prop5")
},
{
"property" : ObjectId("prop6")
}
}
Here is some further information on that array of properties I'm adding. I get it from running these queries. Perhaps one of them needs changed?
This query gets an array of current properties:
var oldActiveProperties = db.properties.distinct( "saleNumber", { "active": true, "auction": ObjectId("abc") } );
Then those results are used to find properties in the new file that weren't in the old file:
var PropsToAdd = db.newProperties.distinct(
"_id"
, { "saleNumber": { "$nin": oldActiveProperties }, "active": true}
);
The resulting array is what I need to add to the auctions collection.

Use the JavaScript's native map() method to map the array into an array of documents. The following shows this:
var PropsToAdd = db.newProperties.distinct("_id",
{ "saleNumber": { "$nin": oldActiveProperties }, "active": true}
).map(function (p) { return { property: p }; });
db.auctions.update(
{"_id": "abc"},
{ $push: { "properties": { "$each": PropsToAdd } } }
);

Related

How to remove an element in double nested array in MongoDB

I'm working on mongo, in this time in need to remove a specific element from array nested in another one, the structure is like the following:
{
"_id" : ObjectId("5e616314946b6d3ac4ed8252"),
"uid" : "5db069478556622b4a0adca5",
"areas" : [
{
"name" : "mexico",
"elements" : [
"23452345",
"24454675"
],
},
{
"name" : "usa",
"elemets" : [
"123123123",
"1234334"
],
}
]
}
So I need a query that lets me remove any element in "elements" property.
I tried with this query but I couldn't reach it.
db.areas.update(
{ },
{ $pull:
{ areas: {
$elemMatch: {
elements: { $eq : "123123123" }
}
}
}
},
{ multi:false }
);
This just said
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
Here is the update for removing the specific array element in a sub-array (elements).
Note that to remove (pull) the specified element (elements value "24454675"), you need to specify the outer-array's element matching condition (note that there are two elements in the outer-array field areas).
db.areas.update(
{ "areas.name": "mexico" },
{ $pull: { "areas.$.elements": "24454675" } }
)
NOTE: If you don't specify the { "areas.name": "mexico" } condition, the whole sub-document of the areas array will be removed.

How to remove an element from inner array of nested array pymongo using $ pull

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)

Mongo remove from nested object by value

I have a Mongo collection the consists of a document and a nested object describing what collections the document is in and when it was added. I would like to remove key-value pairs from a nested object based on a condition, e.g. is the value (a date) before 1-1-2016.
Example:
{
"_id" : ObjectId("581214940911ad3de98002db"),
"collections" : {
"c01" : ISODate("2016-10-27T15:52:04.512Z"),
"c02" : ISODate("2015-11-21T16:06:06.546Z")
}
}
needs to become
{
"_id" : ObjectId("581214940911ad3de98002db"),
"collections" : {
"c01" : ISODate("2016-10-27T15:52:04.512Z"),
}
}
One alternative would be to change the schema to something like this:
{
"_id" : ObjectId("581214940911ad3de98002db"),
"collections" : [
{
"id": "c01",
"date": ISODate("2016-10-27T15:52:04.512Z")
},
{
"id": "c02",
"date" : ISODate("2015-11-21T16:06:06.546Z")
}
]
}
in which case removing a document from a would be easy. I am a bit reluctant to do that because it would complicate some of the other queries I would like to support. Thanks!
I prefer the second structure for your schema
{
"_id" : ObjectId("581214940911ad3de98002db"),
"collections" : [
{
"id": "c01",
"date": ISODate("2016-10-27T15:52:04.512Z")
},
{
"id": "c02",
"date" : ISODate("2015-11-21T16:06:06.546Z")
}
]
}
then able to remove from collections like this
db.collectionName.update(
{ },// if you want can add query for specific Id {"_id" : requestId},
{ $pull: { collections: { date: {$lt: yourDate} } } }, // if need can convert iso date string like: new Date(yourDate).toISOString()
{ multi: true }
)

Retrieving value of an emedded object in mongo

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

Script to add one value to array in mongo collection

/* 0 */
{
"_id" : ObjectId("55addc2f8dab32aca87ce0bd"),
"partNum" : "part1",
"dest" : "First Part",
"sales" : [
"sale1",
"sale2",
"sale3"
],
"salesData" : {
"sale1" : {
"mcode" : "mc11",
"dtype" : [
"AAA",
"BBB"
]
}
}
}
/* 1 */
{
"_id" : ObjectId("55addc408dab32aca87ce0be"),
"partNum" : "part2",
"dest" : "Second Part",
"sales" : [
"sale1",
"sale2",
"sale3"
],
"salesData" : {
"sale1" : {
"mcode" : "mc22",
"dtype" : [
"AAA",
"BBB"
]
}
}
}
I am not that much efficient in writing mongo script. My requirement is to append one more value to "dtype" array wherever "mcode" is "mc11" in all of the documents inside the collection. Above is the two document output from my collection. I was using the below script to do it and its not working. Can anyone please help me
db.testingRD.find().forEach( function(myDocument)
{
db.testingRD.update({id: myDocument._id}, {$push : {"salesData.sale1.dtype" : "DDD"}});
});
To append one more value to "dtype" array wherever "mcode" is "mc11", use the following update where the query object is the selection criteria for the update and is the same query selector as in the find() method, the update object has the $push modifications to apply and then the options document which is optional. If that is set to true, it updates multiple documents that meet the query criteria:
var query = { "salesData.sale1.mcode": "mc11" },
update = {
"$push": { "salesData.sale1.dtype": "DDD" }
},
options = { "multi": true };
db.testingRD.update(query, update, options);
You had a typing mistake in the script (you forgot an underscore):
db.testingRD.find().forEach( function(myDocument)
{
db.testingRD.update({_id: myDocument._id}, {$push : {"salesData.sale1.dtype" : "DDD"}});
});
I always use a trick when an update seams to not working: I change the update with a printjson + find so that I can see if it is matching anything:
db.testingRD.find().forEach( function(myDocument) { printjson(db.testingRD.find({_id: myDocument._id})) } );