The sequence of operators in pipeline

Hello,

I take over a code in below:

			"$lookup": bson.M{
				"from": "place",
				"let":  bson.M{"resource_id": "$resource_id"},
				"pipeline": []bson.M{
					bson.M{
						"$match": bson.M{
							"$expr": bson.M{
								"$and": []bson.M{
									bson.M{"$eq": []string{"$_id", "$$resource_id"}},
									bson.M{"$eq": []string{"$status", "active"}},
								},
							},
						},
					},
				},
				"as": "place",
			},
		},

I am quite confusing about the sequence of $match --> $expr --> $and --> $eq
I think the correct one should be: $match --> $and --> $expr --> $eq. If both of the two sequence are correct, what’s the difference on the result?

in the mean while, I want to use json to represent the stage above, so here is my code:

		{
			"$lookup": {
				"from": "place",
				"let" : {"resource_id" : "$resource_id"},
				"pipeline" : [
					{
						"$match" : {
							"$and" :[
								{
									"$expr" : {
										"$eq": ["$_id" , "$$resource_id"]
									}
								},
								{
									"$expr" : {
										"$eq": ["$status", "active"]
									}
								}
							]
						}
					}
				],
				"as" : "place"
			}
		}

is it correct?

thanks,

James

Hi,

I’m not sure if your aggregation pipeline is correct, but you should be able to verify the correctness using the shell and some test data. If the ordering of the pipeline matters, I’d recommend using an ordered type like bson.D. bson.M is a map[string]interface{} and the Go language does not guarantee that maps will have the same order each time they’re iterated.

– Divjot

1 Like

Hello Divjot,

Thanks to trigger another question and I hope it will not bother you. My understanding about bson.D and bson.M are limited, I know one is slice and the other is map. Then any other difference?

For example I have a function to create filter from json string:

func BuildFilterFromJson(jsonString string) interface{} {
    var filter bson.M //here if I can user bson.D
    if err := bson.UnmarshalExtJSON([]byte(jsonString), false, &filter); 
    err != nil {
        fmt.Println(err.Error())
        return nil
    }
    return filter
}

For a json string like: {"$and" : [{"$text":{"$search" : %q}}, {"status" : "active"}]}

The BuildFilterFromJson can build the filter successfully to be a bson.M or bson.D. The output are:

map[$and:[map[$text:map[$search:bureau]] map[status:active]]]

and

[{$and [[{$text [{$search bureau}]}] [{status active}]]}]

For a input json string, is there any cases that a only 1 can work, i.e bson.D can be created but bson.M can’t be created, or vice versa. If I use bson.D in func BuildFilterFromJson, should I define some rule or guildline for people when they writing the json string?

thanks,

James

bson.D and bson.M both represent a BSON document. Like you said, bson.D is a slice and bson.M is a map. The difference is that bson.D will maintain insertion order but bson.M will not. This is because maps in the Go language are not ordered when you iterate over them. In cases where you’re building commands (e.g. if you’re using Database.RunCommand) or doing something else where ordering matters, we recommend using bson.D or a struct that gets translated to BSON.

You should be able to switch your BuildFilterFromJson function to use bson.D without any issues. Anything that can be represented in a bson.M can be represented in a bson.D and vice versa.

– Divjot

2 Likes

Hello Divjot,

As usual, your answer is clear and helpful. Many thanks for the support!

James