创建具有未知条件数的MongoDB查询(使用Mongoose)

时间:2015-09-20 19:47:43

标签: node.js mongodb mongoose mongodb-query aggregation-framework

我们说我有以下文件

{colors: [red, blue, green]}
{colors: [yellow, brown, green]}
{colors: [purple]}
{colors: [red, blue, orange]}

我想创建一个函数来查询集合中是否有输入中至少有一种颜色的文档,这是一个数组。例如,如果我想找到一个红色,蓝色和/或绿色的文档,我会写

findColors([red, blue, green])

它会返回

{colors: [red, blue, green]}
{colors: [red, blue, orange]}
{colors: [yellow, brown, green]}

在上面的订单中,因为具有最完美匹配的文档是第一个,而最不完美的匹配是最后一个。我的问题是数组中的元素数量是任意的,所以我不知道如何将它们组合成一个没有循环的查询。直接传递数组的问题是它只会返回

{colors: [red, blue, green]}

但我希望尽可能多的比赛。另外,阵列中的顺序不应该重要。

有没有办法 1)将多个独立查询组合成一个大查询 2)在数组上使用某种模式匹配函数,以便创建数组元素的每个可能组合,然后我可以将其传递给函数。

另外,为了保持较低的运行时间,我只想写下#34;在找到10个文件后停止运行"

1 个答案:

答案 0 :(得分:1)

您不需要循环查询或任何此类事情。提交多个项目以使用数据库进行测试称为$or条件,或者在这种情况下,如果输入列表与类似的值列表匹配,则可以将其显式缩短为$in。这将返回包含任何列出的值的文档与比较字段,在这种情况下也是一个数组。

唯一需要的是将公共元素与共同元素的数量相匹配。这里聚合框架的$setIntersection运算符可用于匹配元素以及$size以返回匹配列表的大小。

有效地"得分"匹配元素的文档,可以传递给$sort以返回最匹配的元素。然后唯一要问的是"停在10"这是$limit操作对结果的作用。

作为一个完整的例子:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

var data = [
  { "colors": [ "red", "blue", "green" ] },
  { "colors": [ "yellow", "brown", "green" ] },
  { "colors": [ "purple" ] },
  { "colors": [ "red", "blue", "orange" ] }
];


var colorSchema = new Schema({
  "colors": []
});

var Color = mongoose.model('Color',colorSchema);

function findColors(list,callback) {
  Color.aggregate(
    [
      { "$match": { "colors": { "$in": list } } },
      { "$project": {
        "colors": 1,
        "score": {
          "$size": {
            "$setIntersection": [ "$colors", list ]
          }
        }
      }},
      { "$sort": { "score": -1 } },
      { "$limit": 10 }
    ],
    callback
  );
}


async.series(
  [
    function(callback) {
      Color.remove({},callback);
    },
    function(callback) {
      async.each(data,function(item,callback) {
        console.log(item);
        Color.create(item,callback);
      },callback);
    },
    function(callback) {
      console.log("here");
      findColors(["red","blue","green"],function(err,result) {
        console.log(result);
        callback(err);
      });
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
)

产生输出:

[ { _id: 55ff60dc683554e114f90bf2,
    colors: [ 'red', 'blue', 'green' ],
    score: 3 },
  { _id: 55ff60dc683554e114f90bf5,
    colors: [ 'red', 'blue', 'orange' ],
    score: 2 },
  { _id: 55ff60dc683554e114f90bf3,
    colors: [ 'yellow', 'brown', 'green' ],
    score: 1 } ]

因此,匹配最多的文档位于顶部,而较少匹配的文档则位于较低位置。此外,任何与列表都不匹配的内容都将被排除在外。