0

I am beginner in MongoDB and struck at a place I am trying to fetch data from nested array but is it taking so long time as data is around 50K data, also it is not much accurate data, below is schema structure please see once -

    {
    "_id": {
      "$oid": "6001df3312ac8b33c9d26b86"
    },
    "City": "Los Angeles",
    "State":"California",
    "Details": [
      {
        "Name": "Shawn",
        "age": "55",
        "Gender": "Male", 
        "profession": " A science teacher with STEM",
        "inDate": "2021-01-15 23:12:17",
        "Cars": [
          "BMW","Ford","Opel"
        ],
        "language": "English"
      },
      {
        "Name": "Nicole",
        "age": "21",
        "Gender": "Female", 
        "profession": "Law student",
        "inDate": "2021-01-16 13:45:00",
        "Cars": [
          "Opel"
        ],
        "language": "English"
      }
    ],
    "date": "2021-01-16"
  } 

Here I am trying to filter date with date and Details.Cars like

db.getCollection('news').find({"Details.Cars":"BMW","date":"2021-01-16"}

it is returning details of other persons too which do not have cars- BMW , Only trying to display details of person like - Shawn which have BMW or special array value and date too not - Nicole, rest should not appear but is it not happening.

Any help is appreciated. :)

2 Answers 2

1

A combination of $match on the top-level fields and $filter on the array elements will do what you seek.

db.foo.aggregate([
    {$match: {"date":"2021-01-16"}}
    ,{$addFields: {"Details": {$filter: {
        input: "$Details",
        as: "zz",
        cond: { $in: ['BMW','$$zz.Cars'] }
        }}
    }}
    ,{$match: {$expr: { $gt:[{$size:"$Details"},0] } }}
]);

Notes:

  1. $unwind is overly expensive for what is needed here and it likely means "reassembling" the data shape later.
  2. We use $addFields where the new field to add (Details) already exists. This effectively means "overwrite in place" and is a common idiom when filtering an array.
  3. The second $match will eliminate docs where the date matches but not a single entry in Details.Cars is a BMW i.e. the array has been filtered down to zero length. Sometimes you want to know this info so if this is the case, do not add the final $match.
  4. I recommend you look into using real dates i.e. ISODate instead of strings so that you can easily take advantage of MongoDB date math and date formatting functions.
Sign up to request clarification or add additional context in comments.

1 Comment

@Jeany As a new contributor, remember as the question owner to hit both the accepted answer check mark and the up-arrow to give it a plus one.
0

Is a common mistake think that find({nested.array:value}) will return only the nested object but actually, this query return the whole object which has a nested object with desired value.

The query is returning the whole document where value BMW exists in the array Details.Cars. So, Nicole is returned too.

To solve this problem:

To get multiple elements that match the criteria you can do an aggregation stage using $unwind to separate the different objects into array and match by the criteria you want.

db.collection.aggregate([
  {
    "$match": { "Details.Cars": "BMW", "date": "2021-01-26" }
  },
  {
    "$unwind": "$Details"
  },
  {
    "$match": { "Details.Cars": "BMW" }
  }
])

This query first match by the criteria to avoid $unwind over all collection.

Then $unwind to get every document and $match again to get only the documents you want.

Example here


To get only one element (for example, if you match by _id and its unique) you can use $elemMatch in this way:

db.collection.find({
  "Details.Cars": "BMW",
  "date": "2021-01-16"
},
{
  "Details": {
    "$elemMatch": {
      "Cars": "BMW"
    }
  }
})

Example here

You can use $elemenMatch into query or projection stage. Docs here and here

Using $elemMatch into query the way is this:

db.collection.find({
  "Details": {
    "$elemMatch": {
      "Cars": "BMW"
    }
  },
  "date": "2021-01-16"
},
{
  "Details.$": 1
})

Example here

The result is the same. In the second case you are using positional operator to return, as docs says:

The first element that matches the query condition on the array.

That is, the first element where "Cars": "BMW".

You can choose the way you want.

4 Comments

but it only showing result for 1st matching array not for all arrays in newsSources, what if there are more than 5 same records, Please check here once - Check-Here
Then you can use $unwind into an aggregation query. Check if this example is correct and I updated the answer to explain it.
Thank you It works like a charm :) but what if there is another city and I have to show data in cities categories like all data of los angeles in one array and another city data in another one separately - this - link because in your method it will separates all the array and showing one by one
If I've understood correctly I think you need $group to get data group by city name.

Your Answer

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