Фильтрация вложенных массивов строк с помощью 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);
});
});
});
}
Это предоставит вам представление / индекс всех тегов. Это позволит вам найти рецепты с глютеном или углеводами, но я не думаю, что это позволит вам найти рецепты без глютена или углеводов. Но вы должны иметь возможность опираться на это, чтобы создать представление / индекс для этих сценариев отрицания.