I'm trying to come up with a query to filter results from a data structure of three nested arrays of strings. E.g.:
{"field":
[
[
["str1", "str2"],
["str3", "str4"]
],
[
["str5", "str6"],
["str7", "str8", "str9"]
],
[
["str10", "str11", "str12"]
]
]
}
For a doc to be valid all of the middle arrays need to have at least one inner array that passes a filter to check if certain strings are not contained in it.
Filtering str1 would make the document valid, as ["str3", str4"] would still validate the middle array of it. Filtering for str1 and str3 would have the first middle array not pass and not return the doc.
What I could come up with, which sadly only seems to work in my head, is
{"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
}}}}}}}}
This however doesn't return any docs.
Testing for smaller cases, results are as expected while dealing with a single array.
For {"field":["str1", "str2", "str3"]}, {"selector":{"$elemMatch":{"$in":["str1"]}}} returns the doc. {"selector":{"$not":{"$elemMatch":{"$in":["str1"]}}}} doesn't.
For one level of nested arrays things broke down. For
{"field":[["str1", "str2"],["str3", "str4"]]}
the selector
{"selector":{
"fields":{
"$elemMatch":{
"$elemMatch":{"$in":["str1"]
}}}}}
returns the doc. But
{"selector":{
"fields":{
"$elemMatch":{
"$not":{
"$elemMatch":{"$in":["str1"]
}}}}}}
doesn't. The way I figure, "$elemMatch":{"$in":["str1"] would pass ["str1", "str2"] and fail ["str3", "str4"]. Negating that has ["str1", "str2"] failing and ["str3", "str4"] passing. The elemMatch would then pass and return the doc because ["str3", "str4"] passes.
What about $elemMatch, $not, or their interaction goes against what I understood of them? Is there a more ideal way to go about creating this query?
I'm fairly new to Cloudant, and to NoSQL in general, and have been guiding myself through Cloudant's, CouchDB's and MongoDB's documentation and google searches, but for this issue I'm drawing blanks.
Thanks in advance.
Edit(23/02/17): A more concrete example
Those docs will represent recipes and their ingredients, as well as tags representing more information about the ingredients such as possible allergy triggers for users, or things they might want to avoid or seek in general.
{"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 being a list of all ingredients involved, the middle array being individual lists for possible substitutes for each ingredient, and the inner array tags being the tags holding the extra info.
In this example, say an user allergic to gluten and lactose wants to query the database for recipes he can make. This doc would be returned, because both flour and milk have substitutes that don't contain those. If someone would like to cut out all carbs on their diet this doc wouldn't be returned, because there's no option in the flour middle array that doesn't contain carbs. The same goes for someone allergic to ovalbumin.
The problem I'm having with Cloudant Query is that I can't seem to figure out how exactly $not and $elemMatch interact with each other in a "$elemMatch":{"$not":{"$elemMatch":{_condition_}}} structure, nor can I see how I'd implement a view/reduce that would allow this kind of filtering through a query.
The only "solution" I can currently see is to get all the docs, or some subset and keep getting more as needed, and filter them on application level, which sounds very unproductive.
"str1","str2", etc.