Mongodb use foreach on query and update results its results - mongodb

I have a mongo collection where a field is supposed to point to another document's id in the same collection, but instead it is pointing to its "number". I need to perform an update on them but I'm having some problems on forming the query. Could you help me?
The structure of the document is like this:
{
"_id": "269410e2-cebf-40f1-a81f-fdce34185cdc",
"number": 1471,
"alternativeLocationId": "9871",
"locationType": "DUMMY"
},
{
"_id": "2945b24a-b82f-45a9-ad06-a884379b5597",
"number": 9871,
"locationType": "MAIN"
}
So as asked, I'd need to make document with number 1471 "alternativeLocationId" to be "2945b24a-b82f-45a9-ad06-a884379b5597" instead of 9871 (Note that the referenced documents are not locationType "DUMMY" nor have this alternativeLocationId field).
The query I've done so far goes like this, but when executed its not doing any changes:
db.location.find({alternativeLocationId: {$exists:true}}).forEach(
function (loc) {
var correctLocation = db.location.findOne({number: loc.alternativeLocationId});
db.location.update(
{_id: loc._id},
{$set: {alternativeLocationId: correctLocation._id} }
);
}
);

As mentioned by user20042973 the issue was the type mismatch between alternativeLocationId and locationNumber, after converting the value to int when looking for it, it works perfectly.

Related

mongodb update an existing field without overwritting

{
questions: {
q1: "",
}
}
result after updating:
{
questions: {
q1: "",
q2: ""
}
}
I want to add q2 inside questions, without overwritting what's already inside it (q1).
One solution I found is to get the whole document, and modify it on my backend, and send the whole document to replace the current one. But it seems really in-effiecient as I have other fields in the document as well.
Is there a query that does it more efficiently? I looked at the mongodb docs, didn't seem to find a query that does it.
As turivishal said, you can use $set like this
But you also can use $addFields in this way:
db.collection.aggregate([
{
"$match": {
"questions.q1": "q1"
}
},
{
"$addFields": {
"questions.q2": "q2"
}
}
])
Example here
Also, reading your comment where you say Cannot create field 'q2' in element {questions: "q1"}. It seems your original schema is not the same you have into your DB.
Your schema says that questions is an object with field q1. But your error says that questions is the field and q1 the value.

Mongoose Model.deleteMany() only deletes first element of matches

I'm trying to use the Model.deleteMany() function from mongoose. I'm trying to do something like this:
MyModel.deleteMany({"_id": {$in: ['objectid 1', 'objectid 2'
But this only deletes the first element of the matches from DB (in this case, if 'objectid 1' exists in the DB, it deletes that, but if it isn't nothing happens (both n and deletedCount is 0 in the returned data from the function). I tried using something other than the _id as a query, and this worked. If I had three elements with the same 'name' field, I could delete these.
I tried _id both with and without quotation marks. I also tried converting the object id strings to actual object ids before passing them to deleteMany, but this had no difference either. I have also of course tried to google this, but everything I've found are examples of usage, where it looks like I'm doing the exact same thing as the various blog posts.
I haven't added much code here because I don't really see what else I could be adding. I'm already printing out the input to the $in object, and this is correct. The strangest thing, I think, is that the first element of the list is deleted. Is it treated as a deleteOne request for some reason? are there any config options I need?
As per request, I've added the query and the documents I'd hope to delete:
//Request
MemberModel.deleteMany({"_id": {$in: [
5ee4f6308631dc413c7f04b4,
5ee4f6308631dc413c7f04b5,
5ee4f6308631dc413c7f04b6
]}};
//Expected to be deleted
[
{
"_id": "5ee4f62f8631dc413c7f04b5",
"firstName": "Name",
"lastName": "Nameson",
"__v": 0
},
{
"_id": "5ee4f62f8631dc413c7f04b6",
"firstName": "Other",
"lastName": "Person",
"__v": 0
}
]
If you have any ideas for what I could try, that would be much appreciated.

Increase value of a field inside an array of element

I have documents that has the following structure in my collection:
{
"_id": "1234566780",
"arrayField": [
{
"id": "1",
"count": 3
},
{
"id": "2",
"count": 5
}
]
}
I want to have a method in my CollectionRepository that increments the value of the field count by giving to the method the collection id and the field id of the elements in the arrayField.
I have the feeling that it is not possible to do it with Spring Data autogenerated queries by method names.
So I'm trying to do it with the #Query annotation, but I have no idea on how to do it, since I need to select the collection based on _id, then select the field inside the array based on id, and then increase the count by one. Any hints?
Sorry for the delay, here is a solution to your problem.
You can update a specific element of an array using arrayFilter update option. The MongoDB update operation would look like:
db.collection.update(
{"_id":"1234566780"},
{$inc:{"arrayField.$[elem].count":1}},
{arrayFilters:[{"elem.id":"2"}]}
Admiting you are using MongoDB server and MongoDB java Driver version 3.6 or greater you could use:
public void updateCollection () {
MongoCollection<Document> collection = mongoTemplate.getDb().getCollection("collection");
Bson query = new Document("_id", "1234566780");
Bson update = Updates.inc("arrayField.$[elem].count",1);
UpdateOptions updateOptions = new UpdateOptions().arrayFilters(Arrays.asList( Filters.eq("elem.id","2")));
collection.updateOne(query, update, updateOptions);
}
Indeed Spring data method signature is not likely to generate this update. What I suggest you is to extend a CustomCollectionRepository interface with your CollectionRepository. You could define this update method signature in this interface and implement it in a CustomCollectionRepositoryImpl class. Please find the part of the documentation that mention this solution.

MongoDB query to update nested array

I'm trying to use some nested arrays in mongoDB but after several hours fighting with the mongo docs and several other SO questions. I'm thinking I might be doing something terribly wrong. The code below is supposed to update the confirms array in the confirms array but it won't ever do anything but add new entries. Any help would be appreciated.
Events.update({_id: eventId, "confirms.person": personId}, {
$set: {
"confirms.$.person": personId,
"confirms.$.confirmed": isConfirmed,
"confirms.$.timestamp": new Date()
}
});
And here is a sample of the data I'm trying to operate on, it is currently WRONG because a personId should be unique.
{
"_id": "RnE4PaPSZ9FC9MAAQ",
"eventTitle": "Epic lan PARTY!!!!!",
"eventDate": "31/07/2015",
"confirms": [{
"person": "jjoqekYYaA6n8nYvs",
"confirmed": true,
"timestamp": ISODate("2015-07-25T17:15:28.212Z")
}, {
"person": "jjoqekYYaA6n8nYvs",
"confirmed": true,
"timestamp": ISODate("2015-07-25T17:16:50.485Z")
}
}]
}
Just in case it might turn out to be relevant, i'm working in meteor js.
To update the element of an array which is the variable of a Collection item you could do this:
Get the array:
var arr = Events.findOne({_id: eventId}).confirms;
Do your operation
Update old array with modified array:
Events.update({_id: eventId}, {$set: {confirms: arr}});
In case of problems with step 2: you could iterate through the array checking if this.person === personId then set this.confirmed = isConfirmed & this.timestamp = new Date.
There might be ways of operating on arrays within Mongo but this one works for sure.

How do I manage a sublist in Mongodb?

I have different types of data that would be difficult to model and scale with a relational database (e.g., a product type)
I'm interested in using Mongodb to solve this problem.
I am referencing the documentation at mongodb's website:
http://docs.mongodb.org/manual/tutorial/model-referenced-one-to-many-relationships-between-documents/
For the data type that I am storing, I need to also maintain a relational list of id's where this particular product is available (e.g., store location id's).
In their example regarding "one-to-many relationships with embedded documents", they have the following:
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
books: [12346789, 234567890, ...]
}
I am currently importing the data with a spreadsheet, and want to use a batchInsert.
To avoid duplicates, I assume that:
1) I need to do an ensure index on the ID, and ignore errors on the insert?
2) Do I then need to loop through all the ID's to insert a new related ID to the books?
Your question could possibly be defined a little better, but let's consider the case that you have rows in a spreadsheet or other source that are all de-normalized in some way. So in a JSON representation the rows would be something like this:
{
"publisher": "O'Reilly Media",
"founded": 1980,
"location": "CA",
"book": 12346789
},
{
"publisher": "O'Reilly Media",
"founded": 1980,
"location": "CA",
"book": 234567890
}
So in order to get those sort of row results into the structure you wanted, one way to do this would be using the "upsert" functionality of the .update() method:
So assuming you have some way of looping the input values and they are identified with some structure then an analog to this would be something like:
books.forEach(function(book) {
db.publishers.update(
{
"name": book.publisher
},
{
"$setOnInsert": {
"founded": book.founded,
"location": book.location,
},
"$addToSet": { "books": book.book }
},
{ "upsert": true }
);
})
This essentially simplified the code so that MongoDB is doing all of the data collection work for you. So where the "name" of the publisher is considered to be unique, what the statement does is first search for a document in the collection that matches the query condition given, as the "name".
In the case where that document is not found, then a new document is inserted. So either the database or driver will take care of creating the new _id value for this document and your "condition" is also automatically inserted to the new document since it was an implied value that should exist.
The usage of the $setOnInsert operator is to say that those fields will only be set when a new document is created. The final part uses $addToSet in order to "push" the book values that have not already been found into the "books" array (or set).
The reason for the separation is for when a document is actually found to exist with the specified "publisher" name. In this case, all of the fields under the $setOnInsert will be ignored as they should already be in the document. So only the $addToSet operation is processed and sent to the server in order to add the new entry to the "books" array (set) and where it does not already exist.
So that would be simplified logic compared to aggregating the new records in code before sending a new insert operation. However it is not very "batch" like as you are still performing some operation to the server for each row.
This is fixed in MongoDB version 2.6 and above as there is now the ability to do "batch" updates. So with a similar analog:
var batch = [];
books.forEach(function(book) {
batch.push({
"q": { "name": book.publisher },
"u": {
"$setOnInsert": {
"founded": book.founded,
"location": book.location,
},
"$addToSet": { "books": book.book }
},
"upsert": true
});
if ( ( batch.length % 500 ) == 0 ) {
db.runCommand( "update", "updates": batch );
batch = [];
}
});
db.runCommand( "update", "updates": batch );
So what is doing in setting up all of the constructed update statements into a single call to the server with a sensible size of operations sent in the batch, in this case once every 500 items processed. The actual limit is the BSON document maximum of 16MB so this can be altered appropriate to your data.
If your MongoDB version is lower than 2.6 then you either use the first form or do something similar to the second form using the existing batch insert functionality. But if you choose to insert then you need to do all the pre-aggregation work within your code.
All of the methods are of course supported with the PHP driver, so it is just a matter of adapting this to your actual code and which course you want to take.