由于管理层的决定,我们将userId用于users集合,postId用于posts集合,topicId用于主题集合,而不是每个集合的'_id'作为唯一标识符。
这导致一些问题开始 - 我遇到的一个问题是upserts -
使用Mongoose,我们有一个模式将userId限制为唯一值 - 但是当对用户模型进行更新时,将upsert设置为 true ,MongoDB似乎只查看ObjectIds查看是否存在同一个集合的集合 - 它不检查模型是否已存在具有相同的userId - 因此Mongo执行插入而不是更新。
让我用一些数据来说明这一点:
假设用户的集合有一个文档:
{
_id:'561b0fad638e99481ab6d84a'
userId:3,
name:'foo'
}
然后我们运行:
User.update({userId:3},{"$set":{name:'bar'},{upsert:true},function(err,resp){
if(err){
// "errMessage": "insertDocument :: caused by :: 11000 E11000 duplicate key error index: app42153482.users.$userId_1 dup key: { : 3 }",
}
});
有人会认为MongoDB会找到带有userId:3和udpate的现有文档,所以必须有一些我做错了,因为它给了我重复的键错误?
答案 0 :(得分:6)
通常,默认值ObjectId对于_id更为理想。在这种情况下,您可以覆盖默认的_id,也可以拥有自己的id字段(例如您的userId)。
使用单独的计数器集合来跟踪最后使用的数字序列。 _id 字段包含序列名称, seq 字段包含序列的最后一个值。
插入计数器集合,即userid的初始值:
db.counters.insert( {
_id: "userid",
seq: 0 } )
创建一个 getNextSequence 函数,该函数接受序列的名称。该函数使用 findAndModify()方法以原子方式递增 seq 值并返回此新值:
function getNextSequence(name) {
var ret = db.counters.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
在 insert()期间使用此 getNextSequence()功能。
db.users.insert(
{
_id: getNextSequence("userid"),
name: "Sarah C."
}
)
db.users.insert(
{
_id: getNextSequence("userid"),
name: "Bob D."
}
)
这样,您可以在同一个计数器集合中维护任意数量的序列。有关upsert问题,请查看此链接Create an auto-increment sequence field中的 乐观循环 块。
第二种方法是使用像mongodb-autoincrement这样的猫鼬中间件。
希望它有所帮助。
答案 1 :(得分:5)
我不知道您使用的是哪个版本的MongoDB和Mongoose,但我无法使用MongoDB 3.0和Mongoose 4.1.10重现您的问题。
我为你做了一个样本,它将创建并保存一个新用户,更新(使用upsert),并通过upsert创建另一个用户。尝试运行此代码:
"use strict";
var mongoose=require("mongoose");
var Schema = require('mongoose').Schema;
var ObjectId = mongoose.Schema.Types.ObjectId;
// Connect to test
mongoose.connect("mongodb://localhost:27017/test");
// Lets create your schema
var userSchema = new Schema({
_id: ObjectId,
userId: {type: Number, unique: true },
name: String
});
var User = mongoose.model("User", userSchema, "Users");
User.remove() // Let's prune our collection to start clean
.then( function() {
// Create our sample record
var myUser = new User({
_id:'561b0fad638e99481ab6d84a',
userId:3,
name:'foo'
});
return myUser.save();
})
.then( function() {
// Now its time to update (upsert userId 3)
return User.update({userId:3},{"$set":{name:'bar'}},{upsert:true});
})
.then( function() {
// Now its time to insert (upsert userId 4)
return User.update({userId:4},{"$set":{name:'bee'}},{upsert:true});
})
.then( function() {
// Lets show what we have inserted
return User.find().then(function(data) {console.log(data)});
})
.catch( function(err) {
// Show errors if anything goes wrong
console.error("ERROR", err);
})
.then( function() {
mongoose.disconnect();
});
答案 2 :(得分:0)
遵循文档(MongoDB 3.0)upsert:如果查询条件与_id字段匹配,则true将不会插入不存在的文档。
请参阅:https://docs.mongodb.org/manual/reference/method/db.collection.update/#mongodb30-upsert-id
为什么不将用户的user_name用作唯一ID?
因为在mongodb环境中使用自动递增字段作为ID是一种不好的做法,特别是如果要使用分片 =>所有插入都将出现在最新的碎片上 => mongodb集群必须经常重新平衡/重新分配数据。 (目前这不会出现在您的系统上,因为您仍然使用生成的_id字段)
您当然可以在user_id字段上创建唯一索引: https://docs.mongodb.org/manual/core/index-unique/#index-type-unique