Azima
Azima

Reputation: 4151

nodejs mongodb check a range against a single value

I have some documents as following in db:

User A:
{
    "_id" : ObjectId("5f1ec1869ea3e213cc2a159e"),
    "age": 27,
    "gender": "male",
    "preference": {
        "ageGroup": "25-35",
        "gender": "female"
    }
}

User X:
{
    "_id" : ObjectId("378ec1869ea3e212333a159e"),
    "age": 27,
    "gender": "female",
    "preference": {
        "ageGroup": "20-30",
        "gender": "male"
    }
}

I am trying to filter docs based on :

Here's what I am trying:

const getGsaMatches = async function (currentUser) {
const user: any = await User.findOne({ _id: currentUser._id });
const userPreference = user.preference;

const ageRange = userPreference.ageGroup.split("-");
const minAgeRange = ageRange[0];
const maxAgeRange = ageRange[1];

const matchResponse: any = await User.find({       
    "gender": userPreference.gender,
    "age": { $gte: minAgeRange, $lte: maxAgeRange },

    "preference.gender": user.gender,
    "preference.ageGroup": user.age, // I'm stuck here
    _id: { $ne: currentUser._id }
});
return matchResponse;

}

preference.ageGroup contains value in 25-30 string format.

How can I store this field so that it can be compared against a given single integer value?

I hope I made the problem clear.

Upvotes: 1

Views: 111

Answers (1)

Tom Slabbaert
Tom Slabbaert

Reputation: 22296

A good way to start is to actually store it as an Integer. If you're using Mongo v4.0+ you can also use $toInt but if this is a query you're doing often then you might aswell save it in a structure like:

ageGroup: {
   min: 20,
   max: 30,
   value: "20-30"
}

Now you can do something like this:

const matchResponse: any = await User.find({
    _id: { $ne: currentUser._id },
    "gender": userPreference.gender,
    "age": { $gte: minAgeRange, $lte: maxAgeRange },

    "preference.gender": user.gender,
    "preference.ageGroup.min":  {$lte: user.age},
    "preference.ageGroup.max":  {$gte: user.age}
});

Assuming you don't want to change the structure then you'll have to use an aggregation with $toInt as I suggested like so:

const matchResponse: any = await User.aggregate([
    {
        $addFields: {
            tmpAgeField: {
                $map: {
                    input: {$split: ["$preference.ageGroup", "-"]},
                    as: "age",
                    in: {$toInt: "$$age"}
                }
            }
        }
    },
    {
        $match: {
            _id: { $ne: currentUser._id },
            "gender": userPreference.gender,
            "age": { $gte: minAgeRange, $lte: maxAgeRange },
            "preference.gender": user.gender,
            "tmpAgeField.0":  {$lte: user.age},
            "tmpAgeField.1":  {$gte: user.age}
        }
    }
]);

Upvotes: 1

Related Questions