Node.js 如果不存在,如何创建项;如果存在,如何返回错误
我正在写alexa skill,希望检查MongoDB中是否存在用户。我的代码可以工作,但如果用户已经在数据库中,我不知道如何定义这种情况:( 每次执行代码时,我都会得到: “你好,安娜,你是新来的” 我的用户Anna保存在MongoDB中 但我想区分我的用户何时已经在数据库中,并对此作出反应 有谁能解决我的问题吗Node.js 如果不存在,如何创建项;如果存在,如何返回错误,node.js,mongodb,mongoose,Node.js,Mongodb,Mongoose,我正在写alexa skill,希望检查MongoDB中是否存在用户。我的代码可以工作,但如果用户已经在数据库中,我不知道如何定义这种情况:( 每次执行代码时,我都会得到: “你好,安娜,你是新来的” 我的用户Anna保存在MongoDB中 但我想区分我的用户何时已经在数据库中,并对此作出反应 有谁能解决我的问题吗 var myName = "Anan1"; var userID = this.event.session.user.userId; console.log(
var myName = "Anan1";
var userID = this.event.session.user.userId;
console.log(userID);
self = this;
User.findOneAndUpdate(
{userId: userID},
{$set:{name:myName}},
{upsert: true, new: false, runValidators: true},
function(err, doc){
if(err){
console.log("eeoror");
}
console.log(doc);
if (doc==null){
self.emit(':ask',
"Hello "+ myName +"you are new here")
}else {
self.emit(':ask',
"Hello "+ myName +"you are not new here")
}
});
听起来,你真正想要的是一个成功的人,而不是一个成功的人 可以在[mongoose]中使用模式字段选项设置唯一键:
const s = new Schema({ name: { type: String, unique: true }});
或通过以下方法:
如果试图创建一个文档,但该文档已包含该键的条目,则将抛出一个错误:
注意:违反该约束将在保存时从MongoDB返回E11000错误,而不是Mongoose验证错误
正如前面的评论所述,您有两种基本方法来确定某个东西是否是“创建”的。它们是:
- 返回响应中的
,并检查rawResult
属性,该属性告诉您是否为“upsert”updatedeexisting
- 设置
,以便在结果中实际返回“no document”,而实际上是“upsert”new:false
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/thereornot';
mongoose.set('debug', true);
mongoose.Promise = global.Promise;
const userSchema = new Schema({
username: { type: String, unique: true }, // Just to prove a point really
password: String
});
const User = mongoose.model('User', userSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
// Shows updatedExisting as false - Therefore "created"
let bill1 = await User.findOneAndUpdate(
{ username: 'Bill' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: true, rawResult: true }
);
log(bill1);
// Shows updatedExisting as true - Therefore "existing"
let bill2 = await User.findOneAndUpdate(
{ username: 'Bill' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: true, rawResult: true }
);
log(bill2);
// Test with something like:
// if ( bill2.lastErrorObject.updatedExisting ) throw new Error("already there");
// Return will be null on "created"
let ted1 = await User.findOneAndUpdate(
{ username: 'Ted' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: false }
);
log(ted1);
// Return will be an object where "existing" and found
let ted2 = await User.findOneAndUpdate(
{ username: 'Ted' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: false }
);
log(ted2);
// Test with something like:
// if (ted2 !== null) throw new Error("already there");
// Demonstrating "why" we reserve the "Duplicate" error
let fred1 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'password' },
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
log(fred1); // null - so okay
let fred2 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'badpassword' }, // <-- dup key for wrong password
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
mongoose.disconnect();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
因此,第一个案例实际上考虑了以下代码:
User.findOneAndUpdate(
{ username: 'Bill' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: true, rawResult: true }
)
这里的大多数选项都是标准的,因为“所有”“upsert”
操作将导致用于“匹配”的字段内容(即用户名
)是在新文档中创建的“始终”,因此您不需要修改该字段。为了不实际“修改”您可以在后续请求上使用的其他字段,这些字段仅在未找到匹配项的“upsert”
操作期间添加这些属性
这里,标准的new:true
用于从操作返回“修改过的”文档,但区别在于rawResult
,如返回的响应所示:
{
"lastErrorObject": {
"n": 1,
"updatedExisting": false,
"upserted": "5adfc8696878cfc4992e7634"
},
"value": {
"_id": "5adfc8696878cfc4992e7634",
"username": "Bill",
"__v": 0,
"password": "password"
},
"ok": 1,
"operationTime": "6548172736517111811",
"$clusterTime": {
"clusterTime": "6548172736517111811",
"signature": {
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId": 0
}
}
}
您从驱动程序获得的不是“mongoose文档”,而是实际的“原始”响应。实际文档内容位于“value”
属性下,但我们感兴趣的是“lastErrorObject”
这里我们看到属性updatedeExisting:false
。这表示实际上没有找到“匹配项”,因此“创建”了一个新文档。因此,您可以使用该属性来确定实际发生的创建
再次发出相同的查询选项时,结果将不同:
{
"lastErrorObject": {
"n": 1,
"updatedExisting": true // <--- Now I'm true
},
"value": {
"_id": "5adfc8696878cfc4992e7634",
"username": "Bill",
"__v": 0,
"password": "password"
},
"ok": 1,
"operationTime": "6548172736517111811",
"$clusterTime": {
"clusterTime": "6548172736517111811",
"signature": {
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId": 0
}
}
}
大多数相同的情况都适用,只是现在操作是返回文档的原始状态,而不是在“操作”之后返回文档的“修改”状态。因此,当没有与“查询”语句实际匹配的文档时,返回的结果是null
:
Mongoose: users.findAndModify({ username: 'Ted' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: false, remove: false, fields: {} })
null // <-- Got null in response :(
因此,任何“notnull
”的响应都表示文档已经存在,并且您可以根据响应中实际接收到的内容来分支逻辑
因此,这是两种基本的方法来回答你的问题,它们肯定是“起作用的”!正如在这里用同样的陈述所演示和再现的一样
附录-为错误密码保留重复密钥 完整列表中还暗示了一种更有效的方法,这基本上就是简单地
.insert()
(或.create()
来自mongoose模型)新数据,并在“unique”处抛出“replicate key”错误这是一种有效的方法,但在“用户验证”中有一个特殊的用例,这是一种方便的逻辑处理,即“验证密码”
因此,通过用户名
和密码
组合来检索用户信息是一种非常常见的模式。在“upsert”的情况下,这种组合被证明是“唯一的”,因此,如果没有找到匹配项,就会尝试“插入”。这正是使匹配密码成为一种有用的实现的原因
考虑以下几点:
// Demonstrating "why" we reserve the "Duplicate" error
let fred1 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'password' },
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
log(fred1); // null - so okay
let fred2 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'badpassword' }, // <-- dup key for wrong password
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
因此,您应该意识到,您现在获得了三个“免费”评估的条件。即:
- “upsert”由
或更新的existing:false
结果记录,具体取决于方法null
- 您可以通过
或文档返回的“notupdatedeexisting:true
”来知道文档(通过组合)“存在”null
- 如果提供的
与密码
的现有密码不匹配,则您将获得“重复密钥错误”,您可以捕获该错误并相应地作出响应,并建议用户响应“密码不正确”用户名
这是使用“upserts”而不是简单地向集合抛出insert的主要原因,因为您可以获得不同的逻辑分支,而无需向数据库发出额外的请求来确定“哪个”这些条件中应该有实际的响应。你可以做
new:false
,只需检查返回的文档是否为null
。也就是说,notnull
表示它以前存在,null
表示它已创建。在这种情况下,你基本上应该拥有所有文档的详细信息,所以你可能不知道'不需要匹配的返回值。或者,您可以请求rawResponse
,它应该指示是否发生了upsert
。此外,您可能真的希望$setOnInsert
在这里。它不起作用:(我一直在打招呼您不是新来的。我更新了我的代码,请您看看:)这实际上并不重要,因为操作已声明为使用upsert
User.findOneAndUpdate( { username: 'Ted' }, { $setOnInsert: { password: 'password' } }, { upsert: true, new: false } )
Mongoose: users.findAndModify({ username: 'Ted' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: false, remove: false, fields: {} })
null // <-- Got null in response :(
{
"_id": "5adfc8696878cfc4992e7639",
"username": "Ted",
"__v": 0,
"password": "password"
}
// Demonstrating "why" we reserve the "Duplicate" error
let fred1 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'password' },
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
log(fred1); // null - so okay
let fred2 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'badpassword' }, // <-- dup key for wrong password
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
{ MongoError: E11000 duplicate key error collection: thereornot.users index: username_1 dup key: { : "Fred" }