Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB: what is the difference between $elemMatch and $and to find objects inside array?

Is there any logical difference between the usage of the query operator $and

db.collection.find({$and: [{"array.field1": "someValue"}, {"array.field2": 3}]})

and the usage of the projection operator $elemMatch

db.collection.find({array: {$elemMatch: {field1: "someValue", field2: 3}}})

to find documents which contain the object fields inside an array?

like image 241
Simon Thiel Avatar asked Jan 26 '23 17:01

Simon Thiel


2 Answers

I will explain this with an example. Consider the collection arrays. It has a field called arr which is an array of embedded documents (with fields a and b).

Some documents in the arrays collection:

{ "_id" : 1, "arr" : [ { "a" : "a1", "b" : "b1" }, { "a" : "a2", "b" : "b2" } ] }
{ "_id" : 2, "arr" : [ { "a" : "a1", "b" : "b11" }, { "a" : "a2", "b" : "b22" } ] }
{ "_id" : 3, "arr" : [ { "a" : "a2", "b" : "b1" }, { "a" : "a", "b" : "b1" } ] }
{ "_id" : 4, "arr" : [ { "a" : "a1", "b" : "b91" }, { "a" : "a29", "b" : "b1" } ] }

I want to find all documents with the array embedded-document fields a="a1" AND b="b1". Note this must be within the same element embedded-document of the array. I use $elemMatch for this and get the desired result.

> db.arrays.find( { arr: { $elemMatch: { a: "a1", b: "b1" } } } )
==>
{ "_id" : 1, "arr" : [ { "a" : "a1", "b" : "b1" }, { "a" : "a2", "b" : "b2" } ] }

Now, if I use the $and operator like in the following query, the results are not correct. As you can see an additional document is selected. The query worked with the array embedded-document fields a="a1" OR b="b1".

> db.arrays.find({$and: [ { "arr.a": "a1" }, { "arr.b": "b1" } ] } )
==>
{ "_id" : 1, "arr" : [ { "a" : "a1", "b" : "b1" }, { "a" : "a2", "b" : "b2" } ] }
{ "_id" : 4, "arr" : [ { "a" : "a1", "b" : "b91" }, { "a" : "a29", "b" : "b1" } ] }

So, using the $and operator is NOT intended for this purpose (i.e., querying on multiple fields of an array of sub-documents).

Also, to query on an array embedded-document field (only one field) the $elemMatch is not required, for example:

> db.arrays.find( { "arr.a": "a2" } )
==>
{ "_id" : 1, "arr" : [ { "a" : "a1", "b" : "b1" }, { "a" : "a2", "b" : "b2" } ] }
{ "_id" : 2, "arr" : [ { "a" : "a1", "b" : "b11" }, { "a" : "a2", "b" : "b22" } ] }
{ "_id" : 3, "arr" : [ { "a" : "a2", "b" : "b1" }, { "a" : "a", "b" : "b1" } ] }
like image 126
prasad_ Avatar answered Jan 29 '23 07:01

prasad_


Your first query will find documents, where array have at least one element with field1= somevalue and at least one element with field2=3. Both elements can be different. The second one will retrieve documents where array have at least one element matching the two conditions simultaneously. Here's a data sample to explain :

   {
    array: [
      {
        field1: 1,   
      },
      {
        field2: 2
      },
      {
        field1: 1,
        field2: 3
      },
    ]
  },
  {
    array: [
      {
        field1: 1,
        field2: 2
      },
      {
        field2: 3
      }
    ]
  },
  {
    array: [
      {
        field1: 1,
        field2: "other"
      },
      {
        field2: 2
      }
    ]
  }

The first query

db.collection.find({"array.field1": 1,"array.field2": 2}) (equivalent to your $and syntax)

will returne the three documents,

db.collection.find({array: {$elemMatch: {field1: 1, field2: 2}}})

will return only the second document (the only one having an element matching both criterias)

EDIT : The logical operator of the first query is OR, for the second one it's AND, at level of array element.

like image 33
matthPen Avatar answered Jan 29 '23 06:01

matthPen