手写时,我的聚合管道中的$project
步骤如下:
{
"$project":{
"DRIVE":{
"componentSummary":{"manufacturer" : "$_id.DRIVE_manufacturer"},
"componentCount":"$_id.DRIVE_componentCount"
},
"hostnames":1,
"_id":0
}
}
据我所知,我可以使用ProjectionOperationBulder
创建单一级别的嵌套(使用builder.nested),例如:
{
"$project":{
"DRIVE":{
"manufacturer":"$_id.DRIVE_manufacturer"
},
"hostnames":1,
"_id":0
}
}
但我似乎无法弄清楚如何深入嵌套另一个级别,因为Field
接口只允许String名称和String目标,而不是能够定义另一个Field
作为目标。
谢谢!
答案 0 :(得分:0)
对于任何正在努力解决这个问题的人来说,Spring Data Mongo本身并不支持多级嵌套(稳定版本1.9.5)。但是,从1.9.3开始,它支持custom AggregationExpressions,允许您自己定义行为。请注意,如果沿着这条路走下去,那么您必须手动为查询构建JSON。我的实现非常快速和肮脏,但这里仅供参考。
protected class NestedField implements Field {
private String name;
private List<Field> fields;
public NestedField(String name, List<Field> fields) {
this.name = name;
this.fields = fields;
}
public List<Field> getFields() {
return fields;
}
@Override
public String getName() {
return name;
}
private String escapeSystemVariables(String fieldTarget) {
if (fieldTarget.startsWith("_id")) {
return StringUtils.prependIfMissing(fieldTarget, "$");
} else {
return fieldTarget;
}
}
private String encloseStringInQuotations(String quotable) {
return JSON.serialize(quotable);
}
private String buildSingleFieldTarget(Field field) {
if (field instanceof NestedField) {
return String.join(":", encloseStringInQuotations(field.getName()), field.getTarget());
}
return String.join(":", encloseStringInQuotations(field.getName()), encloseStringInQuotations(escapeSystemVariables(
field.getTarget())));
}
private String buildFieldTargetList(List<Field> fields) {
List<String> fieldStrings = new ArrayList<>();
fields.forEach(field -> {
fieldStrings.add(buildSingleFieldTarget(field));
});
return Joiner.on(",").skipNulls().join(fieldStrings);
}
@Override
public String getTarget() {
// TODO Auto-generated method stub
return String.format("{%s}", buildFieldTargetList(fields));
}
@Override
public boolean isAliased() {
return true;
}
}
protected class NestedProjection implements AggregationExpression {
private List<Field> projectedFields;
public NestedProjection(List<Field> projectedFields) {
this.projectedFields = projectedFields;
}@Override
public DBObject toDbObject(AggregationOperationContext context) {
DBObject projectionExpression = new BasicDBObject();
for(Field f : projectedFields) {
//this is necessary because if we just put f.getTarget(), spring-mongo will attempt to JSON-escape the string
DBObject target = (DBObject) com.mongodb.util.JSON.parse(f.getTarget());
projectionExpression.put(f.getName(), target);
}
return projectionExpression;
}
}