Reputation: 377
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.
Upvotes: 7
Views: 13168
Reputation: 210
Your ensureIndex() call is not quite right. The first argument to ensureIndex() contains the keys that should be indexed; index options (like uniqueness) can be passed as the second argument. See the Java driver docs.
What you've got right now is attempting to create a non-unique index on two fields: "name" and "unique".
Try this to create your index:
DBObject nameIndex = new BasicDBObject();
nameIndex.put("name",1);
DBObject nameIndexOptions = new BasicDBObject();
nameIndexOptions.put("unique", true);
queue.ensureIndex(nameIndex, nameIndexOptions);
Upvotes: 6
Reputation: 120
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.
Upvotes: 7