如何在Spring Data Mongo的聚合投影字段中嵌套字段

时间:2016-10-11 21:52:45

标签: java mongodb spring-data aggregation-framework spring-data-mongodb

手写时,我的聚合管道中的$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作为目标。

谢谢!

1 个答案:

答案 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;
}

}