简体   繁体   English

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

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

When written by hand, the $project step in my aggregation pipeline looks like: 手动编写时,我的聚合管道中的$project步骤如下所示:

    {  
     "$project":{  
        "DRIVE":{
           "componentSummary":{"manufacturer" : "$_id.DRIVE_manufacturer"},
           "componentCount":"$_id.DRIVE_componentCount"
        },
        "hostnames":1,
        "_id":0
     }
  }

I understand that I can use the ProjectionOperationBulder to create a single level of nesting (using builder.nested ), to make something like, say: 我知道我可以使用ProjectionOperationBulder来创建一个单独的嵌套级别(使用builder.nested )来制作类似的东西,比如:

    {  
     "$project":{  
        "DRIVE":{  
           "manufacturer":"$_id.DRIVE_manufacturer"
        },
        "hostnames":1,
        "_id":0
     }
  }

But I can't seem to figure out how to nest another level deep, as the Field interface only allows for a String name and a String target, rather than being able to define antother Field as the target. 但我似乎无法弄清楚如何深入嵌套另一个级别,因为Field接口只允许String名称和String目标,而不是能够将另一个Field定义为目标。

Thanks! 谢谢!

For anyone else struggling with this -- Spring Data Mongo does not natively support multi level nesting as of this writing (stable version 1.9.5). 对于其他正在努力解决这个问题的人来说,Spring Data Mongo本身并不支持多级嵌套(稳定版本1.9.5)。 However, as of 1.9.3, it does support custom AggregationExpressions that allow you to define the behavior yourself. 但是,从1.9.3开始,它支持自定义AggregationExpressions ,允许您自己定义行为。 Be aware that if you go down this route, you'll have to build the JSON for the query mostly by hand. 请注意,如果沿着这条路线前进,您将必须手动为查询构建JSON。 My implementation is pretty quick and dirty but here it is for reference's sake. 我的实现非常快速和肮脏,但这里是为了参考的缘故。

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

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM