Reputation: 43
I've built a grammar (using peg.js) to support simple search queries in a Node.js web-application; I need to translate the AST it produces into a $match
parameter for a MongoDB aggregate function. Most cases are fine, but I'm having trouble figuring out what NOT (A AND B)
should look like in MongoDB.
Some example data:
{ _id:123, labels:['a', 'b', 'c'], tags:['g', 'h', 'i']},
{ _id:456, labels:['a', 'e', 'f'], tags:['i', 'j', 'k']},
{ _id:789, labels:['c', 'd', 'b'], tags:['h', 'l', 'm']}
A user might type in the query NOT (labels:a AND labels:b)
, or NOT (labels:b AND tags:h)
which are both valid queries, based on my grammar.
The intent for the first query would be to bring back all documents that do not contain both 'a' and 'b' in their labels
field (so the second and third document would be returned); for the second query, it should match all documents that do not contain both 'b' in the labels
field, and 'h' in the tags
field.
MongoDB's $not
operator doesn't seem to allow you to neatly negate a logical operation in the way I'm looking for; ideally, I'd like to do something like:
{
$not: {
$and: [
{ labels: a},
{ labels: b},
]
}
}
or
{
$not: {
$and: [
{ labels: b},
{ tags: h},
]
}
}
Is there some other operator I can use to do this? Also, ideally, this would be something easy to build programatically - so that I could build a potentially complex compound query inside the parentheses, and then just negate it if there was a NOT
.
EDIT: Expanded the dataset, and expanded and clarified my actual requirements for an answer.
Upvotes: 4
Views: 248
Reputation: 123
For one field (as per the original question) this should do it:
{
'labels': {
'$not': {
'$all': [
'a',
'b',
]
}
}
}
For multiple fields, you'd need to break them out but this query would work:
{
'$and': [
{
'labels': {
'$not': {
'$all': ['b'],
},
},
},
{
'tags': {
'$not': {
'$all': ['h'],
}
}
}
]
}
I don't think it's possible to have the not as a main root element
Upvotes: 1