简体   繁体   中英

Mongoengine aggregate doesn't work like mongo

I have a Mongo Document with an Embedded document list inside(example below). I'm trying to run an aggregation that will return the full document with only the matched objects from within the list.

Example Collection Data

{
    "make": "Toyota",
    "color": "blue",
    "tires": [{
        "make": "Mishlen",
        "size": 185
    }, {
        "make": "Mishlen",
        "size": 210
    }]
}

I managed to make it work in MongoDB with the following query

db.cars.aggregate(
    [
        {
            $match: {$and: [{"tires.size": {$gt: 200}}]}
        },
        {
            $addFields: {
                "tires": {
                    $filter: {
                        input: '$tires',
                        as: 'tires',
                        cond: {$gt: ['$$tires.size', 200]}
                    }
                }
            }
        },
        {
            $limit: 100
        },
        {
            $skip: 0
        }
])

I'm trying to run the same aggregation in mongoengine and it's returning an empty list everytime.

pipeline = [
    {
        "$match": {"$and": [{"tires.size": {"$gt": 200}}]}
    },
    {
        "$addFields": {
            "tires": {
                "$filter": {
                    "input": "$tires",
                    "as": "tires",
                    "cond": {"$and": [{"$gt": ["$$tires.size", 200]}]}
                }
            }
        }
    }
]
self.obj_type.objects.aggregate(*pipeline)

What am I doing wrong?

UPDATE

My problem was much easier than I thought, I missed that I was passing the numbers as a string instead of int in python. Thank everyone for the help

Below workflow would be the most simple

 pipeline = [
            {
        $unwind: "$tires"
            },
            {
                "$match": {"$and": [{"tires.size": {"$gt": 200}}]}
            },
            {
         $group : { 
                   _id : "$make", 
                   tires: { $push: "$tires" } 
                  }
            } 

        ]
        self.obj_type.objects.aggregate(*pipeline) 

After inserting the sample document that you provided, I don't have any particular problem to run the aggregation with the pipeline you provided. See below:

class Tire(EmbeddedDocument):
    make = StringField()
    size = IntField()

class Car(Document):
    make = StringField()
    color = StringField()
    tires = EmbeddedDocumentListField(Tire)

    meta = {'collection': 'cars'}

pipeline = [
    {
        "$match": {"$and": [{"tires.size": {"$gt": 200}}]}
    },
    {
        "$addFields": {
            "tires": {
                "$filter": {
                    "input": "$tires",
                    "as": "tires",
                    "cond": {"$and": [{"$gt": ["$$tires.size", 200]}]}
                }
            }
        }
    }
]

# Verify aggregation pipeline runs fine with the driver (pymongo)
coll = Car._get_collection()
pymongo_result = list(coll.aggregate(pipeline))
assert len(pymongo_result) == 1

# Run same aggregation pipeline with MongoEngine
mongoengine_result = list(Car.objects.aggregate(*pipeline))
assert len(mongoengine_result) == 1

result = [
    {'_id': ObjectId('5e0a5c8e7c57cd9b300710fb'),
     'color': 'blue',
     'make': 'Toyota',
     'tires': [{'make': 'Mishlen', 'size': 210.0}]
    }
]
assert mongoengine_result == pymongo_result == result

I've used the latest mongoengine version but AFAIK there was no major change in the aggregation wrapper of MongoEngine lately. It is possible that the problem resides in self.obj_type.objects on your end, try from a fresh queryset like I do (ie YourDocumentClass.objects ) to see if it makes a difference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM