使用Spring Data在Mongo DB中展开和拆分多个子文档

问题描述

这里是存储数据的示例

[
   {
      "userId":"user123","name":"John","card":{
         "amount":1000.0,"sentMoneyList":[
            {
               "creationDate":"2019-08-07T00:00:00.000+0000","shopId":"merchant1","loyaltyPoint":200,"amount":250
            },{
               "creationDate":"2019-01-07T00:00:00.000+0000","shopId":"merchant2","loyaltyPoint":100,"amount":99
            }
         ],"receivedMoneyList":[
            {
               "creationDate":"2019-09-07T00:00:00.000+0000","amount":40
            },{
               "creationDate":"2019-03-07T00:00:00.000+0000","amount":500
            }
         ]
      }
   }
]

我想建立一个从给定日期开始的所有用户的收款和汇款时间表。 如果startDate为"2019-02-01T00:00:00.000+0000",则我的请求的输出应如下所示:

[
   {
      "userId":"user123","amount":250
            }
         ]
      }
   },{
      "userId":"user123","amount":40
            }
         ]
      }
   },"receivedMoneyList":[
            {
               "creationDate":"2019-03-07T00:00:00.000+0000","amount":500
            }
         ]
      }
   }
]

以下是尝试获得此结果的Java代码

  Criteria criteriaClient = new Criteria();  
  MatchOperation matchOperation = match(criteriaClient.orOperator(
          Criteria.where("card.sentMoneyList.creationDate").gte(startDate),Criteria.where("card.receivedMoneyList.creationDate").gte(startDate)));

  UnwindOperation unwindSent = Aggregation.unwind("card.sentMoneyList");
  UnwindOperation unwindReceived = Aggregation.unwind("card.receivedMoneyList");

  Aggregation aggregation = Aggregation.newAggregation(unwindSent,unwindReceived,matchOperation);

   List<UserDTO> result = mongoTemplate.aggregate(
                aggregation,"users",UserDTO.class).getMappedResults();

它给出一个空的列表。为了得到上面的结果,查询中缺少什么? 谢谢

解决方法

您可以使用$facet获得预期的输出,这有助于您对传入的数据进行分类。在这里,我在 sentMoney 数组中获得了 sentMoneyList 数组,在 receivedMoney 中获得了 receivedMoneyList 数组。然后汇总任何能为您提供输出的内容。

public List<Object> test() {
    Aggregation aggregation = Aggregation.newAggregation(
        facet(
                p -> new Document("$project",new Document("card.receivedMoneyList",0)
                ),a -> new Document("$addFields",new Document("card.sentMoneyList",new Document("$filter",new Document("input","$card.sentMoneyList")
                                                .append("cond",new Document("$gte",Arrays.asList("$$this.creationDate","2019-02-01T00:00:00.000+0000"))
                                                )
                                )
                        )
                ),unwind("$card.sentMoneyList")

        ).as("sentMoney").and(
                p -> new Document("$project",new Document("card.receivedMoney","$card.receivedMoney")
                                                .append("cond",unwind("$card.receivedMoney")
        ).as("receivedMoney"),p -> new Document("$project",new Document("combined",new Document("$concatArrays",Arrays.asList("$sentMoney","$receivedMoney"))
                )
        ),unwind("$combined"),replaceRoot("combined")

    ).withOptions(AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build());

    return mongoTemplate.aggregate(aggregation,mongoTemplate.getCollectionName(Users.class),Object.class).getMappedResults();

}

首先,我要求您使用Object.class来获取汇总结果并以List<Object>的形式返回。如果效果良好,则可以将此模型转换为UserDTO.class,其结构应与输出相同。

您已添加目标集合users,但这不是一个好习惯。因此,请使用mongoTemplate.getCollectionName(YOUR_TARGET_COLLECTION.class)

否:我没有尝试过此代码,但这是基于有效的Mongo playground

编写的