mongodb 查询嵌入式文档数组比较某项字段的值

本文最后更新于: 2022年10月10日 下午

[toc]

问题描述

现在我有一个这样的 mongodb 文档模型 Article

author、group 这两个字段分别左外部联接 UserArticleGroup两个集合

我使用的是 mongoose 对象文档模型(ODM)库,集合名称都是大写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

const ArticleSchema = new mongoose.Schema({
title: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
group: { type: mongoose.Schema.Types.ObjectId, ref: "ArticleGroup" },
});
// ArticleGroup
const ArticleGroupSchema = new mongoose.Schema({
name: { type: String, required: true },
desc: { type: String, default: "" },
state: { type: String, required: true, default: "public" },
members: [
{
desc: String,
role: String,
user: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
},
],
});
// User
const userSchema = new mongoose.Schema(
{
email: { type: String, unique: true },
password: String,
},
);

test data :

1
2
3
4
5
6
{
_id: "6131aa5b367318e2df14b988",
title: "功能更新清单",
author: ObjectId("607edeb4b1e1bea9acb5af38"),
group: ObjectId("612d00a43c52975d4ade10d4")
}

我预期的结果

我想在 aggregate 聚合操作内通过比较 author._id == group.members.user 的值,向文档添加一个新字段 can_edit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 Article.aggregate([
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "author",
},
},
{
$lookup: {
from: "articlegroups",
localField: "group",
foreignField: "_id",
as: "group"
}
},
{
$addFields: {
can_edit: {
$eq: ["$group.members.user", "$author._id"],
},
}
}
])

可是我得到的 can_edit 值始终是 false,我确定在我的测试数据中 author._id 的值和group.members.user 是相同。

期望返回的文档结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
_id: "6131aa5b367318e2df14b988",
can_edit: true,
title: "功能更新清单",
author: {
_id: "607edeb4b1e1bea9acb5af38",
email: "frmachao@126.com",

},
group: {
_id: "612d00a43c52975d4ade10d4",
desc: "开发",
state: "public",
name: "开发小组",
members: [
{
_id: "612d00a43c52975d4ade10d5",
role: "admin",
user: "607edeb4b1e1bea9acb5af38",

}
]
}
}

刚刚,我找到一个相似的问题 stackoverflow。上面提到: $eq (聚合)比较不同于查询 eq opeator 的值和类型,后者可以比较任何类型的值。需要 $in (聚合)来验证数组中的值

于是我尝试做了下面更改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Article.aggregate([
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "author",
},
},
{
$lookup: {
from: "articlegroups",
localField: "group",
foreignField: "_id",
as: "group"
}
},
{
$addFields: {
can_edit: {
$in: [
"$author._id",
"$group.members.user"
],
},
},
}
])

但是遇到了新问题: $in requires an array as a second argument

在挣扎了100分钟: 尝试用搜索引擎检索我遇到的问题、去翻阅$eq聚合阶段的文档、咨询微信里做过服务端开发的网友

可是问题还是没有解决,最后我决定去社区提问。

去社区提问

  1. 国内 segmentfault
  2. 国外的 stackoverflow

    mongoplayground

stackoverflow 上面,一位热心的马来西亚程序员帮我解决了问题:
他告诉我 应该 group.members must be array checking,也就是先要判断group.members 是不是一个数组。

分析:在我定义的数据模型里,group 并不是一个必填项,所以 group 字段可能为空

1
2
3
4
5
const ArticleSchema = new mongoose.Schema({
title: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
group: { type: mongoose.Schema.Types.ObjectId, ref: "ArticleGroup" },
});

热情的马来西亚程序员 甚至给出了完整的 判断 group.members must be array 的样例,附上调试地址,respect!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
db.collection.aggregate([
{
$addFields: {
can_edit: {
$switch: {
branches: [
{
case: {
$ne: [
{
"$type": "$group.members"
},
"array"
]
},
then: false
},
{
case: {
$ne: [
{
"$type": "$group.members"
},
"array"
]
},
then: false
},
{
case: {
$in: [
"$author._id",
"$group.members.user"
]
},
then: true
}
],
default: false
}
}
}
}
])

收获

  1. mongoplayground一个网站 可以在线调试 mongoDB的查询
  2. $eq (聚合)比较不同于查询 eq opeator 的值和类型,后者可以比较任何类型的值。需要 $in (聚合)来验证数组中的值。
  3. 去社区提问需要注意:描述清楚自己的问题,不要带上自己的业务细节把问题剥离出来,最好能提供一个在线运行的playground

🤔思考

  1. 自学能力差: 为什么我翻看文档时没有看到 $eq (聚合)比较不同于查询 eq opeator 的值和类型,后者可以比较任何类型的值。需要 $in (聚合)来验证数组中的值
  2. 英语很差:stackoverflow 上编写的提问描述全部来自 google 翻译 中–》英