错误地编写了MongoDB聚合管道的$match阶段。

huangapple go评论81阅读模式
英文:

incorrectly drafted MongoDB aggregation pipeline $match stage

问题

我正在尝试使用go.mongodb.org/mongo-driver/mongo库在golang语言中进行查询(下面我附上了纯mongodb查询的工作代码),以下是golang查询代码。我无法正确使用matchStage,我尝试了许多变体,我确定我只是非常粗心或者根本不理解。

如何同时使用$match$expr$and$lte来创建一个正确的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
}

错误:(InvalidPipelineOperator) Unrecognized expression '$create_date'

MongoDB playground链接

英文:

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

答案1

得分: 2

$lte的值必须是一个数组而不是一个文档:

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
						}},
					},
				}},
			}},
		},
	},
}

另外请注意,你可以从复合字面量中省略primitive.E类型:

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
						}},
					},
				}},
			}},
		},
	},
}

但请注意,你在Mongo Playground上的表达式是不正确的。引用自文档

$match接受一个指定查询条件的文档。查询语法与读操作查询语法相同...

在使用$expr时,你必须使用$eq,例如:

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
						}},
					},
				}},
			}},
		},
	},
}

在这里尝试:https://mongoplayground.net/p/SBEJD-Fyhjl

你应该在$match中使用一个普通的查询文档。

看看这个等效的、更简单的解决方案:

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
				}},
			},
		},
	},
}

如果你使用bson.M而不是bson.D,甚至更简单:

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

当然,在这种情况下,你不能使用mongo.Pipeline作为管道,但[]any[]bson.M也可以。

英文:

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 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.

huangapple
  • 本文由 发表于 2023年2月3日 04:37:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75328672.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定