12

I want to be able to find multiple documents that have three or more matching values in an array. Let's say we the following documents:

   [{
       name: 'John',
       cars: [1, 2, 3, 4]
   },
   {
       name: 'Jane',
       cars: [1, 2, 3, 8]
   },
   {
       name: 'Smith',
       cars: [1, 8, 10]
   }]

And we want to find documents that have at least three of the values (in cars) in the following array:

   [1, 2, 3, 4, 5, 6, 7]

The results would then be:

   [{
       name: 'John',
       cars: [1, 2, 3, 4]
   },
   {
       name: 'Jane',
       cars: [1, 2, 3, 8]
   }]

Anyone know how to achieve this?

0

4 Answers 4

16

You can have a $in query issued and then by code filter the record having 3 or more entries in the desired array. (Here is some samle python code)

def dennisQuestion():
    permissibleCars = [1,2,3,4,5,6,7]
    cursor = db.collection.find({"cars": {"$in": permissibleCars}})
    for record in cursor:
       if len(set(permissible) & set(record["cars"]))) >= 3
          yield record
Sign up to request clarification or add additional context in comments.

Comments

12

This is a good question, and I don't think there's a simple way to do it with the usual operators that MongoDB gives you. However I can think of the following methods to achieve this:

1. New Field

Calculate this in app code and maintain the result in a new field on the document.

2. Brute Force

db.Collection.find( { $or: [
    { cars: $all [ 1, 2, 3 ] },
    { cars: $all [ 2, 3, 4 ] },
    ... list out all 35 combinations
] } )

3. Use $where

db.Collection.find( { cars: { $in: [1,2,3,4,5,6,7] }, $where: function() {
    var numMatches = 0;
    for (var i = 1; i <= 7; i++)
        if (this.cars.indexOf(i) > -1) numMatches++;
    return numMatches >= 3;
} } );

Comments

0

I had to slightly modify @Zaid Masud option 3 when the values where strings in Mongo 4.0.3:

db.Collection.find( { cars: { $in: ["s1", "s2", "s3" , "s4", "s5" , "s6" , "s7"] },
    $where: function() {
        var options = ["s1", "s2", "s3" , "s4", "s5" , "s6" , "s7"];
        var numMatches = 0;
        for (var i = 0; i < 7; i++)
            if (this.cars.indexOf(options[i]) > -1) 
                numMatches++;
        return numMatches >= 3;
    } 
} );

(This seemed a bit large for a comment)

Comments

0

For Mongo v4.4.1 this query works

[
  {
    $project: {
      name: 1,
      cars: 1,
      show: {
        $let: {
          vars: {
            "b": {
              $gte: [{$size: {$setIntersection: [ [1,2,3,4,5,6,7],"$cars"]}},3]
            }
          },
          in: "$$b"
        }
      }
    }
  },
  {
    $match: {
      show: true,
      
    }
  },
  {
    $project: {
      show: 0
    }
  }
]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.