MongoDB:数组条目的有序匹配

时间:2018-04-25 18:31:01

标签: mongodb

我有一个看起来像这样的文件:

"tokens":
[
  {
    "index": 1,
    "word": "I",
    "pos": "NNP",
  },
  {
    "index": 2,
    "word": "played",
    "pos": "VBZ",
  },
  {
    "index": 3,
    "word": "football",
    "pos": "IN",
  }
]

我的疑问是:

db.test.find({
    $and: [
        {
            'tokens.word': 'I'
        },
        {
            tokens: {
                $elemMatch: {
                    word: /f.*/,
                    pos: 'IN'
                }
            }
        }
    ]
})

我的查询输出是上面的文档。但是在这种情况下结果应该是不匹配的,因为我正在搜索

  

字:“我”后跟 [字:/f.*/和pos:'IN']

与文档中的tokens数组不匹配,因为令牌I后跟played,然后是football。但是,在查询中,过滤器的顺序不同,因为使用

开始搜索
  

字:“我”

接着是

  

F。*

[足球在这种情况下]。

1 个答案:

答案 0 :(得分:0)

$and运算符是纯逻辑运算符,其中过滤条件的顺序不起任何作用

因此,从结果集的角度来看,以下查询绝对等效:

$and: [
    { "a": 1 },
    { "b": 2 }
]

$and: [
    { "b": 2 },
    { "a": 1 }
]

documentation州:

  

$并对两个或更多的数组执行逻辑AND运算   表达式(例如,等)并选择   满足数组中所有表达式的文档。 $和   操作员使用短路评估。如果是第一个表达式(例如   )评估为false,MongoDB不会评估   剩下的表达。

在您的示例中,"index1"条目与第一个过滤器"tokens.word": "I"匹配,"index3"文档与第二个$elemMatch过滤器匹配。所以文件必须返回。

<强>更新

这是一个想法 - 更多的是一个真正的起点 - 让你更接近你想要的东西:

db.collection.aggregate({
    $addFields: { // create a temporary field 
        "differenceBetweenMatchPositions": { // called "differenceBetweenMatchPositions"
            $subtract: [ // which holds the difference between
                { $indexOfArray: [ "$tokens.word", "I" ] }, // the index of first element matching first condition and
                { $min: { // the lowest index of
                    $filter: { // a filtered array
                        input: { $range: [ 0, { $size: "$tokens" } ] }, // that holds the indices [ 0, 1, 2, ..., n ] where n is the number of items in the "tokens" array - 1
                        as: "this", // which we want to access using "$$this"
                        cond: {
                            $let: {
                                vars: { "elem": { $arrayElemAt: [ "$tokens", "$$this" ] } }, // get the n-th element in our "tokens" array
                                in: {
                                    $and: [
                                        { $eq: [ "$$elem.pos", "IN" ] }, // "pos" field must be "IN"
                                        { $eq: [ { $substrBytes: [ "$$elem.word", 0, 1 ] }, "f" ] } // 1st character of the "word" must be "f"
                                    ] 
                                }
                            }
                        }
                    }
                }
            }]
        }
    }
}, {
    $match: {
        "differenceBetweenMatchPositions": { $lt: 0 } // we only want documents where the first condition is matched by an item in our array before the second one gets matched, too.
    }
})