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

How do I nest a field within an Aggregation projection field in Spring Data Mongo

手写时,聚合管道中的 $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 接口只允许一个字符串名称和一个字符串目标,而不是能够定义另一个 Field 作为目标。

谢谢!

对于其他为此苦苦挣扎的人 -- Spring 数据 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;
}

}