Фильтрация вложенных массивов строк с помощью Cloudant Query

Я пытаюсь придумать запрос для фильтрации результатов из структуры данных трех вложенных массивов строк. Например:

{"field":
    [
        [
            ["str1", "str2"],
            ["str3", "str4"]
        ],
        [
            ["str5", "str6"],
            ["str7", "str8", "str9"]
        ],
        [
            ["str10", "str11", "str12"]
        ]
    ]
}

Чтобы документ был действительным, все средние массивы должны иметь хотя бы один внутренний массив, который проходит фильтр, чтобы проверить, не содержатся ли в нем определенные строки.

фильтрация str1 сделает документ действительным, так как ["str3", str4"] все равно будет проверять средний массив этого. Фильтрация для str1 а также str3 будет иметь первый средний массив не передать и не вернуть документ.

То, что я мог придумать, что, к сожалению, только кажется, работает в моей голове, это

{"selector":{                         //comments start at the bottom and go up
 "field":{
  "$not":{                            //negate the outer array result so it passes if all middle arrays passed
   "$elemMatch":{                     //group the negated middle arrays results and fail outer array if all negated middle arrays failed
    "$not":{                          //expose failed middle arrays by negating the result
     "$elemMatch":{                   //group the results and pass middle array if at least one of the inner arrays is valid
      "$not":{                        //negate the results to pass the ones not to be filtered
       "$elemMatch":{"$in":["str1"]}  //find which of the inner arrays contain the strings to be filtered out
}}}}}}}}

Это, однако, не возвращает никаких документов.

Тестирование для небольших случаев дает ожидаемые результаты при работе с одним массивом. За {"field":["str1", "str2", "str3"]}, {"selector":{"$elemMatch":{"$in":["str1"]}}} возвращает документ. {"selector":{"$not":{"$elemMatch":{"$in":["str1"]}}}} не делает.

Для одного уровня вложенных массивов все сломалось. За

{"field":[["str1", "str2"],["str3", "str4"]]}

селектор

{"selector":{
 "fields":{
  "$elemMatch":{
   "$elemMatch":{"$in":["str1"]
}}}}}

возвращает документ. Но

{"selector":{
 "fields":{
  "$elemMatch":{
   "$not":{
    "$elemMatch":{"$in":["str1"]
}}}}}}

не делает. Как я понимаю, "$elemMatch":{"$in":["str1"] пройдет ["str1", "str2"] и потерпеть неудачу ["str3", "str4"], Отрицание того, что имеет ["str1", "str2"] провал и ["str3", "str4"] прохождение. ElemMatch будет затем передать и вернуть документ, потому что ["str3", "str4"] проходит.

А как насчет $elemMatch, $not или их взаимодействие идет вразрез с тем, что я о них понял? Есть ли более идеальный способ создания этого запроса?

Я довольно новичок в Cloudant и NoSQL в целом, и я руководил документацией Cloudant, CouchDB и MongoDB и поиском в Google, но для этой проблемы я рисую пробелы.

Заранее спасибо.

Редактировать (23/02/17): более конкретный пример

Эти документы будут представлять рецепты и их ингредиенты, а также теги, представляющие дополнительную информацию об ингредиентах, таких как возможные триггеры аллергии для пользователей или вещи, которые они могут захотеть избегать или искать в целом.

{"name":"cake"
"ingredients":
    [
        [
            {"name":"flour", "tags":["gluten", "carbs"]},
            {"name":"gluten free flour substitute", "tags":["carbs"]}
        ],
        [
            {"name":"milk", "tags":["milk", "lactose", "casein"],
            {"name":"lactose free milk substitute", "tags":[]}
        ],
        [
            {"name":"eggs", "tags":["ovalbumin"]
        ]
    ]
}

ingredients список всех участвующих ингредиентов, средний массив - отдельные списки возможных заменителей для каждого ингредиента, а внутренний массив tags быть тегами, содержащими дополнительную информацию.

В этом примере, скажем, пользователь, имеющий аллергию на глютен и лактозу, хочет запросить в базе данных рецепты, которые он может сделать. Этот документ будет возвращен, потому что и мука, и молоко имеют заменители, которые их не содержат. Если кто-то захочет отказаться от всех углеводов в своем рационе, этот документ не вернется, потому что в среднем массиве муки, не содержащем углеводов, нет выбора. То же самое касается аллергии на овальбумин.

Проблема с Cloudant Query заключается в том, что я не могу понять, как именно $not а также $elemMatch взаимодействовать друг с другом в "$elemMatch":{"$not":{"$elemMatch":{_condition_}}} структура, и я не могу видеть, как я реализовал бы представление / уменьшение, которое позволило бы этот вид фильтрации через запрос.

Единственное "решение", которое я вижу в настоящее время, - это получить все документы или некоторое подмножество и продолжать получать больше по мере необходимости и фильтровать их на уровне приложения, что звучит очень непродуктивно.

1 ответ

Я не думаю, что это еще не все, но попробуйте посмотреть с помощью функции карты, как это на вашем примере рецептов:

function(doc) {
  doc.ingredients.map(function(ingredient) {
    ingredient.map(function(variant) {
      variant.tags.map(function(tag) {
        emit(tag);
      });
    });
  });
}

Это предоставит вам представление / индекс всех тегов. Это позволит вам найти рецепты с глютеном или углеводами, но я не думаю, что это позволит вам найти рецепты без глютена или углеводов. Но вы должны иметь возможность опираться на это, чтобы создать представление / индекс для этих сценариев отрицания.

Другие вопросы по тегам