toonvanstrijp
toonvanstrijp

Reputation: 417

Mongoose aggregate multiple collections with match and filtering

I've three mongoose models

When the user likes something it get saved in the Liked collection. But what I want to do is. Make a query that gives 10 available product the user hasn't liked before and is not his own product. I can't figure out how to make this possible in one query. Would it be possible?

User:

{
    password: String,
    type: {
        type: String,
        enum: ['user', 'admin'],
        required: true,
        default: "user"
    },
    firstName: {type: String},
    middleName: {type: String},
    lastName: {type: String},
    gender: {type: String},
    profilePicture: {type: Object},
    setOwnProfilePicture: {type: Boolean, default: false},
    facebook:{
        id: {type: String},
        token: {type: String}
    }
}

Product:

{
    condition: {
        type: String,
        enum: ['Als nieuw', 'Goed', 'Redelijk', 'Matig'],
        required: true,
        default: "Goed"
    },
    type: {
        type: String,
        enum: ['Ruilen', 'Doneren'],
        required: true,
        default: "Ruilen"
    },
    zipCode: {type: String},
    houseNumber: {type: String},
    distance: {type: Number},
    likes: {type: Number, default: 0},
    dislikes: {type: Number, default: 0},
    title: {type: String},
    description: {type: String},
    owner: {type: Schema.ObjectId},
    images: { type : Array , "default" : [] }
}

Liked:

{
    ownerProductID: {type: Schema.ObjectId},
    productID: {type: Schema.ObjectId},
    liked: Boolean
}

UPDATE: I find out what query to use, but in the result I have a field called liked but I want to filter this out, so I don't get any extra values in my result.

The query I use now:

db.products.aggregate(

    // Pipeline
    [
        // Stage 1
        {
            $match: {'owner': {$ne: ObjectId("583f2f33ee975f4e8560b9fe")}}
        },

        // Stage 2
        {
            $lookup: {
                "from" : "likes",
                "localField" : "_id",
                "foreignField" : "productID",
                "as" : "liked"
            }
        },

        // Stage 3
        {
            $match: {'liked.ownerProduct': {$nin: [ObjectId("585834609bb1aa1cbe98257b")]}}
        },

        // Stage 4
        {
            $limit: 10
        },

    ]

);

Upvotes: 0

Views: 3196

Answers (2)

toonvanstrijp
toonvanstrijp

Reputation: 417

I managed to get the result I wanted. By using this query:

db.products.aggregate(

    [
        {
            $match: {'owner': {$ne: ObjectId("583f2f33ee975f4e8560b9fe")}}
        },
        {
            $lookup: {
                "from" : "likes",
                "localField" : "_id",
                "foreignField" : "productID",
                "as" : "liked"
            }
        },
        {
            $match: {'liked.ownerProduct': {$nin: [ObjectId("585834609bb1aa1cbe98257b")]}}
        },
        {
            $limit: 10
        },
        {
            $project: {
                _id: 1,
                owner: 1,
                zipCode: 1,
                title: 1,
                description: 1,
                houseNumber: 1,
                images: 1,
                dislikes: 1,
                likes: 1,
                type: 1,
                condition: 1
            }
        },
    ]
);

Upvotes: 1

sidgate
sidgate

Reputation: 15244

Use aggregate to filter on multiple levels.

Assuming you have the USER_OBJECTID, first filter products on owner, then join the collection with Liked and filter again with user ID.

db.Product.aggregate([
    {$match: {'owner': {$ne: '<USER_OBJECTID>'}}},
    {$lookup: {
        from: 'liked',
        localField: '_id',
        foreignField: 'productID',
        as: 'likes'
        }
    },
    {$match: {'likes.userID': {$nin: [<USER_OBJECTID>]}}},
    {$limit: 10}
])

Upvotes: 2

Related Questions