首页 > 解决方案 > 创建 Mongoose 从具有属性集的多个表中查找和填充

问题描述

对于猫鼬的帮助,我将不胜感激。我有 3 个表:(用户)用户表,(动物)动物表和表 AnimalComments。所以(AnimalComments)表参考用户和动物。

const schemaComment = new mongoose.Schema({
        userRef: {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        },
        animalRef: {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'Animal'
        },
        content: {
            type: String
        }
    });

const schemaAnimal = new mongoose.Schema({
    name: {
        type: String
    },
    isCommentedByMe: {
        type   : Boolean,
        default: false
    },
    commentCount: {
        type   : Number,
        default: 0
    }
});

我想要什么:我有动物。用户可以为动物添加评论。当用户评论动物时,他的评论被添加到表 AnimalComments 中,其中存储了 userRef (userId)、animalRef (animalId) 和用户评论文本。然后在请求响应中,我想返回表 Animals 中的所有动物,但我需要根据表 AnimalComments 中的值更新属性 commentCount 和 isCommentedByMe。

来自表动物的回应:

{
    "animals": [
        {
            "isCommentedByMe": false,
            "commentCount": 0,
            "name": "Jessica",
            "userRef": {
                "id": "5dc9bdf3dd5cae00177e184d"
            },
            "id": "5dcedd48368e9800176f2ef3"
        }
    ]
}

来自表用户的响应:

{
    "users": [
        {
            "name": "Jony Cash",
            "id": "5dc9bdf3dd5cae00177e184d"
        }
    ]
}

来自表 AnimalComments 的响应:

{
    "comments": [
        {
            "userRef": "5dc9bdf3dd5cae00177e184d",
            "animalRef": "5dcedd48368e9800176f2ef3",
             "content": "Sample text"
        }
    ]
}

我想要例如结果:

{
    "animals": [
        {
            "isCommentedByMe": true, 
            "commentCount": 4, 
            "name": "Jessica",
            "userRef": {
                "id": "5dc9bdf3dd5cae00177e184d"
            },
            "id": "5dcedd48368e9800176f2ef3"
        }
    ]
}

标签: node.jsdatabasemongodbmongoosepopulate

解决方案


您不需要在动物模式中保留 isCommentedByMe 和 commentCount 字段。

你需要能够访问你的动物的评论。但在动物图式中,没有领域可以建立这种联系。所以我们需要使用虚拟人口。

所以你的动物图式必须是这样的:

const schemaAnimal = new mongoose.Schema(
  {
    name: {
      type: String
    }
  },
  {
    toJSON: { virtuals: true },
    toObject: { virtuals: true }
  }
);

// Virtual populate
schemaAnimal.virtual("comments", {
  ref: "Comment",   //must be changed to the name you used for Comment model.
  foreignField: "animalRef",
  localField: "_id"
});

现在,我们可以使用以下代码来填充注释。

router.get("/animals", async (req, res) => {
  const animals = await Animal.find({}).populate("comments");
  res.send(animals);
});

这将为您提供如下结果:

[
    {
        "_id": "5dd66c73069f88614c12b394",
        "name": "Animal 1",
        "__v": 0,
        "comments": [
            {
                "_id": "5dd66cfd069f88614c12b39a",
                "userRef": "5dd66b54c5195127ec5a1b82",
                "animalRef": "5dd66c73069f88614c12b394",
                "content": "User 1 - Animal 1",
                "__v": 0
            },
            {
                "_id": "5dd66d30069f88614c12b39d",
                "userRef": "5dd66b71c5195127ec5a1b83",
                "animalRef": "5dd66c73069f88614c12b394",
                "content": "User 2 - Animal 1",
                "__v": 0
            }
        ],
        "id": "5dd66c73069f88614c12b394"
    },
    {
        "_id": "5dd66c7d069f88614c12b395",
        "name": "Animal 2",
        "__v": 0,
        "comments": [
            {
                "_id": "5dd66d09069f88614c12b39b",
                "userRef": "5dd66b54c5195127ec5a1b82",
                "animalRef": "5dd66c7d069f88614c12b395",
                "content": "User 1 - Animal 2",
                "__v": 0
            }
        ],
        "id": "5dd66c7d069f88614c12b395"
    },
    {
        "_id": "5dd66c88069f88614c12b396",
        "name": "Animal 3",
        "__v": 0,
        "comments": [
            {
                "_id": "5dd66d46069f88614c12b39e",
                "userRef": "5dd66b71c5195127ec5a1b83",
                "animalRef": "5dd66c88069f88614c12b396",
                "content": "User 2 - Animal 3",
                "__v": 0
            }
        ],
        "id": "5dd66c88069f88614c12b396"
    }
]

要将此结果转换为您想要的结果,我们可以像这样使用 map:

请注意,您需要将loggedInUserId 变量设置为登录用户的ID。

router.get("/animals", async (req, res) => {
  const loggedInUserId = "5dd66b54c5195127ec5a1b82";

  const animals = await Animal.find({}).populate("comments");

  const result = animals.map(animal => {
    return {
      id: animal._id,
      name: animal.name,
      isCommentedByMe:
        animal.comments.filter(c => c.userRef.toString() === loggedInUserId)
          .length > 0,
      commentCount: animal.comments.length
    };
  });

  res.send(result);
});

结果将是这样的:

[
    {
        "id": "5dd66c73069f88614c12b394",
        "name": "Animal 1",
        "isCommentedByMe": true,
        "commentCount": 2
    },
    {
        "id": "5dd66c7d069f88614c12b395",
        "name": "Animal 2",
        "isCommentedByMe": true,
        "commentCount": 1
    },
    {
        "id": "5dd66c88069f88614c12b396",
        "name": "Animal 3",
        "isCommentedByMe": false,
        "commentCount": 1
    }
]

评论中问题的答案是:(如何引用用户)

  const animals = await Animal.find({}).populate({
    path: "comments",
    model: Comment,
    populate: [
      {
        path: "userRef",
        model: User
      }
    ]
  });

这将为您提供这样的 userRef:

[
    {
        "_id": "5dd66c73069f88614c12b394",
        "name": "Animal 1",
        "__v": 0,
        "comments": [
            {
                "_id": "5dd66cfd069f88614c12b39a",
                "userRef": {
                    "_id": "5dd66b54c5195127ec5a1b82",
                    "name": "User 1",
                    "__v": 0
                },
                "animalRef": "5dd66c73069f88614c12b394",
                "content": "User 1 - Animal 1",
                "__v": 0
            },
            {
                "_id": "5dd66d30069f88614c12b39d",
                "userRef": {
                    "_id": "5dd66b71c5195127ec5a1b83",
                    "name": "User 2",
                    "__v": 0
                },
                "animalRef": "5dd66c73069f88614c12b394",
                "content": "User 2 - Animal 1",
                "__v": 0
            }
        ],
        "id": "5dd66c73069f88614c12b394"
    },
    {
        "_id": "5dd66c7d069f88614c12b395",
        "name": "Animal 2",
        "__v": 0,
        "comments": [
            {
                "_id": "5dd66d09069f88614c12b39b",
                "userRef": {
                    "_id": "5dd66b54c5195127ec5a1b82",
                    "name": "User 1",
                    "__v": 0
                },
                "animalRef": "5dd66c7d069f88614c12b395",
                "content": "User 1 - Animal 2",
                "__v": 0
            }
        ],
        "id": "5dd66c7d069f88614c12b395"
    },
    {
        "_id": "5dd66c88069f88614c12b396",
        "name": "Animal 3",
        "__v": 0,
        "comments": [
            {
                "_id": "5dd66d46069f88614c12b39e",
                "userRef": {
                    "_id": "5dd66b71c5195127ec5a1b83",
                    "name": "User 2",
                    "__v": 0
                },
                "animalRef": "5dd66c88069f88614c12b396",
                "content": "User 2 - Animal 3",
                "__v": 0
            }
        ],
        "id": "5dd66c88069f88614c12b396"
    }
]

推荐阅读