I am querying for finding exact array match and retrieved it successfully but when I try to find out the exact array with values in different order then it get fails.

Example

db.coll.insert({"user":"harsh","hobbies":["1","2","3"]})
db.coll.insert({"user":"kaushik","hobbies":["1","2"]})
db.coll.find({"hobbies":["1","2"]})

2nd Document Retrieved Successfully

db.coll.find({"hobbies":["2","1"]})

Showing Nothing

Please help

Solution 1

The currently accepted answer does NOT ensure an exact match on your array, just that the size is identical and that the array shares at least one item with the query array.

For example, the query

db.coll.find({ "hobbies": { "$size" : 2, "$in": [ "2", "1", "5", "hamburger" ] }  });

would still return the user kaushik in that case.

What you need to do for an exact match is to combine $size with $all, like so:

db.coll.find({ "hobbies": { "$size" : 2, "$all": [ "2", "1" ] }  });

But be aware that this can be a very expensive operation, depending on your amount and structure of data. Since MongoDB keeps the order of inserted arrays stable, you might fare better with ensuring arrays to be in a sorted order when inserting to the DB, so that you may rely on a static order when querying.

Solution 2

To match the array field exactly Mongo provides $eq operator which can be operated over an array also like a value.

db.collection.find({ "hobbies": {$eq: [ "singing", "Music" ] }});

Also $eq checks the order in which you specify the elements.

If you use below query:

db.coll.find({ "hobbies": { "$size" : 2, "$all": [ "2", "1" ] }  });

Then the exact match will not be returned. Suppose you query:

db.coll.find({ "hobbies": { "$size" : 2, "$all": [ "2", "2" ] }  });

This query will return all documents having an element 2 and has size 2 (e.g. it will also return the document having hobies :[2,1]).

Solution 3

Mongodb filter by exactly array elements without regard to order or specified order. Source: https://savecode.net/code/javascript/mongodb+filter+by+exactly+array+elements+without+regard+to+order+or+specified+order

// Insert data
db.inventory.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
   { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
   { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
   { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
   { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);

// Query 1: filter by exactly array elements without regard to order
db.inventory.find({ "tags": { "$size" : 2, "$all": [ "red", "blank" ] }  });
// result:
[
  {
    _id: ObjectId("6179333c97a0f2eeb98a6e02"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6179333c97a0f2eeb98a6e03"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6179333c97a0f2eeb98a6e05"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]

// Query 2: filter by exactly array elements in the specified order
db.inventory.find( { tags: ["blank", "red"] } )
// result:
[
  {
    _id: ObjectId("6179333c97a0f2eeb98a6e02"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6179333c97a0f2eeb98a6e05"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]

// Query 3: filter by an array that contains both the elements without regard to order or other elements in the array
db.inventory.find( { tags: { $all: ["red", "blank"] } } )
// result:
[
  {
    _id: ObjectId("6179333c97a0f2eeb98a6e02"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6179333c97a0f2eeb98a6e03"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6179333c97a0f2eeb98a6e05"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]

Solution 4

This query will find exact array with any order.

let query = {$or: [
{hobbies:{$eq:["1","2"]}},
{hobbies:{$eq:["2","1"]}}
]};

db.coll.find(query)

Solution 5

with $all we can achieve this. Query : {cast:{$all:["James J. Corbett","George Bickel"]}}

Output : cast : ["George Bickel","Emma Carus","George M. Cohan","James J. Corbett"]

Solution 6

Using aggregate this is how I got mine proficient and faster:

 db.collection.aggregate([
 {$unwind: "$array"},
 
  {
        
    $match: {
      
      "array.field" : "value"
      
    }
  },

You can then unwind it again for making it flat array and then do grouping on it.

Solution 7

This question is rather old, but I was pinged because another answer shows that the accepted answer isn't sufficient for arrays containing duplicate values, so let's fix that.

Since we have a fundamental underlying limitation with what queries are capable of doing, we need to avoid these hacky, error-prone array intersections. The best way to check if two arrays contain an identical set of values without performing an explicit count of each value is to sort both of the arrays we want to compare and then compare the sorted versions of those arrays. Since MongoDB does not support an array sort to the best of my knowledge, we will need to rely on aggregation to emulate the behavior we want:

// Note: make sure the target_hobbies array is sorted!
var target_hobbies = [1, 2];

db.coll.aggregate([
  { // Limits the initial pipeline size to only possible candidates.
    $match: {
      hobbies: {
        $size: target_hobbies.length,
        $all: target_hobbies
      }
    }
  },
  { // Split the hobbies array into individual array elements.
    $unwind: "$hobbies"
  },
  { // Sort the elements into ascending order (do 'hobbies: -1' for descending).
    $sort: {
      _id: 1,
      hobbies: 1
    }
  },
  { // Insert all of the elements back into their respective arrays.
    $group: {
      _id: "$_id",
      __MY_ROOT: { $first: "$$ROOT" }, // Aids in preserving the other fields.
      hobbies: {
        $push: "$hobbies"
      }
    }
  },
  { // Replaces the root document in the pipeline with the original stored in __MY_ROOT, with the sorted hobbies array applied on top of it.
    // Not strictly necessary, but helpful to have available if desired and much easier than a bunch of 'fieldName: {$first: "$fieldName"}' entries in our $group operation.
    $replaceRoot: {
      newRoot: {
        $mergeObjects: [
          "$__MY_ROOT",
          {
            hobbies: "$hobbies"
          }
        ]
      }
    }
  }
  { // Now that the pipeline contains documents with hobbies arrays in ascending sort order, we can simply perform an exact match using the sorted target_hobbies.
    $match: {
      hobbies: target_hobbies
    }
  }
]);

I cannot speak for the performance of this query, and it may very well cause the pipeline to become too large if there are too many initial candidate documents. If you're working with large data sets, then once again, do as the currently accepted answer states and insert array elements in sorted order. By doing so you can perform static array matches, which will be far more efficient since they can be properly indexed and will not be limited by the pipeline size limitation of the aggregation framework. But for a stopgap, this should ensure a greater level of accuracy.