创建 Mongoose 从多个表中查找并填充 属性 集

Create Mongoose Find and populate from multiple tables with property set

对于 Mongoose 的帮助,我将不胜感激。我有 3 tables:(Users)Table 用户,(Animals)table 动物和 table AnimalComments。所以 (AnimalComments)table 引用用户和动物。

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
    }
});

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

table 动物的回应:

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

table 用户的回复:

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

来自 table 动物评论的回复:

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

我想要例如结果:

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

您不需要在动物模式中保留 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"
    }
]

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

请注意,您需要将 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"
    }
]