Mongoose查询,查找B.array中具有A.array匹配项的typesB的所有项目

我有一个模特Person

 schemas.person = new mongoose.Schema({ firstname: {type: String}, lastname: {type: String}, age: Number, email: {type: String}, gender: {type: String}, matches: [{type: mongoose.Schema.Types.ObjectId, ref: models.Person}], }); 

matches是指另一个有相似兴趣的人(即约会目的)

假设我有10个male和10个female

我想写一个查询,给我一个与所有10个女性相互匹配的男性 – 也就是说,所有10个女性出现在他的matchesarrays中,他出现在所有10个女性的matchesarrays中。

所以在伪代码中,它看起来像这样:

 models.Person.find({"gender": "male"}).exec(function(err, men){ models.Person.find({"gender": "female"}).exec(function(err, women){ models.Person.find(some item from men.matches $in all women.matches}, function(err, result){ console.log(result); } } }); 

最后一部分some item from men.matches $in all women.matches一个some item from men.matches $in all women.matches在Mongoose中我不知道该怎么说,我不能在网上find一个好的方法。

请注意,匹配数组看起来像:

  [ { "$oid": "558ced061d35bd072e7b5825" }, { "$oid": "558ced061d35bd072e7b58a0" }, { "$oid": "558ced061d35bd072e7b58c6" } ] 

有什么方法来expression这一点?

两个关键标准是每个人都有一个匹配:

  1. 可能匹配的id必须在它们的matches数组中
  2. 他们的id必须在另一个人的matchesarrays

第三个提到的比赛是女性的标准应该已经通过包含在matchesarrays中来说明,假定这种偏好限制被反映在那里。 如果没有,为什么限制他们的比赛在这里,而不是? 这就是说,这将是一个简单的附加查询参数添加,我会留给你。 #爱赢

所以,这里有一种方法是使用find来获得所有人的列表,然后使用第二个find来检索所有可能的匹配(没有性别排斥)。

 models.Person.find({ gender: "male" }).exec().then(function gotAllMen(persons) { // persons is an array of men // Find any matches for each person return new Promise.all(persons.map(function(person) { return models.Person.find({ _id: {$in: person.matches}, // Satisfies 1 matches: person._id // Satisfies 2 }).exec().then(function(match) { // Create a new object for each match return { person: person, match: match }; }); })); }).then(function matches(matches) { // Have a date }); 

注意:这是未经testing的代码

一种方法是先获取所有女性ID的数组,然后将这个数组与男性查询的matches字段进行比较,最后遍历女性查询来查找匹配项。

为了帮助解释上述概念,假设您在mongo shell中创build了一个testing集合,其中包含以下文档(为了简洁明了,缩短了模式):

 db.test.insert([ { "_id" : 1, "gender" : "female", "name" : "a", "matches" : [6, 10] },{ "_id" : 2, "gender" : "female", "name" : "b", "matches" : [7, 10] },{ "_id" : 3, "gender" : "female", "name" : "c", "matches" : [8, 10] },{ "_id" : 4, "gender" : "female", "name" : "d", "matches" : [9, 10] },{ "_id" : 5, "gender" : "female", "name" : "e", "matches" : [10] },{ "_id" : 6, "gender" : "male", "name" : "f", "matches" : [1, 3 ] },{ "_id" : 7, "gender" : "male", "name" : "g", "matches" : [1, 2 ] },{ "_id" : 8, "gender" : "male", "name" : "h", "matches" : [3, 5] },{ "_id" : 9, "gender" : "male", "name" : "i", "matches" : [5] },{ "_id" : 10, "gender" : "male", "name" : "j", "matches" : [1,2,3,4,5] } ]) 

从观察结果可以看出,满足查询条件的是给予你一个与所有5个女性相互匹配的男性的查询 – 也就是说,所有5个女性出现在他的匹配数组中,并且他出现在匹配数组中全部5名女性。

现在我们如何在mongo shell中翻译这个查询呢?

 function arraysEqual(a, b) { if (a === b) return true; if (a == null || b == null) return false; if (a.length != b.length) return false; a.sort(); b.sort(); for (var i = 0; i < a.length; ++i) { if (a[i] !== b[i]) return false; } return true; } var all_females = db.test.find({"gender": "female"}).map(function(p){return p._id}), male = {}, matched = {}; db.test.find({"gender": "male"}).forEach(function (m){ if (arraysEqual(m.matches, all_females)) male = m; }); var matches = db.test.find({"gender": "female", "matches": male._id}).map(function(p){return p._id}); if (arraysEqual(matches, all_females)) matched = male; printjson(matched); 

输出

 { "_id" : 10, "gender" : "male", "name" : "j", "matches" : [1,2,3,4,5] } 

现在你可以使用lodash库中的helper方法_.each()_.isEqual()来实现上面的Mongoose,如下所示(未经testing):

 var male = {}, matched = {}; models.Person.find({"gender": "female"}).exec(function(err, docs){ if(err) return res.send(err); var all_females = docs.map(function(p){return p._id}); models.Person.find({"gender": "male"}).exec(function(err, men){ _.each(men, function(m, i) { if (_.isEqual(all_females, m.matched)) male = m; }); models.Person.find({"gender": "female", "matches": male._id}).exec(function(err, women){ var matched_females = women.map(function(w){return w._id}); if (_.isEqual(all_females, matched_females)) matched = male; console.log(matched); }); }); });