pidgey
pidgey

Reputation: 245

mongo find documents where at least one item from an array is not in the other

I have this problem in mongo where I would like to make a query (regular mongo query, using the terminal) to find all the documents where a field is either empty, or has at least one value which does not match an array of regexes.

My documents have the following pattern:

{
  "id": "20",
  "array": ["abc123", "abc456", "def123", "ghi123"]
}

and

{
  "id": "21",
  "array": ["abc123", "abc456", "def123"]
}

My array to match them with would look like something like this:

[new RegExp(abc), new RegExp(def)]

The result of the query should be only the first document, as this one contains "ghi123", which doesn't match against any regex in the array.

I know there are operators like $all, $elementMatch, $in etc to build this query, but so far, I haven't found the right solution, and I've been stuck for quite a while... I found other StackOverflow pages that I found useful and might lead to the right answer, but I haven't been able to apply it to my problem yet. This one seemed the most useful:

Check if every element in array matches condition

Does anyone have an idea on how to fix this?

Upvotes: 2

Views: 1454

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151190

This is really just a regex problem as all the logic can be handled there:

db.collection.find({ "array": /^(?!abc|def)/ })

This is just basically a negative assertion that the element being tested matches either condition separated by | which means or.

Since the first document is the only one that has an array element that would not meet the condition then it is the only case that will evaluate true here.

Regular expressions are generally constructed from a base "string" so considering the following representation of a "list" this should not be hard to follow:

var args = ["abc","def"];

var exp = "^(?!" + args.join("|") + ")";
db.collection.find({ "array": new Regexp(exp) });

Or even:

var args = ["abc","def"];
var inclause = { "$in": [ ] };

inclause["$in"] = args.map(function(arg) {
    return new Regexp( "^(?" + arg + ")" );
});

db.collection.find({ "array": inclause });

But the first form should generally be more efficient.

Upvotes: 2

Related Questions