Search code examples
springmongodbspring-data-mongodb

Create Spring Data Aggregation Query with Projection of Nested Array


Here is how my document looks like:

{
"_id" : ObjectId("583cb6bcce047d1e68339b64"),
"variantDetails" : [ 
    {
        "variants" : {
            "_" : "_"
        },
        "sku" : "069563-59690"
    }, 
    {
        "variants" : {
            "size" : "35"
        },
        "sku" : "069563-59690-35",
        "barcode" : "809702246941"
    }, 
    {
        "variants" : {
            "size" : "36"
        },
        "sku" : "069563-59690-36",
        "barcode" : "809702246958"
    }
    ......
] }

And I would like to use a complex aggregation query like this:

db.getCollection('product').aggregate([
    { '$match': { 'variantDetails.sku': { '$in': ['069563-59690', '069563-59690-36', '069563-59690-37', '511534-01001'] } } },
    { '$project': {'_id': 1, 'variantDetails': 1, 'variantLength': { '$size': '$variantDetails' } } },
    { '$unwind': '$variantDetails' },
    { '$match': { 'variantDetails.sku': { '$in': ['069563-59690', '069563-59690-36', '069563-59690-37', '511534-01001'] } } },
    { '$match': { '$or': [
        {'variantLength': { '$ne': 1 }, 'variantDetails.variants._': { '$ne': '_' } },
        {'variantLength': 1 }
    ] } },
    { '$group': { '_id': '$_id', 'variantDetails': { '$push': '$variantDetails' } } },
    { '$project': {'_id': 1, 'variantDetails.sku': 1, 'variantDetails.barcode': 1} }
])

And here is my java code:

    final Aggregation agg = Aggregation.newAggregation(
            Aggregation.match(Criteria.where("variantDetails.sku").in(skus)),
            Aggregation.project("_id", "variantDetails").and("variantDetails").project("size").as("variantLength"),
            Aggregation.unwind("variantDetails"),
            Aggregation.match(Criteria.where("variantDetails.sku").in(skus)),
            Aggregation.match(new Criteria().orOperator(Criteria.where("variantLength").is(1), Criteria.where("variantLength").ne(1).and("variantDetails.variants._").is("_"))),
            Aggregation.group("_id").push("variantDetails").as("variantDetails"),
            Aggregation.project("_id", "variantDetails.sku", "variantDetails.barcode")
            );

    final AggregationResults<Product> result =  this.mongo.aggregate(agg, this.mongo.getCollectionName(Product.class), Product.class);
    return result.getMappedResults();

The problem is that spring translate

Aggregation.project("_id", "variantDetails.sku", "variantDetails.barcode")

To

{ "$project" : { "_id" : 1 , "sku" : "$variantDetails.sku" , "barcode" : "$variantDetails.barcode"}

But I'm expecting

{ '$project': {'_id': 1, 'variantDetails.sku': 1, 'variantDetails.barcode': 1} }

Could someone let me know how to make it right?


Solution

  • You just need to specify the label as alias in the projection operation as the default that spring provides doesnt match. Use Spring 1.8.5 version

    Aggregation.project("_id")
                       .and(context -> new BasicDBObject("$arrayElemAt", Arrays.asList("variantDetails.sku", 0))).as("variantDetails.sku")
                       .and(context -> new BasicDBObject("$arrayElemAt", Arrays.asList("variantDetails.barcode", 0))).as("variantDetails.barcode"));