本文介绍了如果不存在则如何创建项目,如果存在则返回错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写alexa技能,并希望检查用户是否存在于MongoDB中.我的代码有效,但是如果用户已经在数据库中,我不知道如何定义情况:(

I'm writing alexa skill and would like to check if user exists in MongoDB. My code works but I don't know how to define situation if user is already in a database :(

每次执行代码时,都会得到:安娜,你好,你是新来的"

Everytime when I execute code I get:"Hello Anna you are new here"

我的用户Anna被保存在MongoDB中

My user Anna is saved in MongoDB

但是我想区分用户何时已经在数据库中并对此做出反应.

But I would like to distinguish when my user is already in a database and react for that.

有什么聪明的人可以解决我的问题吗?

Does anybody smart has a solution for my problem?

    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")
            }

        });

推荐答案

如前面的注释中所述,您有两种基本方法来确定是否创建"了某些东西.这些是:

As noted in comment earlier, you have two basic approaches to work out whether something was "created" or not. These are either to:

  • 在响应中返回rawResult并检查updatedExisting属性,该属性告诉您是否为"upsert"

  • Return the rawResult in the response and check the updatedExisting property which tells you if it's an "upsert" or not

设置new: false,以便当实际上是"upsert"时在结果中实际返回"no document"

Set new: false so that "no document" is actually returned in result when it's actually an "upsert"

作为演示清单:

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()
  }


})()

输出:

Mongoose: users.remove({}, {})
Mongoose: users.findAndModify({ username: 'Bill' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: true, rawResult: true, remove: false, fields: {} })
{
  "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: users.findAndModify({ username: 'Bill' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: true, rawResult: true, remove: false, fields: {} })
{
  "lastErrorObject": {
    "n": 1,
    "updatedExisting": true
  },
  "value": {
    "_id": "5adfc8696878cfc4992e7634",
    "username": "Bill",
    "__v": 0,
    "password": "password"
  },
  "ok": 1,
  "operationTime": "6548172736517111811",
  "$clusterTime": {
    "clusterTime": "6548172736517111811",
    "signature": {
      "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
      "keyId": 0
    }
  }
}
Mongoose: users.findAndModify({ username: 'Ted' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: false, remove: false, fields: {} })
null
Mongoose: users.findAndModify({ username: 'Ted' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: false, remove: false, fields: {} })
{
  "_id": "5adfc8696878cfc4992e7639",
  "username": "Ted",
  "__v": 0,
  "password": "password"
}

所以第一种情况实际上是考虑此代码的:

So the first case actually considers this code:

User.findOneAndUpdate(
  { username: 'Bill' },
  { $setOnInsert: { password: 'password' } },
  { upsert: true, new: true, rawResult: true }
)

大多数选项在这里都是标准的,因为全部" "upsert"操作将导致字段内容用于匹配"(即username)是始终" 在新文档中创建,因此您无需 $set 该字段.为了在后续请求中不实际修改"其他字段,您可以使用 $setOnInsert ,仅在未找到匹配项的"upsert"动作期间添加这些属性.

Most options are standard here as "all" "upsert" actions will result in the field content being used to "match" ( i.e the username ) is "always" created in the new document, so you don't need to $set that field. In order to not actually "modify" other fields on subsequent requests you can use $setOnInsert, which only adds these properties during an "upsert" action where no match is found.

在这里,标准new: true用于从操作中返回已修改"的文档,但不同之处在于rawResult,如返回的响应所示:

Here the standard new: true is used to return the "modified" document from the action, but the difference is in the rawResult as is shown in the returned response:

{
  "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
    }
  }
}

您会从驱动程序获得实际的原始"响应,而不是猫鼬文档".实际的文档内容在"value"属性下,但它是我们感兴趣的"lastErrorObject".

Instead of a "mongoose document" you get the actual "raw" response from the driver. The actual document content is under the "value" property, but it's the "lastErrorObject" we are interested in.

在这里我们看到属性updatedExisting: false.这表明实际上未找到不匹配",因此创建"了一个新文档.因此,您可以使用它来确定创建确实发生了.

Here we see the property updatedExisting: false. This indicates that "no match" was actually found, thus a new document was "created". So you can use this to determine that creation actually happened.

当您再次发出相同的查询选项时,结果将有所不同:

When you issue the same query options again, the result will be different:

{
  "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
    }
  }
}

updatedExisting的值现在为true,这是因为在查询语句中已经存在与username: 'Bill'匹配的文档.这告诉您文档已经在那儿,因此您可以分支逻辑以返回错误"或所需的任何响应.

The updatedExisting value is now true, and this is because there already was a document that matched the username: 'Bill' in the query statement. This tells you the document was already there, so you can then branch your logic to return an "Error" or whatever response you want.

在另一种情况下,可能希望不"返回原始"响应,而使用返回的猫鼬文档".在这种情况下,我们将值更改为new: false,而没有rawResult选项.

In the other case, it may be desirable to "not" return the "raw" response and use a returned "mongoose document" instead. In this case we vary the value to be new: false without the rawResult option.

User.findOneAndUpdate(
  { username: 'Ted' },
  { $setOnInsert: { password: 'password' } },
  { upsert: true, new: false }
)

除了现在返回的是文档的原始状态,而与操作之后的文档的已修改"状态相反,大多数情况相同.因此,当没有与"query"语句实际匹配的文档时,返回的结果为null:

Most of the same things apply except that now the action is the original state of the document is returned as opposed to the "modified" state of the document "after" the action. Therefore when there is no document that actually matches the "query" statement, the returned result is null:

Mongoose: users.findAndModify({ username: 'Ted' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: false, remove: false, fields: {} })
null           // <-- Got null in response :(

这告诉您文档是创建的",并且自从您使用语句发送数据以来(最好是在$setOnInsert中),您已经知道文档的内容是有争议的.重要的是,您已经知道要实际返回文档内容需要返回什么.

This tells you the document was "created", and it's arguable that you already know what the content of the document should be since you sent that data with the statement ( ideally in the $setOnInsert ). Point being, you already know what to return "should" you require to actually return the document content.

相比之下,找到"的文档返回原始状态",显示文档在被修改之前":

By contrast, a "found" document returns the "original state" showing the document "before" it was modified:

{
  "_id": "5adfc8696878cfc4992e7639",
  "username": "Ted",
  "__v": 0,
  "password": "password"
}

因此,任何不是"null"的响应都表明该文档已经存在,并且您可以再次根据响应中实际收到的内容来分支逻辑.

Therefore any response which is "not null" is therefore an indication that the document was already present, and again you can branch your logic depending on what was actually received in response.

因此,这是您要问的两种基本方法,并且肯定可以起作用"!正如此处使用相同的语句所演示和可复制的一样.

So those are the two basic approaches to what you are asking, and they most certainly "do work"! And just as is demonstrated and reproducible with the same statements here.

在完整列表中也暗示了一种更有效的方法,该方法本质上是简单地.insert()(或猫鼬模型中的.create())新数据,并带有重复键"错误,其中实际遇到按索引的唯一"属性.这是一种有效的方法,但是用户验证"中有一个特殊的用例,这是一种方便的逻辑处理,也就是验证密码".

There is one more valid approach that is hinted at in the full listing as well, which is essentially to simply .insert() ( or .create() from mongoose models ) new data and have a "duplicate key" error throw where the "unique" property by index is actually encountered. It's a valid approach but there is one particular use case in "user validation" which is a handy piece of logic handling, and that is "validating passwords".

因此,通过usernamepassword组合检索用户信息是一种非常常见的模式.在"upsert"的情况下,此组合证明是唯一"的,因此,如果找不到匹配项,则尝试"insert".这正是使密码匹配成为此处有用的实现的原因.

So it's a pretty common pattern to retrieve user information by the username and password combination. In the case of an "upsert" this combination justifies as "unique" and therefore an "insert" is attempted if no match is found. This is exactly what makes matching the password a useful implementation here.

请考虑以下内容:

    // 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 }
    );

在第一次尝试中,我们实际上并没有"Fred"username,因此将发生"upsert",并且上面已经描述的所有其他事情都会发生,以识别它是创建文档还是找到的文档

On the first attempt we don't actually have a username for "Fred", so the "upsert" would occur and all the other things as already described above happen to identify whether it was a creation or a found document.

后面的语句使用相同的username值,但提供与所记录内容不同的密码. MongoDB在这里尝试创建"新文档,因为它在组合上不匹配,但是由于username应该是"unique",因此您会收到重复键错误":

The statement that follows uses the same username value but provides a different password to what is recorded. Here MongoDB attempts to "create" the new document since it did not match on the combination, but because the username is expected to be "unique" you receive a "Duplicate key error":

{ MongoError: E11000 duplicate key error collection: thereornot.users index: username_1 dup key: { : "Fred" }

因此,您应该意识到的是,您现在获得了三个条件来评估免费".存在:

So what you should realize is you now get three conditions to evaluate for "free". Being:

  • 根据方法,通过updatedExisting: falsenull结果记录"upsert".
  • 您知道文档(通过组合)通过updatedExisting: true存在",或者文档返回的位置是不是null".
  • 如果提供的passwordusername的已存在项不匹配,则您将得到重复键错误",您可以对其进行陷阱并做出相应的响应,并向用户建议密码"不正确".
  • The "upsert" was recorded by either the updatedExisting: false or null result depending on the method.
  • You know the document ( by combination ) "exists" via either the updatedExisting: true or where the document returns was "not null".
  • If the password provided was not a match for what already existed for the username, then you would get the "duplicate key error" which you can trap and respond accordingly, advising the user in response that the "password is incorrect".

全部来自一个请求.

这是使用"upserts"的主要理由,而不是在集合上简单地插入插入内容,因为您可以获得逻辑的不同分支,而无需向数据库提出其他请求来确定这些条件中的哪一个"应为实际响应.

That's the main reasoning for using "upserts" as opposed to simply throwing inserts at a collection, as you can get different branching of the logic without making additional requests to the database to determine "which" of those conditions should be the actual response.

这篇关于如果不存在则如何创建项目,如果存在则返回错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-07 03:47