Mongodb - find entries containing a specific array of keywords

Recently, I wanted to filter out entries containing a specific array of keywords in MongoDB, for example: I have five entries containing an array of words:

{a:[1,2]} {a:[1,3,8]} {a:[1,2,5]} {a:[3,5,1]} {a:[4,5]} 

If I enter an array [1,2,3,5] for the search, then I want to get:

 {a:[1,2]} {a:[1,2,5]} {a:[3,5,1]} 

Each of them is an auxiliary array [1,2,3,5] .

Any idea?

Please do not use the where clause (when possible). Thanks!

+7
source share
2 answers

Its easy to use in mongodb, but the more difficult part is preparing the data for the request. Let me explain that in oder

Simple part

You can use $in to find the matching elements in the array. Let's try

 db.coll.find({a:{$in:[1,2,3,5]}) 

and result

 { "_id" : ObjectId("4f37c41739ed13aa728e9efb"), "a" : [ 1, 2 ] } { "_id" : ObjectId("4f37c42439ed13aa728e9efc"), "a" : [ 1, 3, 8 ] } { "_id" : ObjectId("4f37c42c39ed13aa728e9efd"), "a" : [ 1, 2, 5 ] } { "_id" : ObjectId("4f37c43439ed13aa728e9efe"), "a" : [ 3, 5, 1 ] } { "_id" : ObjectId("4f37c43e39ed13aa728e9eff"), "a" : [ 4, 5 ] } 

ohh, this is not the result we expected. Yes, because $ is in the return element, if the corresponding matching element is found (not necessarily all).

Thus, we can fix this by passing the exact elements of the array to $ in, for example, if we want to find the elements corresponding to these exact arrays {a: [1,2]} {a: [1,2,5]} and { a: [4,5,6]}

 db.coll.find({a:{$in:[[1,2],[1,2,5],[4,5,6]]}}) 

You'll get

  { "_id" : ObjectId("4f37c41739ed13aa728e9efb"), "a" : [ 1, 2 ] } { "_id" : ObjectId("4f37c42c39ed13aa728e9efd"), "a" : [ 1, 2, 5 ] } 

Thats all

The hardest part

The real hardest part is generating the entire possible combination of your input array [1,2,3,5]. You need to find a way to get the whole combination of the original array (from your client) and pass it to $ in.

For example, this JS method will provide you with all combinations of a given array.

 var combine = function(a) { var fn = function(n, src, got, all) { if (n == 0) { if (got.length > 0) { all[all.length] = got; } return; } for (var j = 0; j < src.length; j++) { fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all); } return; } var all = []; for (var i=0; i < a.length; i++) { fn(i, a, [], all); } all.push(a); return all; } >> arr= combine([1,2,3,5]) 

will provide you

 [ [ 1 ], [ 2 ], [ 3 ], [ 5 ], [ 1, 2 ], [ 1, 3 ], [ 1, 5 ], [ 2, 3 ], [ 2, 5 ], [ 3, 5 ], [ 1, 2, 3 ], [ 1, 2, 5 ], [ 1, 3, 5 ], [ 2, 3, 5 ], [ 1, 2, 3, 5 ] ] 

and you can pass this arr to $ in to find all the macthing elements

  db.coll.find({a:{$in:arr}}) 

will provide you

 { "_id" : ObjectId("4f37c41739ed13aa728e9efb"), "a" : [ 1, 2 ] } { "_id" : ObjectId("4f37c42c39ed13aa728e9efd"), "a" : [ 1, 2, 5 ] } 

Wait !, still not returning the remaining two possible items.

Because I look at arr well, it finds only a combination. it returns [1,3,5] , but the data in the document [3,5,1] . Therefore, it is clear that $in checks the elements in the given order (strange!).

So, now you understand that it is really difficult to compare the mongodb! Request. You can modify the previous JS code to find a possible permutation for each combination and pass it to mongodb $in . This is a trick.

Since you did not mention any choice of language, it is difficult to recommend any permutation code. But you can find many different approaches in Stackoverflow or googling.

+8
source

If I understand, you want to return only objects, all values โ€‹โ€‹of property a are in the argument of the find array.

Following Travis's suggestion in the comments, you should follow these steps:

  • Define a JS function to achieve your desires (since there is no native way to do this in MongoDB);
  • Save the function to the server;
  • Use the function in $where .

If you define your function to be used only with this specific property ( a , in this case), you may need to skip step 2. However, since this may be a useful function for other properties of other documents, I have defined a more general function that should be saved on the server that AFAIK will use (I'm new to Mongo too).

Below are my tests on the mongo shell:

 <--! language: lang-js --> // step 1: defining the function for your specific search only = function(property, values) { for(var i in property) if (values.indexOf(property[i]) < 0) return false return true } // step 2: saving it on the server db.system.js.save( { _id : 'only', value : only } ) // step 3: using the function with $where db.coll.find({$where: "only(this.a, [1,2,3,5])"}) 

With 5 objects indicated in the question, you will receive:

 { "_id" : ObjectId("4f3838f85594f902212eb532"), "a" : [ 1, 2 ] } { "_id" : ObjectId("4f3839075594f902212eb534"), "a" : [ 1, 2, 5 ] } { "_id" : ObjectId("4f38390e5594f902212eb535"), "a" : [ 3, 5, 1 ] } 

The downside is performance. More details

0
source

All Articles