MongoDB: Упорядоченное соответствие записей массива - PullRequest
0 голосов
/ 25 апреля 2018

У меня есть документ, который выглядит следующим образом:

"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'
                }
            }
        }
    ]
})

Результатом моего запроса является приведенный выше документ.Но результат в этом случае не должен совпадать, так как я ищу

слово: «I» , за которым следуют [word: /f.*/ и pos: 'IN ']

, который не соответствует массиву tokens в документе, так как за маркером I следует played, а затем football.Однако в запросе порядок фильтров отличается, так как поиск начинается с

word: «I»

, за которым следует

ф. *

[футбол в данном случае].

1 Ответ

0 голосов
/ 25 апреля 2018

Оператор $and - это чисто логический оператор, в котором порядок условий фильтра не играет никакой роли .

Таким образом, следующие запросы абсолютно эквивалентны из заданного значениявида:

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

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

Документация гласит:

$ и выполняет логическую операцию И над массивом из двух или более выражений (например,и т. д.) и выбирает документы, которые удовлетворяют всем выражениям в массиве.Оператор $ and использует оценку короткого замыкания.Если первое выражение (например) оценивается как ложное, 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.
    }
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...