如何使用node js与mongoose创建一个唯一的json元素列表

How to create a unique list of json elements using node js with mongoose

本文关键字:唯一 一个 json 列表 元素 js node 何使用 mongoose 创建      更新时间:2023-09-26

我试图创建一个应用程序,用户添加到数据库的json文件。这个文件有三个属性:Name, Id和另一个值,我需要这个Id是唯一的,如果用户试图用这个Id创建另一个用户,他将得到一个错误消息。

我试图创建一个唯一的对象,但当然它没有工作(使用猫鼬场景):

    var userSchema = mongoose.Schema({
    list_users_ticket: {type: [{
        whoChecked_name: String,
        whoChecked_id: String,
        what_checked: String
    }], unique: true},
    name : { type: String, required: true } ,
    reg_id: { type: String, required: true, unique: true }
});

mongoose.createConnection('mongodb://------------');
var User = mongoose.model('User', userSchema);
// make this available to our users in our Node applications
module.exports = User;

我在dbRequests中是这样使用的:

   exports.update = function(cur_reg_id, user_ticked) {
    User.find({reg_id: cur_reg_id},function(err, users){
       //removeusers(cur_reg_id);
        if (err) throw err;
        var len = users.length;
        console.log("Len" + len)
        if(len == 0 || len > 1){
                callback({'response':"You didn't register"});
        } else {

// Here I want to check if the value exist or not, and if yes, update the jsn filed like the code below :)

                users[0].list_users_ticket.push(user_ticked);
                console.log(users[0])
                users[0].save(function(err) {
                    if (err) throw err;
                    callback({'response':"User Saved   " + user_ticked});
                });
        }});

        //console.log('User successfully updated!');
}

这段代码不适合我。我可以遍历列表中的所有元素,但这会在服务器中消耗大量的功率。

索引不是这样工作的。其目的是使元素在整个"集合"中是"唯一的",而不是在文档中,当然也不是在文档中的数组项

对于数组子文档的每个键都具有所有值的"唯一"项的非常简单的情况,那么答案是 $addToSet

但是这里有bug

虽然$addToSet操作符在这里是正确的,但Mongoose目前并没有很好地发挥作用,特别是它正在重新排序要添加的对象中的键,从而使原本相同的对象不再相同。

从MongoDB的角度来看,如果键与数组中已经存在的对象的顺序不同,那么它就不是"相同"的,并且会添加一个新对象。这种重新排序使得$addToSet作为操作符在这种情况下毫无用处。

唯一的解决方法是在键的顺序不重要的情况下测试数组中对象的存在,这通常是查询操作符的情况。因此,使用 $elemMatch $not 条件应该可以达到目的:

{
  "list_users_ticket": {
    "$not": {
        "$elemMatch": {
            "whoChecked_name": "bob",
            "whoChecked_id": "1",
            "what_checked": "something"
        }
    }
  }
}

作为完整的演示:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/usertest');
mongoose.set("debug",true);
var ticketSchema = new Schema({
  whoChecked_name: String,
  whoChecked_id: String,
  what_checked: String
},{ "_id": false });
var userSchema = new Schema({
  list_users_ticket: [ticketSchema]
});
var User = mongoose.model( "User", userSchema );
var userData = {
  whoChecked_name: "bob",
  whoChecked_id: "1",
  what_checked: "something"
};
var query = {
  "list_users_ticket": {
    "$not": {
      "$elemMatch": userData
    }
  }
};
function logger(data) {
  return JSON.stringify( data, undefined, 2 );
}
async.waterfall(
  [
    function(callback) {
      User.remove({},function(err) {
        callback(err);
      })
    },
    function(callback) {
      var user= new User({ "list_users_ticket": userData });
      user.save(function(err,user) {
        callback(err,user);
      });
    },
    function(user,callback) {
      console.log("Previous State: %s", logger(user));
      query["_id"] = user._id;
      User.findOneAndUpdate(
        query,
        { "$push": { "list_users_ticket": userData } },
        { "new": true },
        function(err,doc) {
          if (err) callback(err);
          console.log("Modified: %s", logger(doc));
          callback();
        }
      );
    },
    function(callback) {
      User.findOne({},function(err,user) {
        if (err) callback(err);
        console.log("Current: %s", logger(user));
        callback();
      });
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);
与输出:

Mongoose: users.remove({}) {}
Mongoose: users.insert({ _id: ObjectId("55cffae0b5fd68c324e6934b"), list_users_ticket: [ { whoChecked_name: 'bob', whoChecked_id: '1', what_checked: 'something' } ], __v: 0 })
Previous State: {
  "__v": 0,
  "_id": "55cffae0b5fd68c324e6934b",
  "list_users_ticket": [
    {
      "whoChecked_name": "bob",
      "whoChecked_id": "1",
      "what_checked": "something"
    }
  ]
}
Mongoose: users.findAndModify({ _id: ObjectId("55cffae0b5fd68c324e6934b"), list_users_ticket: { '$not': { '$elemMatch': { whoChecked_name: 'bob', whoChecked_id: '1', what_checked: 'something' } } } }) [] { '$push': { list_users_ticket: { what_checked: 'something', whoChecked_id: '1', whoChecked_name: 'bob' } } } { new: true, upsert: false, remove: false }
Modified: null
Mongoose: users.findOne({}) { fields: undefined }
Current: {
  "_id": "55cffae0b5fd68c324e6934b",
  "__v": 0,
  "list_users_ticket": [
    {
      "whoChecked_name": "bob",
      "whoChecked_id": "1",
      "what_checked": "something"
    }
  ]
}

这里的签入基本上确保在要插入的数据的数组中没有找到匹配的元素,否则将根本不匹配文档。虽然这与$addToSet操作的行为不完全相同,但就是否将项添加到数组而言,结果是相同的。

根据您的需要,这可能需要也可能不需要做更多的工作,但是在执行这样的$addToSet操作时,它是一种变通方法,直到做了一些事情来保留和不改变对象键的顺序。


原创内容

假设User是关联模式的模型,并且从数据库中删除并清除了"index"约束。异步。瀑布也有助于这里的流:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/usertest');
mongoose.set("debug",true);
var ticketSchema = new Schema({
  whoChecked_name: String,
  whoChecked_id: String,
  what_checked: String
},{ "_id": false });
var userSchema = new Schema({
  list_users_ticket: [ticketSchema]
});
var User = mongoose.model( "User", userSchema );
var userData = {
  whoChecked_name: "bob",
  whoChecked_id: "1",
  what_checked: "something"
};
function logger(data) {
  return JSON.stringify( data, undefined, 2 );
}
async.waterfall(
  [
    function(callback) {
      User.remove({},function(err) {
        callback(err);
      })
    },
    function(callback) {
      var user= new User({ "list_users_ticket": userData });
      user.save(function(err,user) {
        callback(err,user);
      });
    },
    function(user,callback) {
      console.log("Previous State: %s", logger(user));
      User.findByIdAndUpdate(user._id,
        { "$addToSet": { "list_users_ticket": userData } },
        { "new": true },
        function(err,doc) {
          if (err) callback(err);
          console.log("Modified: %s", logger(doc));
          callback();
        }
      );
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

这将表明,当您向"set"的成员添加"完全相同的细节"时,结果是没有添加任何额外的内容。

所以你点不是使用索引,而是控制逻辑,如所示。


大编辑


另一个巨大的猫鼬bug。以下是列出的代码中当前mongoose版本的详细信息:

Mongoose: users.remove({}) {}
Mongoose: users.insert({ _id: ObjectId("55cf60bc5fd3f5a70937190b"), list_users_ticket: [ { whoChecked_name: 'bob', whoChecked_id: '1', what_checked: 'something' } ], __v: 0 })
Previous State: {
  "__v": 0,
  "_id": "55cf60bc5fd3f5a70937190b",
  "list_users_ticket": [
    {
      "whoChecked_name": "bob",
      "whoChecked_id": "1",
      "what_checked": "something"
    }
  ]
}
Mongoose: users.findAndModify({ _id: ObjectId("55cf60bc5fd3f5a70937190b") }) [] { '$addToSet': { list_users_ticket: { what_checked: 'something', whoChecked_id: '1', whoChecked_name: 'bob' } } } { new: true, upsert: false, remove: false }
Modified: {
  "_id": "55cf60bc5fd3f5a70937190b",
  "__v": 0,
  "list_users_ticket": [
    {
      "whoChecked_name": "bob",
      "whoChecked_id": "1",
      "what_checked": "something"
    },
    {
      "what_checked": "something",
      "whoChecked_id": "1",
      "whoChecked_name": "bob"
    }
  ]
}

查看新插入的"set"条目中的"键顺序"有何不同。仍然想知道MongoDB自己是如何接受这个的。

而标准节点驱动程序清单:

var async = require("async"),
    mongodb = require("mongodb");
    MongoClient = mongodb.MongoClient;
var userData = {
  who_checked_name: "bob",
  whoChecked_id: "1",
  what_checked: "something"
};
MongoClient.connect('mongodb://localhost/usertest',function(err,db) {
  function logger(data) {
    return JSON.stringify( data, undefined, 2 );
  }
  var collection = db.collection('users');
  async.waterfall(
    [
      function(callback) {
        collection.remove({},function(err) {
          console.log("did that");
          callback(err);
        });
      },
      function(callback) {
        collection.insert({ "list_users_ticket": [userData] },function(err,doc) {
          console.log("that too");
          callback(err,doc);
        });
      },
      function(user,callback) {
        console.log( logger(user) );
        collection.findOneAndUpdate(
          { "_id": user._id },
          { "$addToSet": { "list_users_ticket": userData } },
          { "returnOriginal": false },
          function(err,doc) {
            if (err) callback(err);
            console.log( logger(doc) );
            callback();
          }
        );
      }
    ],
    function(err) {
      if (err) throw err;
      db.close();
    }
  );
});

做正确的事情和期望的事情:

{
  "result": {
    "ok": 1,
    "n": 1
  },
  "ops": [
    {
      "list_users_ticket": [
        {
          "who_checked_name": "bob",
          "whoChecked_id": "1",
          "what_checked": "something"
        }
      ],
      "_id": "55cf62024cceb3ed181a7fbc"
    }
  ],
  "insertedCount": 1,
  "insertedIds": [
    "55cf62024cceb3ed181a7fbc"
  ]
}
{
  "lastErrorObject": {
    "updatedExisting": true,
    "n": 1
  },
  "value": {
    "_id": "55cf62024cceb3ed181a7fbc",
    "list_users_ticket": [
      {
        "who_checked_name": "bob",
        "whoChecked_id": "1",
        "what_checked": "something"
      }
    ]
  },
  "ok": 1
}
相关文章: