Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB: insert record if it doesn't exist, ignore if it does

I have a series of MongoDB records like:

{"name":"Bob", "gpa":4} {"name":"Sarah", "gpa":3}

I will encounter various additional records that may or may not pertain to the same people. I want to accept them if they're new people, and not update them if they're people we've seen before. So if I get {"name":"Jim", "gpa":2}, I want to accept that as-is. If I get {"name":"Sarah", "gpa":4} (different GPA value), I want to just ignore it. This doesn't seem to be the logic of either update with "upsert" set to yes, or of findAndModify with "upsert."

https://stackoverflow.com/a/3260290/514757 suggests a way (unique index on the first field, insert record, immediately update the second field) but it's a few years old and I wonder if there isn't a better way now to do this in a single step.

EDIT:

The accepted answer seems great, but it's not working for me! First, I created the index (this is with the Java driver):

nameIndex.put("name", 1);
nameIndex.put("unique", true);
queue.ensureIndex(nameIndex);

I can see from the command line that the index exists and is unique. Then, an item is inserted:

DBObject person = new BasicDBObject();
person.put("name", "Bob");
person.put("score", 200000);
queue.insert(person);

Later, the item with the highest score has its score reduced to zero:

BasicDBObject reset = new BasicDBObject();
reset.put("$set", new BasicDBObject("score", 0));
DBObject dbObj = queue.findAndModify(null, new BasicDBObject("score", -1), reset);

All of this works just as intended! But, later, the same name may be found again, with a new score. When this last bit of code runs, a new item is created with a different score, which is exactly what I don't want:

BasicDBObject queueable = new BasicDBObject();
queueable.put("name", "Could be Bob again, could be someone new");
queueable.put("score", 1234);
queue.insert(queueable);

If I search for Bob, I find:

{ "_id" : ObjectId("4f5e865ad6d09315326ea0f0"), "score" : 0, "name" : "Bob" }
{ "_id" : ObjectId("4f5e8691d6d09315326ea190"), "name" : "Bob", "score" : 886 }

A second record has been created, with the higher score again. Does the order of the fields matter? It doesn't seem like it should, and I don't know how to control that.

like image 604
umbraphile Avatar asked Mar 12 '12 01:03

umbraphile


People also ask

Does MongoDB update insert if not exists?

This particular code checks if the field “team” has a value of “Hornets.” If this value exists, then nothing will happen. However, if this value does not exist then it will insert a document with specific values for the “team”, “points”, and “rebounds” fields.

Does MongoDB Create collection if not exists?

If a collection does not exist, MongoDB creates the collection when you first store data for that collection. You can also explicitly create a collection with various options, such as setting the maximum size or the documentation validation rules.

How do you check if record already exists in MongoDB?

In MongoDB, we can check the existence of the field in the specified collection using the $exists operator. When the value of $exists operator is set to true, then this operator matches the document that contains the specified field(including the documents where the value of that field is null).

What does Upsert true do in MongoDB?

With the upsert option set to true , if no matching documents exist for the Bulk. find() condition, then the update or the replacement operation performs an insert. If a matching document does exist, then the update or replacement operation performs the specified update or replacement.


1 Answers

The best way to accomplish this would be to set a unique index on the name field using:

db.foo.ensureIndex({name:1}, {unique:true});

This will cause Sarah to not get updated when you make an insert call because it will find another record where 'Sarah' is already set for the name field.

like image 76
Fapiko Avatar answered Nov 08 '22 09:11

Fapiko