У меня есть документ в MongoDB, который выглядит следующим образом.
{
"_id" : ObjectId("5ceb812b3ec6d22cb94c82ca"),
"key" : "KEYCODE001",
"values" : [
{
"classId" : "CLASS_01",
"objects" : [
{
"code" : "DD0001"
},
{
"code" : "DD0010"
}
]
},
{
"classId" : "CLASS_02",
"objects" : [
{
"code" : "AD0001"
}
]
}
]
}
Я заинтересован в получении результата, как показано ниже.
{
"classId" : "CLASS_01",
"objects" : [
{
"code" : "DD0001"
},
{
"code" : "DD0010"
}
]
}
Для этого я придумал конвейер агрегации в Robo 3T, который выглядит следующим образом. И это работает, как ожидалось.
[
{
$match:{
'key':'KEYCODE001'
}
},
{
"$unwind":{
"path": "$values",
"preserveNullAndEmptyArrays": true
}
},
{
"$unwind":{
"path": "$values.objects",
"preserveNullAndEmptyArrays": true
}
},
{
$match:{
'values.classId':'CLASS_01'
}
},
{
$project:{
'object':'$values.objects',
'classId':'$values.classId'
}
},
{
$group:{
'_id':'$classId',
'objects':{
$push:'$object'
}
}
},
{
$project:{
'_id':0,
'classId':'$_id',
'objects':'$$objects'
}
}
]
Теперь, когда я пытаюсь сделать то же самое в приложении SpringBoot, я не могу его запустить. В итоге у меня возникла ошибка java.lang.IllegalArgumentException: Invalid reference '$complication'!
. Ниже приведено то, что я сделал на Java до сих пор.
final Aggregation aggregation = newAggregation(
match(Criteria.where("key").is("KEYCODE001")),
unwind("$values", true),
unwind("$values.objects", true),
match(Criteria.where("classId").is("CLASS_01")),
project().and("$values.classId").as("classId").and("$values.objects").as("object"),
group("classId", "objects").push("$object").as("objects").first("$classId").as("_id"),
project().and("$_id").as("classId").and("$objects").as("objects")
);
Что я делаю неправильно? После исследования я обнаружил, что несколько полей в группе не работают или что-то в этом роде (см. этот вопрос). Итак, возможно ли то, что я сейчас делаю, в Spring Boot?
После нескольких часов отладки + проб и ошибок было обнаружено, что следующее решение работает.
final Aggregation aggregation = newAggregation(
match(Criteria.where("key").is("KEYCODE001")),
unwind("values", true),
unwind("values.objects", true),
match(Criteria.where("values.classId").is("CLASS_01")),
project().and("values.classId").as("classId").and("values.objects").as("object"),
group(Fields.from(Fields.field("_id", "classId"))).push("object").as("objects"),
project().and("_id").as("classId").and("objects").as("objects")
);
Все сводится к group(Fields.from(Fields.field("_id", "classId"))).push("object").as("objects")
тому, что вводит org.springframework.data.mongodb.core.aggregation.Fields
объект, который оборачивает список org.springframework.data.mongodb.core.aggregation.Field
объектов. Внутри Field
можно инкапсулировать имя поля и цель. Это привело к следующему конвейеру, который соответствует ожидаемому.
[
{
"$match" :{
"key" : "KEYCODE001"
}
},
{
"$unwind" :{
"path" : "$values", "preserveNullAndEmptyArrays" : true
}
},
{
"$unwind" :{
"path" : "$values.objects", "preserveNullAndEmptyArrays" : true
}
},
{
"$match" :{
"values.classId" : "CLASS_01"
}
},
{
"$project" :{
"classId" : "$values.classId", "object" : "$values.objects"
}
},
{
"$group" :{
"_id" : "$classId",
"objects" :{
"$push" : "$object"
}
}
},
{
"$project" :{
"classId" : "$_id", "objects" : 1
}
}
]
Кроме того, я понял, что нет необходимости использовать знак $
везде и всюду.
Для тех, у кого есть похожая проблема, посмотрите эта почта.
Mongo может быть крутым и не модельным, но его язык запросов повсюду. Черт, я скучаю по SQL.
Благодаря вашему посту я нашел решение очень похожей проблемы!