发布于 2015-09-14 14:59:14 | 226 次阅读 | 评论: 0 | 来源: 网络整理
MongoDB reserves the _id field in the top level of all documents as a primary key. _id must be unique, and always has an index with a unique constraint. However, except for the unique constraint you can use any value for the _id field in your collections. This tutorial describes two methods for creating an incrementing sequence number for the _id field using the following:
警告
Generally in MongoDB, you would not use an auto-increment pattern for the _id field, or any field, because it does not scale for databases with larger numbers of documents. Typically the default value ObjectId is more ideal for the _id.
Use a separate counters collection tracks the last number sequence used. The _id field contains the sequence name and the seq contains the last value of the sequence.
Insert into the counters collection, the initial value for the userid:
db.counters.insert(
{
_id: "userid",
seq: 0
}
)
Create a getNextSequence function that accepts a name of the sequence. The function uses the findAndModify() method to atomically increment the seq value and return this new value:
function getNextSequence(name) {
var ret = db.counters.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
Use this getNextSequence() function during insert().
db.users.insert(
{
_id: getNextSequence("userid"),
name: "Sarah C."
}
)
db.users.insert(
{
_id: getNextSequence("userid"),
name: "Bob D."
}
)
You can verify the results with find():
db.users.find()
The _id fields contain incrementing sequence values:
{
_id : 1,
name : "Sarah C."
}
{
_id : 2,
name : "Bob D."
}
注解
When findAndModify() includes the upsert: true option and the query field(s) is not uniquely indexed, the method could insert a document multiple times in certain circumstances. For instance, if multiple clients each invoke the method with the same query condition and these methods complete the find phase before any of methods perform the modify phase, these methods could insert the same document.
In the counters collection example, the query field is the _id field, which always has a unique index. Consider that the findAndModify() includes the upsert: true option, as in the following modified example:
function getNextSequence(name) {
var ret = db.counters.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true,
upsert: true
}
);
return ret.seq;
}
If multiple clients were to invoke the getNextSequence() method with the same name parameter, then the methods would observe one of the following behaviors:
If the method fails due to a unique index constraint violation, retry the method. Absent a delete of the document, the retry should not fail.
In this pattern, an Optimistic Loop calculates the incremented _id value and attempts to insert a document with the calculated _id value. If the insert is successful, the loop ends. Otherwise, the loop will iterate through possible _id values until the insert is successful.
Create a function named insertDocument that performs the “insert if not present” loop. The function wraps the insert() method and takes a doc and a targetCollection arguments.
function insertDocument(doc, targetCollection) {
while (1) {
var cursor = targetCollection.find( {}, { _id: 1 } ).sort( { _id: -1 } ).limit(1);
var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1;
doc._id = seq;
targetCollection.insert(doc);
var err = db.getLastErrorObj();
if( err && err.code ) {
if( err.code == 11000 /* dup key */ )
continue;
else
print( "unexpected error inserting data: " + tojson( err ) );
}
break;
}
}
The while (1) loop performs the following actions:
Use the insertDocument() function to perform an insert:
var myCollection = db.users2;
insertDocument(
{
name: "Grace H."
},
myCollection
);
insertDocument(
{
name: "Ted R."
},
myCollection
)
You can verify the results with find():
db.users2.find()
The _id fields contain incrementing sequence values:
{
_id: 1,
name: "Grace H."
}
{
_id : 2,
"name" : "Ted R."
}
The while loop may iterate many times in collections with larger insert volumes.