【问题标题】:Why doesn't my Mongo aggregation work properly on nested document?为什么我的 Mongo 聚合不能在嵌套文档上正常工作?
【发布时间】:2025-12-13 14:55:02
【问题描述】:

我有以下课程:

@Document(collection = "rCollection")
public class R{
    public R() {
    }
    @Id
    private String id;

    @TextIndexed(weight=2)
    private String fp;

    @TextIndexed(weight=1)
    private String sp;

    @DBRef
    @Indexed
    private P p;

    @DBRef
    private User user;
    private String date;
}

还有:

@Document(collection = "pCollection")
public class P {

    public P() {}

    @Id 
    private String id;
}

我想做的是根据类的p 元素在RCollectiongroup 中搜索结果。由于P 类中的id 是唯一的,我尝试根据p.id 对结果进行分组。 我的存储库中有以下代码:

TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny(text);
MatchOperation match = Aggregation.match(criteria);
GroupOperation group = Aggregation.group("p.id");
Aggregation aggregation = Aggregation.newAggregation(match, group);

AggregationResults<finalResults> results = mongoTemplate.aggregate(aggregation, "rCollection", finalResults.class);

List<finalResults> result= results.getMappedResults();

System.out.println("size is: "+result.size());
System.out.println("output = "+result.get(0).toString());

当我在上面的代码中使用fp 而不是p.id 时,代码运行良好。但是如果我使用p.id,我会看到结果为空。 result.size() 为 1,但 result.get(0).toString() 为空。即如下输出:

size is: 1
output = FinalResults [fp=null, sp=null]

这是我的finalResults 类(实际上和R 类完全一样):

private class FinalResults{

    public finalResults() {
    }
    private String id;

    private String fp;
    private String sp;

    private P p;
    private User user;
    private String date;

    @Override
    public String toString() {
        return "FinalResults [fp=" + fp + ", sp=" + sp +"]";
    }
}

更新: 样本数据: 我在rCollection 中拥有的一切:

{ 
"_id" : ObjectId("5e9db54c3b4b97129065b773"),
 "_class" : "com.main.tothepoint.model.R", 
"fp" : "hello\ni\nam\n",
"sp " : "hi",
"date" : "Mon Apr 20 16:44:28 CEST 2020",
"p" : DBRef("p", ObjectId("5e9db54c3b4b97129065b772")),
"user" : DBRef("user", ObjectId("5e5aa321383ba54434092e8d"))
}
{
 "_id" : ObjectId("5e9db7903b4b97129065b775"),
"_class" : "com.main.tothepoint.model.R", 
"fp " : "hi",
"sp " : "hello",
"date" : "Mon Apr 20 16:54:08 CEST 2020",
"p" : DBRef("p", ObjectId("5e9db7903b4b97129065b774")),
"user" : DBRef("user", ObjectId("5e6e567dc77ed32ab4ad796b"))
}
{
"_id" : ObjectId("5e9db7a73b4b97129065b777"),
"_class" : "com.main.tothepoint.model.R",
"fp" : "hi",
"sp" : "",
"date" : "Mon Apr 20 16:54:31 CEST 2020",
"p" : DBRef("p", ObjectId("5e9db7a73b4b97129065b776")),
"user" : DBRef("user", ObjectId("5e6e567dc77ed32ab4ad796b"))
}
{
"_id" : ObjectId("5e9ef7e04b6e5d338c02fda0"),
"_class" : "com.main.tothepoint.model.R",
"fp" : "hello",
"sp" : "",
"date" : "Tue Apr 21 15:40:48 CEST 2020",
"p" : DBRef("p", ObjectId("5e9ef7e04b6e5d338c02fd9f")),
"user" : DBRef("user", ObjectId("5e5aa321383ba54434092e8d"))
}
{
"_id" : ObjectId("5e9ef82b4b6e5d338c02fda3"),
"_class" : "com.main.tothepoint.model.R",
"fp " : "hello",
"sp" : "",
"date" : "Tue Apr 21 15:42:03 CEST 2020",
"p" : DBRef("p", ObjectId("5e9ef82b4b6e5d338c02fda2")),
"user" : DBRef("user", ObjectId("5e9ef81f4b6e5d338c02fda1"))
}

以及来自pCollection的相关部分:

{
 "_id" : ObjectId("5e9db54c3b4b97129065b772"),
"_class" : "com.main.tothepoint.model.P",
"p_name" : "Hisar",
"longitude" : 75.7216527,
"latitude" : 29.1491875
}


{
"_id" : ObjectId("5e9db7a73b4b97129065b776"),
"_class" : "com.main.tothepoint.model.P",
"p_name" : "Helsinki",
"longitude" : 24.9383791,
"latitude" : 60.16985569999999
}

数据比我的问题的第一部分包含更多的元素,因为我试图使我的示例类尽可能小。 在我的代码中,我想搜索可以存在于fpsp 中的文本,例如,我搜索“hi”,然后我看到结果的大小为 2187。

【问题讨论】:

    标签: java spring mongodb aggregation-framework mongotemplate


    【解决方案1】:

    问题出在这里:

    GroupOperation group = Aggregation.group("p.id");
    

    在 MongoDB shell 中,它会是这样的:

    {$group:{ _id:"$p._id" }}
    

    如您所见,您的小组赛尚未完成。您需要在小组赛阶段累积fpsp 字段。要累积fpsp 字段的所有可能值,我们需要使用$push(具有重复值的数组)或$addToSet(设置唯一值)运算符并添加额外的$unwind。阶段。

    TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny(text);
    MatchOperation match = Aggregation.match(criteria);
    GroupOperation group = Aggregation.group("p.id").push("fp").as("fp").push("sp").as("sp");
    UnwindOperation unwindFp = Aggregation.unwind("fp");
    UnwindOperation unwindSp = Aggregation.unwind("sp");
    Aggregation aggregation = Aggregation.newAggregation(match, group, unwindFp, unwindSp);
    
    AggregationResults<finalResults> results = mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(R.class), FinalResults.class);
    

    【讨论】:

    • 非常感谢您的回答。 fp 和 sp 可以不同。抱歉,我对spring boot和mongodb真的很陌生,我知道.push("fp").as("fp"),但我不知道如何实现push和addToSet的组合。 (或者我是否理解错了你的答案?)你能再解释一下吗?提前致谢
    • @user1419243 更新了我的答案,请再次检查。您可以在mongoplayground.net 中重现您的查询
    • 感谢您的更新。但是,它仍然无法正常工作。我不再得到空项目,但我的输出现在的大小为 2187,虽然我的数据库中只有 5 个项目,其中 3 个案例有搜索字符串,并且从这三个项目中,其中 2 个共享同一个地方.id。所以,我希望得到两组所有 3 个项目。
    • @user1419243 发布请示例数据
    • 我更新了问题。再次感谢您的跟进。