1

有一个指向作者列表的书籍列表,我想显示一棵树,在每个节点中都有作者姓名和他写的书籍数量。最初,我将authors[]数组直接嵌入到书籍集合中,这就像一个魅力,使用了聚合框架的魔力。然而,后来,我意识到给每个作者附加一些附加信息会很好(例如照片、传记数据、出生日期等)。对于第一个解决方案,这很糟糕,因为:

  • 它复制数据(没什么大不了的,是的,我知道 mongo 的目的是封装完整的对象,但现在让我们忽略它);
  • 每当在旧记录上创建或更新附加属性时,都不会从此更改中受益,除非我专门查询一些独特的旧属性并使用新/更新的值更新所有图书作者。

接下来是使用第二个集合,称为authors,每个books文档都引用一个作者 ID 列表,如下所示:

{
    "_id" : ObjectId("58ed2a254374473fced950c1"),
    "authors" : [ 
        "58ed2a254d74s73fced950c1", 
        "58ed2a234374473fce3950c1"
    ],
    "title" : "Book title"
....
}

要获取作者详细信息,我有两种选择:

  • 进行附加查询以从作者集合中获取数据;
  • 使用 DBRefs。

问题:

  1. 使用 DBRefs 自动将作者数据加载到 book 对象中,类似于 JPA @MannyToOne 所做的事情?
  2. 是否可以得到每个作者的成书数量,而不必查询每个作者的书数?当作者被嵌入时,我能够汇总不同的作者姓名以及他所在的书籍文档的数量。在两个集合之间可以进行这样的查询吗?

您对实施此行为有何建议?(我正在使用 Spring Data)

4

2 回答 2

2

您可以在 spring mongo 应用程序中尝试以下查询。

UnwindOperation unwindAuthorIds = Aggregation.unwind("authorsIds", true);
LookupOperation lookupAuthor = Aggregation.lookup("authors_collection", "authorsIds", "_id", "ref");
UnwindOperation unwindRefs = Aggregation.unwind("ref", true);
GroupOperation groupByAuthor = Aggregation.group("ref.authorName").count().as("count");

Aggregation aggregation = Aggregation.newAggregation(unwindAuthorIds, lookupAuthor, unwindRefs, groupByAuthor);

List<BasicDBObject> results = mongoOperations.aggregate(aggregation, "book_collection", BasicDBObject.class).getMappedResults();
于 2017-04-15T18:13:37.270 回答
1

按照@Veeram 的建议,我能够编写此查询:

db.book_collection.aggregate([
    {
        $unwind: "$authorsIds"
    },
    {
        $lookup: {
            from: "authors_collection",
            localField: "authorsIds",
            foreignField: "_id",
            as: "ref"
        }
    },
    {$group: {_id: "$ref.authorName", count: {$sum: 1}}}
])

它返回如下内容:

{
    "_id" : [ 
        "Paulo Coelho"
    ],
    "count" : 1
}

/* 2 */
{
    "_id" : [ 
        "Jules Verne"
    ],
    "count" : 2
}

这正是我所需要的,而且听起来很对。我现在只需要做一个额外的查询来获取没有作者集的书籍。

于 2017-04-15T12:01:11.437 回答