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

917 阅读2分钟

[toc]

问题描述

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

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

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


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 :

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

我预期的结果

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

 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 是相同。

期望返回的文档结构:

  {
    _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(聚合)比较不同于查询eqopeator的值和类型,后者可以比较任何类型的值。需要eq (聚合)比较不同于查询 eq opeator 的值和类型,后者可以比较任何类型的值。需要 in (聚合)来验证数组中的值

于是我尝试做了下面更改

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 字段可能为空

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

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

🤔思考

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