[英]How to aggregate in spring data mongo db a nested object and avoid a PropertyReferenceException?
I am creating a new endpoint in springboot that will return simple stats on users generated from an aggregate query in a mongo database. 我正在springboot中创建一个新端点,该端点将对从mongo数据库中的聚合查询生成的用户返回简单的统计信息。 However I get a
PropertyReferenceException
. 但是我得到了
PropertyReferenceException
。 I have read multiple stackoverflow questions about it, but didn't find one that solved this problem. 我已经阅读了关于它的多个stackoverflow问题,但没有找到解决该问题的方法。
We have a mongo data scheme like this: 我们有一个像这样的mongo数据方案:
{
"_id" : ObjectId("5d795993288c3831c8dffe60"),
"user" : "000001",
"name" : "test",
"attributes" : {
"brand" : "Chrome",
"language" : "English" }
}
The database is filled with multiple users and we want using Springboot aggregate the stats of users per brand
. 该数据库充满了多个用户,我们希望使用Springboot汇总每个
brand
的用户统计信息。 There could be any number of attributes in the attributes
object. attributes
对象中可以有任意数量的属性。
Here is the aggregation we are doing 这是我们正在做的汇总
Aggregation agg = newAggregation(
group("attributes.brand").count().as("number"),
project("number").and("type").previousOperation()
);
AggregationResults<Stats> groupResults
= mongoTemplate.aggregate(agg, Profile.class, Stats.class);
return groupResults.getMappedResults();
Which produces this mongo query which works: 产生此mongo查询的方法:
> db.collection.aggregate([
{ "$group" : { "_id" : "$attributes.brand" , "number" : { "$sum" : 1}}} ,
{ "$project" : { "number" : 1 , "_id" : 0 , "type" : "$_id"}} ])
{ "number" : 4, "type" : "Chrome" }
{ "number" : 2, "type" : "Firefox" }
However when running a simple integration test we get this error: 但是,在运行简单的集成测试时,会出现以下错误:
org.springframework.data.mapping.PropertyReferenceException: No property brand found for type String! Traversed path: Profile.attributes.
From what I understand, it seems that since attributes
is a Map<String, String>
there might be a schematic problem. 据我了解,似乎由于
attributes
是Map<String, String>
所以可能存在一个示意图问题。 And in the mean time I can't modify the Profile
object. 同时,我无法修改
Profile
对象。
Is there something I am missing in the aggregation, or anything I could change in my Stats
object? 聚合中是否缺少某些内容,或者我可以在
Stats
对象中更改的任何内容?
For reference, here are the data models we're using, to work with JSON and jackson. 作为参考,以下是我们正在使用的用于JSON和jackson的数据模型。
The Stats
data model: Stats
数据模型:
@Document
public class Stats {
@JsonProperty
private String type;
@JsonProperty
private int number;
public Stats() {}
/* ... */
}
The Profile
data model: Profile
数据模型:
@Document
public class Profiles {
@NotNull
@JsonProperty
private String user;
@NotNull
@JsonProperty
private String name;
@JsonProperty
private Map<String, String> attributes = new HashMap<>();
public Stats() {}
/* ... */
}
I found a solution, which was a combination of two problems: 我找到了一个解决方案,它是两个问题的组合:
The PropertyReferenceException
was indeed caused because attributes
is a Map<String, String>
which means there is no schemes for Mongo. 实际上,造成
PropertyReferenceException
是因为attributes
是Map<String, String>
,这意味着没有适用于Mongo的方案。
The error message No property brand found for type String! Traversed path: Profile.attributes.
错误消息
No property brand found for type String! Traversed path: Profile.attributes.
No property brand found for type String! Traversed path: Profile.attributes.
means that the Map
object doesn't have a brand property in it. 表示
Map
对象中没有品牌属性。
In order to fix that without touching my orginal Profile
class, I had to create a new custom class which would map the attributes
to an attributes object having the properties I want to aggreate on like: 为了解决此问题而不接触原始的
Profile
类,我必须创建一个新的自定义类,该类将attributes
映射到具有我要添加的attributes
的attribute对象,例如:
public class StatsAttributes {
@JsonProperty
private String brand;
@JsonProperty
private String language;
public StatsAttributes() {}
/* ... */
}
Then I created a custom StatsProfile
which would leverage my StatsAttributes
and would be similar to the the original Profile
object without modifying it. 然后,我创建了一个自定义
StatsProfile
,它将利用我的StatsAttributes
并且与原始的Profile
对象相似,而无需对其进行修改。
@Document
public class StatsProfile {
@JsonProperty
private String user;
@JsonProperty
private StatsAttributes attributes;
public StatsProfile() {}
/* ... */
}
With that I made disapear my problem with the PropertyReferenceException
using my new class StatsAggregation
in the aggregation: 这样,我使用聚合中的新类
StatsAggregation
使PropertyReferenceException
问题StatsAggregation
了:
AggregationResults<Stats> groupResults
= mongoTemplate.aggregate(agg, StatsProfile.class, Stats.class);
However I would not get any results. 但是我不会得到任何结果。 It seems the query would not find any document in the database.
该查询似乎在数据库中找不到任何文档。 That's where I realied that production mongo objects had the field
"_class: com.company.dao.model.Profile"
which was tied to the Profile
object. 那就是我意识到生产mongo对象具有与
Profile
对象相关联的字段"_class: com.company.dao.model.Profile"
。
After some research, for the new StatsProfile
to work it would need to be a @TypeAlias("Profile")
. 经过一番研究,
StatsProfile
新的StatsProfile
起作用,它必须是@TypeAlias("Profile")
。 After looking around, I found that I also needed to precise a collection name which would lead to: 环顾四周后,我发现还需要精确定义集合名称,这将导致:
@Document(collection = "profile")
@TypeAlias("Profile")
public class StatsProfile {
/* ... */
}
And with all that, finally it worked! 有了所有这些,终于奏效了!
I suppose that's not the prettiest solution, I wish I would not need to create a new Profile object and just consider the
attributes
as aStatsAttributes.class
somehow in the mongoTemplate query.我想这不是最漂亮的解决方案,我希望我不会需要创建一个新的配置文件对象,只考虑
attributes
的StatsAttributes.class
不知何故在mongoTemplate查询。 If anyone knows how to, please share 🙏如果有人知道怎么做,请分享🙏
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.