查找包含数组中特定值的文档,但如果它是最后一个元素则不会

时间:2015-10-27 02:45:40

标签: javascript arrays mongodb mongodb-query

我目前的做法是:

var v = 'Value';
Collection.find({arrayToLookIn: v}).forEach(function(obj) {
  if (obj.arrayToLookIn.indexOf(v) !== obj.arrayToLookIn.length - 1) {
    // do stuff
  }
}

我想知道是否有办法在find()电话中指定这样的规则而不进行内部检查?

我查看了https://docs.mongodb.org/manual/tutorial/query-documents/#match-an-array-element,但没有发现我所寻求的内容。

第一个问题,请温柔:)

1 个答案:

答案 0 :(得分:2)

你现在可以做什么

您想要$where,它可以使用JavaScript评估来匹配文档。所以在这里你要求评估代码测试每个数组元素,而不是最后一个:

Collection.find({
    "arrayToLookIn": v,
    "$where": function() {
        var array = this.arrayToLookIn;
        array.pop();                    // remove last element
        return array.some(function(el) { return el == 'Value' });
    }
})

请注意,因为它是JavaScript发送到服务器的"值"需要在该代码中指定而不是使用变量。您可以选择将JavaScript代码构造为"字符串"将该变量作为文字加入并将其作为参数提交给$where

请注意,我离开了基本的平等匹配,因为$where无法使用类似的索引匹配,因此它的工作就是过滤""过滤"输出匹配在最后一个元素上的结果,而不是测试每个文档以确定它是否在那里。

更好的未来方式

对于好奇的,就目前的MongoDB 3.0发布系列而言,使用聚合框架没有一种真正有效的方法,因此JavaScript评估是更好的选择。

你现在需要做一些愚蠢的事情,比如在$group之后找到$unwind中的最后一个元素,然后在另一个$match之后$unwind找出该值。如果值不止一次存在,它就没有效率且容易出错。

将来的版本将有一个$slice运算符,可以像$redact这样使用:

Collection.aggregate([
    // Still wise to do this as mentioned earlier
    { "$match": { "arrayToLookIn": v } },

    // Then only return if the match was not in the last element
    { "$redact": {
        "$cond": {
            "if": {
                "$setIsSubset": [
                    [v],
                    { "$slice": [
                        "$arrayToLookIn",
                        0,
                        { "$subtract": [ { "$size": "$arrayToLookIn" }, 1 ] }
                    ]}
                ]
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}

$setIsSubset通过仅$slice返回$size的元素减去已通过0删除了最后一个条目的数组的比较1

这应该比$where更高效,因为当使用$slice聚合的下一个版本可用时,它使用本机编码操作进行比较。

更不用说$unwind也可以选择在将来的版本中包含索引位置。但即使添加了它,它仍然不是一个很好的选择。