MongoDB 2.2 - Updating Array Nested Document - mongodb

Is it possible to update a single document field in the Level3 array using $update and $elemMatch? I realize I cannot use the positional operator multiple times given this case and historically I've modified the Level2 nested document with the required deeper changes since these documents aren't very large. I'm hoping there is some syntax that makes it possible to update Level3 array documents using $elemMatch without knowing the position of the target document in the Level3 array or containing document in Level2.
Example:
db.collection.update({_id:'123', level2:{$elemMatch:{'level3.id':'bbb','level3.e1':'hij'}},{'level2.level3.createdDate':new Date()})
{
_id:'123',
f1:'abc',
f2:'def',
level2:[
{_
id:'aaa',
e1:'hij',
e2:'lmo'
level3:[
{
name:'foo',
type:'bar',
createdDate:'2013-3-28T05:18:00'
}]
},
{_
id:'bbb',
e1:'hij',
e2:'lmo'
level3:[
{
name:'foo2',
type:'bar2',
createdDate:'2013-3-28T05:19:00'
}]
}
]
}

There is no way to do this currently using a regular update operation for reasons you noted.
The only work around you can use at the moment is to add versioning to your document and use optimistic locking by reading the document, finding the appropriate elements to modify in your application, changing their values and then using an update that includes the version in the read document (so that if other thread updated the document between your query and your update you would not overwrite the changes but would have to reload the document and try again.
The versioning strategy would not have to be based on the entire document, you can version the first level array elements and then you would be able to update just the sub-array you were concerned with (via an update with $set).

Related

Is there any possible do upsert functionality in "array of an object" using firestore query?

Example :[{
inst:"EVA",
std:"12th"
},
{
inst:"KSF",
std:"12th"
}]
As per the above example, In my case if "inst: "EVA" is already there in the "qualification" array so we need to update the object from the existing one.
Then "inst: "KSF" does not already exist in the "qualification" array so we need to add that one.
Help me if there is any way to upsert using firestore query.
There is no "upsert" operation for objects in arrays. If you need to make changes to that array, you will have to read the document, modify the contents of the array in memory, then update the document with the new contents of the array.
Arrays of objects usually do not work the way that people want, given their limitations on querying and updating. It's usually better to store data as documents in a nested subcollection, so they can be more easily queried and updated by the contents of their fields.

Query documents, update it and return it back to MongoDB

In my MongoDB 3.2 based application I want to perform the documents processing. In order to avoid the repeated processing on the same document I want to update its flag and update this document in the database.
The possible approach is:
Query the data: FindIterable<Document> documents = db.collection.find(query);.
Perform some business logic on these documents.
Iterate over the documents, update each document and store it in a new collection.
Push the new collection to the database with db.collection.updateMany();.
Theoretically, this approach should work but I'm not sure that it is the optimal scenario.
Is there any way in MongoDB Java API to perform the followings two operations:
to query documents (to get them from the DB and to pass to the separate method);
to update them and then store the updated version in DB;
in a more elegant way comparing to the proposed above approach?
You can update document inplace using update:
db.collection.update(
{query},
{update},
{multi:true}
);
It will iterate over all documents in the collection which match the query and updated fields specified in the update.
EDIT:
To apply some business logic to individual documents you can iterate over matching documents as following:
db.collection.find({query}).forEach(
function (doc) {
// your logic business
if (doc.question == "Great Question of Life") {
doc.answer = 42;
}
db.collection.save(doc);
}
)

Insert new Documents or modify an array field of existing document

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

How does MongoDB order their docs in one collection? [duplicate]

This question already has answers here:
How does MongoDB sort records when no sort order is specified?
(2 answers)
Closed 7 years ago.
In my User collection, MongoDB usually orders each new doc in the same order I create them: the last one created is the last one in the collection. But I have detected another collection where the last one I created has the 6 position between 27 docs.
Why is that?
Which order follows each doc in MongoDB collection?
It's called natural order:
natural order
The order in which the database refers to documents on disk. This is the default sort order. See $natural and Return in Natural Order.
This confirms that in general you get them in the same order you inserted, but that's not guaranteed–as you noticed.
Return in Natural Order
The $natural parameter returns items according to their natural order within the database. This ordering is an internal implementation feature, and you should not rely on any particular structure within it.
Index Use
Queries that include a sort by $natural order do not use indexes to fulfill the query predicate with the following exception: If the query predicate is an equality condition on the _id field { _id: <value> }, then the query with the sort by $natural order can use the _id index.
MMAPv1
Typically, the natural order reflects insertion order with the following exception for the MMAPv1 storage engine. For the MMAPv1 storage engine, the natural order does not reflect insertion order if the documents relocate because of document growth or remove operations free up space which are then taken up by newly inserted documents.
Obviously, like the docs mentioned, you should not rely on this default order (This ordering is an internal implementation feature, and you should not rely on any particular structure within it.).
If you need to sort the things, use the sort solutions.
Basically, the following two calls should return documents in the same order (since the default order is $natural):
db.mycollection.find().sort({ "$natural": 1 })
db.mycollection.find()
If you want to sort by another field (e.g. name) you can do that:
db.mycollection.find().sort({ "name": 1 })
For performance reasons, MongoDB never splits a document on the hard drive.
When you start with an empty collection and start inserting document after document into it, mongoDB will place them consecutively on the disk.
But what happens when you update a document and it now takes more space and doesn't fit into its old position anymore without overlapping the next? In that case MongoDB will delete it and re-append it as a new one at the end of the collection file.
Your collection file now has a hole of unused space. This is quite a waste, isn't it? That's why the next document which is inserted and small enough to fit into that hole will be inserted in that hole. That's likely what happened in the case of your second collection.
Bottom line: Never rely on documents being returned in insertion order. When you care about the order, always sort your results.
MongoDB does not "order" the documents at all, unless you ask it to.
The basic insertion will create an ObjectId in the _id primary key value unless you tell it to do otherwise. This ObjectId value is a special value with "monotonic" or "ever increasing" properties, which means each value created is guaranteed to be larger than the last.
If you want "sorted" then do an explicit "sort":
db.collection.find().sort({ "_id": 1 })
Or a "natural" sort means in the order stored on disk:
db.collection.find().sort({ "$natural": 1 })
Which is pretty much the standard unless stated otherwise or an "index" is selected by the query criteria that will determine the sort order. But you can use that to "force" that order if query criteria selected an index that sorted otherwise.
MongoDB documents "move" when grown, and therefore the _id order is not always explicitly the same order as documents are retrieved.
I could find out more about it thanks to the link Return in Natural Order provided by Ionică Bizău.
"The $natural parameter returns items according to their natural order within the database.This ordering is an internal implementation feature, and you should not rely on any particular structure within it.
Typically, the natural order reflects insertion order with the following exception for the MMAPv1 storage engine. For the MMAPv1 storage engine, the natural order does not reflect insertion order if the documents relocate because of document growth or remove operations free up space which are then taken up by newly inserted documents."

Doing an upsert in mongo, can I specify a custom query for the "insert" case? [duplicate]

I am trying to use upsert in MongoDB to update a single field in a document if found OR insert a whole new document with lots of fields. The problem is that it appears to me that MongoDB either replaces every field or inserts a subset of fields in its upsert operation, i.e. it can not insert more fields than it actually wants to update.
What I want to do is the following:
I query for a single unique value
If a document already exists, only a timestamp value (lets call it 'lastseen') is updated to a new value
If a document does not exists, I will add it with a long list of different key/value pairs that should remain static for the remainder of its lifespan.
Lets illustrate:
This example would from my understanding update the 'lastseen' date if 'name' is found, but if 'name' is not found it would only insert 'name' + 'lastseen'.
db.somecollection.update({name: "some name"},{ $set: {"lastseen": "2012-12-28"}}, {upsert:true})
If I added more fields (key/value pairs) to the second argument and drop the $set, then every field would be replaced on update, but would have the desired effect on insert. Is there anything like $insert or similar to perform operations only when inserting?
So it seems to me that I can only get one of the following:
The correct update behavior, but would insert a document with only a subset of the desired fields if document does not exist
The correct insert behavior, but would then overwrite all existing fields if document already exists
Are my understanding correct? If so, is this possible to solve with a single operation?
MongoDB 2.4 has $setOnInsert
db.somecollection.update(
{name: "some name"},
{
$set: {
"lastseen": "2012-12-28"
},
$setOnInsert: {
"firstseen": <TIMESTAMP> # set on insert, not on update
}
},
{upsert:true}
)
There is a feature request for this ( https://jira.mongodb.org/browse/SERVER-340 ) which is resolved in 2.3. Odd releases are actually dev releases so this will be in the 2.4 stable.
So there is no real way in the current stable versions to do this yet. I am afraid the only method is to actually do 3 conditional queries atm: 1 to check the row, then a if to either insert or update.
I suppose if you had real problems with lock here you could do this function with sole JS but that's evil however it would lock this update to a single thread.