mongoengine icon indicating copy to clipboard operation
mongoengine copied to clipboard

How to get all matched documents instead of the First Matching document.

Open haris1998-web opened this issue 2 years ago • 2 comments

Document Structure

Data

class Data(EmbeddedDocument):
    v = FloatField()
    q = StringField()
    co2 = FloatField()
    price = FloatField()
    ts = DateTimeField()

Meters Data

class MetersData(DynamicDocument):
    meta = {'collection': 'dk_heating'}
    _id = ObjectIdField()
    ident = StringField()
    meteringPointId = StringField()
    customer = StringField()
    cvr = StringField()
    type = StringField()
    unit = StringField()
    address = StringField()
    period = EmbeddedDocumentField(Period)
    hourly_data = ListField(EmbeddedDocumentField(Data), db_field='data')
    daily_data = ListField(EmbeddedDocumentField(Data))
    monthly_data = ListField(EmbeddedDocumentField(Data))
    # monthly_data = EmbeddedDocumentListField(Data)
    yearly_data = ListField(EmbeddedDocumentField(Data))

I am using this Query.

Query

MetersData.objects.filter(address=address, customer=customer).fields(
                monthly_data={"$elemMatch": {"q": "E"}},
                address=1, customer=1, cvr=1, ident=1, meteringPointId=1, type=1, unit=1, period=1)

It returns me only the first matching element. I have read the documentation and it reads that $elemMatch is supposed to return only the first matching result. But in my case, I need all the matching results.

Result of the Query

image

I have searched everywhere but I am unable to find a solution.

haris1998-web avatar Jun 09 '22 15:06 haris1998-web

If I understand correctly, your problem is that the result of the query returns only the monthlyData element that are matched.

When you use $elemMatch in the .fields() of MetersData.objects(...).fields(), it gets use as a projection operator (as described here in the MongoDB doc). So the behavior that you see is expected and it only returns what's matched.

In your case you need to use it as a filter, thus in the .objects() or .filters(). This can be done with the elemMatch operator as in: MetersData.objects(monthly_data__elemMatch={"q": "E"}).

or with a raw query with __raw__ , as in MetersData.objects(__raw__={"monthly_data":{"$elemMatch": {"q": "E"}}}).

bagerard avatar Jul 17 '22 20:07 bagerard

Let's keep this ticket opened as the doc could be improved

bagerard avatar Jul 17 '22 22:07 bagerard

After a second thought, I'm not sure how to improve the doc and I think it's okay as it is because it specifies that it's the ``elemMatch projection operator

bagerard avatar Mar 01 '23 21:03 bagerard