Home > database >  incorrectly drafted MongoDB aggregation pipeline $match stage
incorrectly drafted MongoDB aggregation pipeline $match stage

Time:02-03

I'm trying to make a query in golang language (below I've attached working code of pure mongodb query) using go.mongodb.org/mongo-driver/mongo library, below is golang query code. I can't get matchStage to work correctly, I've tried many variants and I'm sure I'm just very inattentive or just don't understand

How can I use $match, $expr, $and and $lte at once to make a correct matchStage?

func (r *Mongo) ChatHistory(ctx context.Context, chatID string, f *Filter) ([]*Message, error) {
    matchStage := bson.D{
        primitive.E{
            Key: "$match",
            Value: bson.D{
                primitive.E{Key: "$expr", Value: bson.D{
                    primitive.E{Key: "$and", Value: bson.A{
                        bson.D{
                            primitive.E{Key: "$lte", Value: bson.D{
                                primitive.E{
                                    Key:   "$create_date",
                                    Value: f.Date, // int64
                                },
                            }},
                        },
                    }},
                }},
            },
        },
    }
    sortStage := bson.D{
        {
            Key: "$sort", Value: bson.D{
                primitive.E{Key: "create_date", Value: -1},
            },
        },
    }
    limitStage := bson.D{primitive.E{Key: "$limit", Value: f.Count}}

    cursor, err := r.colMessage.Aggregate(ctx, mongo.Pipeline{matchStage, sortStage, limitStage})
    if err != nil {
        l.Error().Err(err).Msg("failed find")
        return nil, err
    }

    var res []*Message
    if err = cursor.All(ctx, &res); err != nil {
        l.Error().Err(err).Msg("failed find all documents")
        return nil, err
    }

    if err = cursor.Close(ctx); err != nil {
        l.Error().Err(err).Msg("failed close cursor")
        return nil, err
    }

    return res, nil
}

Error: (InvalidPipelineOperator) Unrecognized expression '$create_date'

MongoDB playground link

CodePudding user response:

Value of $lte must be an array not a document:

matchStage := bson.D{
    primitive.E{
        Key: "$match",
        Value: bson.D{
            primitive.E{Key: "$expr", Value: bson.D{
                primitive.E{Key: "$and", Value: bson.A{
                    bson.D{
                        primitive.E{Key: "$lte", Value: bson.A{
                            "$create_date",
                            f.Date, // int64
                        }},
                    },
                }},
            }},
        },
    },
}

Also note that you can leave out the primitive.E type from the composite literal:

matchStage := bson.D{
    {
        Key: "$match",
        Value: bson.D{
            {Key: "$expr", Value: bson.D{
                {Key: "$and", Value: bson.A{
                    bson.D{
                        {Key: "$lte", Value: bson.A{
                            "$create_date",
                            f.Date, // int64
                        }},
                    },
                }},
            }},
        },
    },
}

But note that your expression is on the mongo playground is incorrect. Quoting from the doc:

$match takes a document that specifies the query conditions. The query syntax is identical to the read operation query syntax ...

When using $expr, you have to use $eq, for example:

matchStage := bson.D{
    {
        Key: "$match",
        Value: bson.D{
            {Key: "$expr", Value: bson.D{
                {Key: "$and", Value: bson.A{
                    bson.D{
                        {Key: "$eq", Value: bson.A{
                            "$chat_id",
                            chatID,
                        }},
                    },
                    bson.D{
                        {Key: "$lte", Value: bson.A{
                            "$create_date",
                            f.Date, // int64
                        }},
                    },
                }},
            }},
        },
    },
}

Try it here: https://mongoplayground.net/p/SBEJD-Fyhjl

You should use a normal query document in $match.

See this equivalent, much simpler solution:

matchStage := bson.D{
    {
        Key: "$match",
        Value: bson.D{
            {Key: "chat_id", Value: chatID},
            {Key: "create_date", Value: bson.D{
                {
                    Key:   "$lte",
                    Value: f.Date, // int64
                }},
            },
        },
    },
}

And even much-much simpler if you use bson.M instead of bson.D:

matchStage := bson.M{
    "$match": bson.M{
        "chat_id":     chatID,
        "create_date": bson.M{"$lte": f.Date},
    },
}

Of course in this last case you can't use mongo.Pipeline for the pipeline, but []any or []bson.M would also do.

  • Related