优化 mongoDB 查询以从单独的集合中获取项目数

Optimize mongoDB query to get count of items from separate collection

我有两个集合,即“标签”和“书签”。

Tags documents: 

        {
            "taggedBookmarksCount": 2,
            "taggedNotesCount": 0,
            "_id": "627a80e6b12b0dc78b3a6d4b",
            "name": "Article"
        },
        {
            "taggedBookmarksCount": 0,
            "taggedNotesCount": 0,
            "_id": "62797885b479b5906ef6ed43",
            "name": "Client"
        },

Bookmark Documents:

{
            "_id": "627a814db12b0dc78b3a6d54",

            "bookmarkTags": [
                {
                    "tagId": "627a814db12b0dc78b3a6d55",
                    "tag": "Article"
                },
                {
                    "tagId": "627a814db12b0dc78b3a6d56",
                    "tag": "to be read"
                }
            ],
            "bookmarkTitle": "Please sorrow of work",
  
           
        }

Objective是获取“标签”集合中所有标签的书签数。

下面是我当前的实现,returns 每个 tags.But 这个查询的书签计数需要大约 3 秒到 运行(REST API 响应时间) 20 个标签。

        tags = await Tag.find(
          {
            userId: req.params.userId
          },
          { _id: 1 }
        );
        tagIds = tags.map(tag => {
          return tag._id.toString();
        });

    const tagCounts = await Promise.all(
      tagIds.map(async tagId => {
        const count = await Model.aggregate([
          {
            $match: {
              bookmarkTags: {
                $elemMatch: {
                  tagId: tagId
                }
              }
            }
          },
          {
            $group: {
              _id: '_id',
              count: {
                $sum: 1
              }
            }
          }
        ]);
        return { tagId, count: count[0] ? count[0].count : 0 };
      })
    );

我假设它需要更长的时间,因为我正在映射所有标签,有多次往返 database.Please 建议减少查询执行时间的方法。

您可以按以下方式进行

db.bookmark.aggregate([
  {
    "$unwind": "$bookmarkTags" //Reshape tags
  },
  {
    "$lookup": { //Do a join
      "from": "tags",
      "localField": "bookmarkTags.tagId",
      "foreignField": "_id",
      "as": "btags"
    }
  },
  {
    "$unwind": { //reshape the array elements
      path: "$btags",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    "$group": { // Group tag wise bookmarks
      "_id": "$bookmarkTags.tagId",
      "docs": {
        "$addToSet": "$btags"
      }
    }
  },
  {
    "$project": { //Get counts, project what you want.
      tag_id: "$_id",
      "count": {
        "$size": "$docs"
      },
      _id: 0
    }
  }
])

Playground

如果你已经给出了标签id列表,那么你可以在比赛阶段使用它。

Updated playground

db.bookmark.aggregate([
  {
    "$unwind": "$bookmarkTags"
  },
  {
    "$lookup": {
      "from": "tags",
      "localField": "bookmarkTags.tagId",
      "foreignField": "_id",
      "as": "btags"
    }
  },
  {
    "$unwind": {
      path: "$btags",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    "$group": {
      "_id": "$btags._id",
      "docs": {
        "$push": "$btags"
      }
    }
  },
  {
    "$project": {
      tag_id: "$_id",
      "count": {
        "$size": "$docs"
      },
      _id: 0
    }
  }
])