Valerio
Valerio

Reputation: 3615

MongoDB search in a nested array

I have a collection structured like this (with sample data):

{
    name: "Billie Jean",
    active: false,
    ranges: [
        { sfeMin: -6.75 , sfeMax: -7 , cilMin: 0.75 , cilMax: 0.75 },
        { sfeMin: -7.25 , sfeMax: -7.5 , cilMin: 1.25 , cilMax: 1.25 },
        { sfeMin: -7.5 , sfeMax: -7.75 , cilMin: 1.5 , cilMax: 1.5 },
        { sfeMin: -7.75 , sfeMax: -8 , cilMin: 1.75 , cilMax: 1.75 },
        { sfeMin: -6.5 , sfeMax: -6.75 , cilMin: 0.5 , cilMax: 0.5 },
        { sfeMin: -7 , sfeMax: -7.25 , cilMin: 0 , cilMax: 0 }
    ]
},
{
    name: "Louis Armstrong",
    active: true,
    ranges: [
        { sfeMin: -8 ,sfeMax: 8 , cilMin: 0 , cilMax: 6 },
        { sfeMin: -8 ,sfeMax: 8 , cilMin: 0 , cilMax: 6 },
        { sfeMin: -8 ,sfeMax: 8 , cilMin: 0 , cilMax: 6 },
        { sfeMin: -8 ,sfeMax: 8 , cilMin: 0 , cilMax: 6 }
    ]
}

(this is some examples of data inserted into the collection).

What I need is to search if an item inside this collection is active and have a value, let's name it x, that falls between sfeMin and sfeMax and another value, let's name it y, that falls between cilMin and cilMax.

Having x=-8 and y=0, using this filter:

{ $and: [{ active: true }, { 'ranges.sfeMin': { $gte: -8 } }, { 'ranges.sfeMax': { $lte: -8 } }, { 'ranges.cilMin': { $gte: 0 } }, { 'ranges.cilMax': { $lte: 0 } } ]}

should not find any value.

But removing {active: false} or switching the value of all records to true for active, the query returns the collection's item with name "Billie Jean", which should not (at least in my desired behavior).

Let's work out the query step by step for the record in question:

Seems like MongoDB treat the ranges array as a single, flattened record instead of running the query in array's row by row. Considering the array as a single, big picture is OK to consider the query valid for Billie, because all values are present and correct but spanned in different array's rows.

This is not what I'm looking for.

Upvotes: 2

Views: 52

Answers (1)

GwenM
GwenM

Reputation: 1335

The problem the way you've done it is the query will NOT know that ranges filters have to be part of the same object, it will look over all object of the array regardless there are in the same object.

Your query queries for documents where the ranges array has at least one embedded document that contains the field sfeMin gte to -8 and at least one embedded document (but not necessarily the same embedded document) that contains the field sfeMax equal to -8 and so on

I haven't tested it but I think this is the way it should be done:

{
  active: true,
  ranges: { $elemMatch: { sfeMin: { $gte: -8 }, sfeMax: { $lte: -8 }, cilMin: { $gte: 0 }, cilMax: { $lte: 0 } }}
}

Upvotes: 2

Related Questions