本文介紹了如何在Spring中聚合數(shù)據(jù)mongo db嵌套對(duì)象并避免PropertyReferenceException?的處理方法,對(duì)大家解決問(wèn)題具有一定的參考價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧!
問(wèn)題描述
我正在SpringBoot中創(chuàng)建一個(gè)新的端點(diǎn),它將返回從Mongo數(shù)據(jù)庫(kù)中的聚合查詢生成的用戶的簡(jiǎn)單統(tǒng)計(jì)信息。然而,我得到一個(gè)PropertyReferenceException
。我已經(jīng)閱讀了有關(guān)它的多個(gè)堆棧溢出問(wèn)題,但沒(méi)有找到解決此問(wèn)題的問(wèn)題。
我們有這樣的Mongo數(shù)據(jù)方案:
{
"_id" : ObjectId("5d795993288c3831c8dffe60"),
"user" : "000001",
"name" : "test",
"attributes" : {
"brand" : "Chrome",
"language" : "English" }
}
數(shù)據(jù)庫(kù)中充滿了多個(gè)用戶,我們希望使用SpringBoot按brand
匯總用戶的統(tǒng)計(jì)數(shù)據(jù)。attributes
對(duì)象中可以有任意數(shù)量的屬性。
這是我們正在進(jìn)行的聚合
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();
它會(huì)生成這個(gè)有效的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" }
但是,在運(yùn)行簡(jiǎn)單的集成測(cè)試時(shí),我們收到以下錯(cuò)誤:
org.springframework.data.mapping.PropertyReferenceException: No property brand found for type String! Traversed path: Profile.attributes.
據(jù)我了解,似乎因?yàn)?code>attributes是Map<String, String>
,所以可能存在原理圖問(wèn)題。同時(shí),我不能修改Profile
對(duì)象。
我是否在聚合中遺漏了什么,或者我的Stats
對(duì)象中有什么可以更改的?
以下是我們用來(lái)處理JSON和Jackson的數(shù)據(jù)模型,以供參考。
Stats
數(shù)據(jù)模型:
@Document
public class Stats {
@JsonProperty
private String type;
@JsonProperty
private int number;
public Stats() {}
/* ... */
}
Profile
數(shù)據(jù)模型:
@Document
public class Profiles {
@NotNull
@JsonProperty
private String user;
@NotNull
@JsonProperty
private String name;
@JsonProperty
private Map<String, String> attributes = new HashMap<>();
public Stats() {}
/* ... */
}
推薦答案
我找到了一個(gè)解決方案,這是兩個(gè)問(wèn)題的組合:
PropertyReferenceException
確實(shí)是因?yàn)?code>attributes是Map<String, String>
,這意味著沒(méi)有針對(duì)Mongo的方案。
錯(cuò)誤消息No property brand found for type String! Traversed path: Profile.attributes.
表示Map
對(duì)象中沒(méi)有品牌屬性。
為了在不觸及原始Profile
類的情況下修復(fù)該問(wèn)題,我必須創(chuàng)建一個(gè)新的自定義類,它將attributes
映射到一個(gè)具有我想要聚集的屬性的屬性對(duì)象:
public class StatsAttributes {
@JsonProperty
private String brand;
@JsonProperty
private String language;
public StatsAttributes() {}
/* ... */
}
然后,我創(chuàng)建了一個(gè)自定義StatsProfile
,它將利用我的StatsAttributes
,并且將類似于原始的Profile
對(duì)象,而不修改它。
@Document
public class StatsProfile {
@JsonProperty
private String user;
@JsonProperty
private StatsAttributes attributes;
public StatsProfile() {}
/* ... */
}
通過(guò)在聚合中使用我的新類StatsAggregation
,我消除了PropertyReferenceException
的問(wèn)題:
AggregationResults<Stats> groupResults
= mongoTemplate.aggregate(agg, StatsProfile.class, Stats.class);
但是,我不會(huì)得到任何結(jié)果。查詢似乎在數(shù)據(jù)庫(kù)中找不到任何文檔。這就是我意識(shí)到生產(chǎn)Mongo對(duì)象具有綁定到Profile
對(duì)象的字段"_class: com.company.dao.model.Profile"
的地方。
經(jīng)過(guò)研究,新的StatsProfile
需要是@TypeAlias("Profile")
才能工作。在環(huán)顧四周后,我發(fā)現(xiàn)我還需要精確地指定一個(gè)集合名稱,該名稱將指向:
@Document(collection = "profile")
@TypeAlias("Profile")
public class StatsProfile {
/* ... */
}
盡管如此,它最終還是成功了!
我想這不是最好的解決方案,我希望我不需要?jiǎng)?chuàng)建新的配置文件對(duì)象,只將
attributes
視為mongoTemplate查詢中的StatsAttributes.class
。如果有人知道如何操作,請(qǐng)分享