Craig Malton
Craig Malton

Reputation: 84

How can I write a Mongoose find query that uses another field as it's conditional?

Consider the following:

I have a Mongoose model called 'Person'. In the schema for the Person mode, each Person has two fields: 'children' and 'maximum_children'. Both fields are of type Number.

I would like to write a find query that returns Persons when that Persons 'children' value is less that it's 'maximum_children' value.

I have tried:

person_model.find({
    children: {
        $lt: maximum_children
    }
}, function (error, persons) {
    // DO SOMETHING ELSE
});

and

person_model.find({
    children: {
        $lt: 'maximum_children'
    }
}, function (error, persons) {
    // DO SOMETHING ELSE
});

I'm doing something wrong in trying to specify the field name that I want to compare 'children' against.

Upvotes: 1

Views: 610

Answers (2)

JohnnyHK
JohnnyHK

Reputation: 312115

$where must execute its JavaScript conditional against every doc so its performance can be quite poor. Instead, you can use aggregate to include a new field in a $project stage the indicates whether the doc matches or not and then filter on that:

person_model.aggregate([
    {$project: {
        isMatch: {$lt: ['$children', '$maximum_children']},
        doc: '$$ROOT'
    }},
    {$match: {isMatch: true}},
    {$project: {_id: 0, doc: 1}}
], function(err, results) {...});

This uses $$ROOT to include the original doc as the doc field of the projection, with a final $project used to remove the isMatch field that was added.

results looks like:

{
    "doc" : {
        "_id" : ObjectId("54d04591257efd80c6965ada"),
        "children" : 5,
        "maximum_children" : 10
    }
}, 
{
    "doc" : {
        "_id" : ObjectId("54d04591257efd80c6965add"),
        "children" : 5,
        "maximum_children" : 6
    }
}

If you want to remove the added doc level of the objects you can use Array#map on results like so:

results = results.map(function(item) { return item.doc; });

Which reshapes results to put them back into their original form:

{
    "_id" : ObjectId("54d04591257efd80c6965ada"),
    "children" : 5,
    "maximum_children" : 10
}, 
{
    "_id" : ObjectId("54d04591257efd80c6965add"),
    "children" : 5,
    "maximum_children" : 6
}

Upvotes: 0

Craig Malton
Craig Malton

Reputation: 84

OK.

I found a solution, just after I posted this question.

The answer seems to be:

person_model.find({
    $where: "children < maximum_children"}, function (error, persons)
}, {
    // DO SOMETHING ELSE
});

Seems to work OK, although it seems messy.

Upvotes: 1

Related Questions