Insert new Documents or modify an array field of existing document - mongodb

Apologies if this is a re-post, but I wasn't able to quite get the query I want from the mongodb documentation examples.
Here's my issue. I am unable execute in a single query to either update an array_field of an existing document or add a new document and initialize the array_field with an initial value.
I can use findOne() with some conditional logic, and probably solve this, but I would think mongodb has an implementation of this use case
Here's the code so far:
#data_json = JSON document to be added to collection
collection.update_one({"json_id":data_json["json_id"], "_dashbd_id_":dashboard_id},{{"$addToSet": {"array_field":keyword}},{"$setOnInsert":data_json}}, upsert=True)
I'm querying by the json_id, and _dashbd_id_ from my collection. If it exists, then I intend to add the "keyword" to the array_field. If it doesn't exist, create a new document as data_json which include array_field = [keyword]
Any hints and suggestions are appreciated!

If I understood you correctly you want to update values in Database only if they do not exist as well as create new documents with arrays in them. Okay there is a way in mongodb which I will mention in this reply. I think you should know few commands first that will help you achieve similar result (again there is a simple way just read on)
Let me start with the first part:
to update an element in an array you use dot notation to the index example:
db.collection_name.update({"_id": id}, {'$set': {"array_name.indexNumber": value}})
say we have the following document in collection name cars
db.cars.findOne():
{
_id: 1
name: EvolutionX
brand: Mitsubish
year: 2012
mods: [ turbo, headlights ]
}
Say in the above example we want to update headlights with rearlights we do the following (using mongoshell you can drop quotes in key names, Not when using the array index though):
db.cars.update({id:1}, {$set:{"mods.1":"rearlights"}})
1 is the index to headlights.
Note and be careful here that if you did not use index inside of an array like
db.cars.update({id:1}, {$set:{"mods":"rearlights"}})
this will overwrite the existing document _id:1 and it will lose all other attributes or fields inside the document so it will result in the follow:
db.cars.findOne():
{
_id: 1
mods: [ rearlights ]
}
Now, say we want to add an element tires to mods array you can use $push as:
db.collection_name.update({"_id": id}, {'$push': {"array_name": value}})
so it will be
db.cars.update({"_id":1}, {"$push":{"mods":"tires"}})
now say instead of updating mods array you want to remove "headlights". In this case you use $pop
db.cars.update({"_id":1}, {"$pop":{"mods":"headlights"}})
Now with that in mind. The easy way: in mongodb to add to array only if element does not exist you can use $addToSet. I love this operator because it will only add to array if the element does not exist. Here is how to use it:
db.cars.update({"_id":1}, {"$addToSet":{"mods":"headlights"}})
Now if headlights is in the array it will not be added, else it will be added to the end of array.
Okay that is the first part of the question. The second part which is initializing a document with an array. Okay there are two thoughts here: the first is you do not have to. using the addToSet you can create the array if it does not exist as (assuming _id 2 exist but without mods array):
db.cars.update({"_id":2}, {"$addToSet":{"mods":"bonnet"}})
This will create the array if document _id:2 exist. Assuming _id:3 does not exist you will have plug in a third attribute called upsert
db.cars.update({"_id":3}, {"$addToSet":{"mods":"headlights"}}, {upsert:true})
this will create a third document with array mods with headlights inside of it and _id:3. Note though no other attributes will be added only the _id and mods array
the second thought is when you insert a new document you insert it with empty mods array as mod:[]
I hope that helps

suppose your data_json ,dashboard_id and keyword contain following detail.
dashboard_id = ObjectId("5423200e6694ce357ad2a1ac")
keyword = "testingKeyword"
data_json =
{
"json_id":ObjectId("5423200e6694ce357ad2a1ac"),
"item":"EFG222",
"reorder":false,
}
if you execute below query
db.collection_name.update({"json_id":data_json["json_id"], "_dashbd_id_":dashboard_id},{{"$addToSet": {"array_field":keyword}},{ upsert=True})
than it will push keyword to array_field if document exist or it will insert new document with following detail as below.
{
"_id":ObjectId("5sdvsdv6sdv694ce357ad2a1ac"),
"json_id":ObjectId("5423200e6694ce357ad2a1ac"),
"dashboard_id": ObjectId("sddfb6694ce357ad2a1ac")
"item":"EFG222",
"reorder":false,
"array_field":
[
"testingKeyword"
]
}

Related

Update value in MongoDB

I have to add new value (or edit actual) company. I tried this code in console:
db.users.update({email: /debute/},{company: "test"})
But now, in database I have only _id and company. Why other values removed? Or how can I prevent of removing other values in future?
By default, the update function replaces the entire document with the document you pass. But you can also use it to only replace individual fields and leave the others alone by using the $set operator.
db.users.update({email: /debute/},{ $set: { company: "test"} } );
You might also want to specify a third argument { multi: true } to the update. Otherwise it will only update the first document it finds which matches the condition.
You need to use an update operator on order to update an existing document, your code replaces the document with the new document. Here is how to do an update:
db.users.update({email: /debute/},{$set: {company: "test"}})

update mongo db documents with regex

I need to find all the documents in mongodb that have keywords that start with number 1-9, then add a '+' in front of the keyword, I can easily find the documents but cannot figure out how to update them.
I tried this one, but it doesn't work
db.placements.update({program_id:{$in:[113,107]},
keyword:{$regex:'^[0-9]', $options:'i'}},
{keyword:"+"+$keyword})
It cannot recognize $keyword, I also tried '.keyword', 'keyword', none of them works. Is there any way to reference the document itself like Java does, using 'this', so I can do something like
this.keyword: "+" + this.keyword
You'll have to use the $set operator in the update query to update a specific field. Also, you cannot concatenate string within an update query. One way to do this would be using cursor forEach() in the shell:
db.placements.find({program_id:{$in:[113,107]}, keyword:{$regex:'^[0-9]', $options:'i'}})
.forEach(function(doc){
db.placements.updateOne({_id:doc._id}, {$set:{"keyword":"+" + doc.keyword}})
})
No, you cannot reference a value on the document itself when querying like you can with SQL.
I would suggest querying the document, updating it on your web/app server and then updating the value back to mongodb.
You will also find that your update command above will wipe your entire document leaving only the keyword field. You should use the $set modifier to update a field or set of fields.
db.placements.update(
{
program_id:{$in:[113,107]},
keyword:{$regex:'^[0-9]', $options:'i'}
},
{ $set: {keyword: new_value}})

Update not inserting a object in subarray,when upsert=true

I am using mongodb update method with upsert=true.
My data looks like this:
{"my_id":"1",
"test_list":[{"test_id":1,"test_name":"pppp"}]}
now I am using the following command:
db.testcol.update({"my_id":1,"test_list.test_id":2},{"$set":{"test_list.$.test_name":"mmmm"}},true,true)
Now I want a new object inserted into the "test_list" as it does not exist
but I am getting the error:
Cannot apply the positional operator without a corresponding query field containing an array.
I cannot use "insert" for my operation, as I dont know whether the data is their and the field needs to be updated,or its not their and needs to be inserted(for the first time)
Upserts are for when you want the update to work whether the entire document is there or not, not just an array element. For the array element case you can use $addToSet:
db.testcol.update(
{"my_id": "1"},
{"$addToSet": {"test_list": {"test_id": 2, "test_name": "mmmm"}}})
It will only add the new element to the doc's test_list array field if a matching element isn't already present.

In Mongodb, how to retrieve the subset of an object that matches a condition?

What I'm trying to do:
Filter a field of a collection that matches a given condition. Instead of returning every item in the field (which is an array of items), I only want to see matched items.
Similar to
select items from test where items.histPrices=[10,12]
It is also similar to what's found on the mongodb website here: http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
Here's what I have been trying:
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"}]})
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"},
{"histPrices":[12,13],"name":"stuff"},{"histPrices":[11,14],"name":"stuff"}]})
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
It will return all the objects that have a match for items.histPrices:[10,12], including ALL of the items in items[]. But I don't want the ones that don't match the condition.
From the comments left on Mongodb two years ago, the solution to get only the items with that histPrices[10,12] is to do it with javascript code, namely, loop through the result set and filter out the other items.
I wonder if there's a way to do that with just the query.
Your find query is wrong
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
Your condition statement should be in the first part of the find statement.In your query {} means fetch all documents similar to this sql
select items from test (no where clause)
you have to change your mongodb find to
db.test.find({"items.histPrices":[10, 12]},{"name":1})
make it work
since your items is an array and if you wanted to return only the matching sub item, you have to use positional operator
db.test.find({"items.histPrices":[10, 12]},{"name":1,'items.$':1})
When working with arrays Embedded to the Document, the best approach is the one suggested by Chien-Wei Huang.
I would just add another aggregation, with the $group (in cases the document is very long, you may not want to retrieve all its content, only the array elements) Operator.
Now the command would look like:
db.test.aggregate({$match:{name:"record"}},
{$unwind:"$items"},
{$match {"items.histPrices":[10, 12]}},
{$group: {_id: "$_id",items: {$push: "$items"}}});)
If you are interested to return only one element from the array in each collection, then you should use projection instead
The same kind of issue solved here:
MongoDB Retrieve a subset of an array in a collection by specifying two fields which should match
db.test.aggregate({$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
But I don't know whether the performance would be OK. You have to verify it with your data.
The usage of $unwind
If you want add some filter condition like name="record", just add another $march at first, ex:
db.test.aggregate({$match:{name:"record"}}, {$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
https://jira.mongodb.org/browse/SERVER-828
Get particular element from mongoDB array
MongoDB query to retrieve one array value by a value in the array

Mongo - dot notation works like $or? Can't provide multiple parameters?

I tried to update an existing document with two dot notation parameters, my query:
{ _id: "4eda5...", comments._id: "4eda6...", comments.author: "john" }
my update was:
{ "comments.$.deleted": true }
However, weirdly enough, when I passed a non-existent combination of comment id+author, it just updated the first matching comment by that author.
Any ideas why that's happening?
EDIT: C# Code sample
var query = Query.And(Query.EQ("_id", itemId), Query.EQ("cmts._id", commentId));
if (!string.IsNullOrEmpty(author))
query = Query.And(query, Query.EQ("cmts.Author", author));
var update = Update.Set("cmts.$.deleted", true);
var result = myCol.Update(query, update, UpdateFlags.None, SafeMode.True);
You want $elemMatch if you want the _id and author to be in the same comment. Really, your query doesn't make much sense including the author as the id should be as unique as you can get, no?
It is based on the first matching array element which replaces the "$" in for the update.
This is working by design. It is similar to an or since it can find a document which both has the _id and an author that match in any of the array elements.
The query is not working the way you are expecting it to. Basically, when using the $ positional notation you need to make sure that your query only has one clause that queries an array, otherwise it is ambiguous which of the two array comparisons the $ should refer to.
In your case, you are asking for a document where:
The _id is equal to some value
The comments array contains some document where the _id is equal to some value
The comments array contains some document where the author is equal to some value
Nothing in your query says that 2. and 3. need to be satisfied by the same document.
So even though you are using a non-existent combination of comment._id and comment.author, your comment array does have at least one entry where the _id is equal to your search value and some other entry (just not the same one) where the author is equal to your search value.
Since the author was the last one checked, that's what set the value of the $, and that's why that array element got updated.