3

Using aggregation pipeline in spring Data, I have documents with nested arrays, and I want to project an array except the last item of it. for example for each document like :

{
    "_id" : ObjectId("59ce411c2708c97154d13150"),
    "doc1" : [ 
        {
            "nodeValue" : "AAA"
        }, 
        {
            "nodeValue" : "BBB"
        }, 
        {
            "nodeValue" : "CCC"
        }, 
        {
            "nodeValue" : "DDD"
        }
    ],
    "field2" : 20170102,
    "field3" : 4,
}

I want as result :

{
    "_id" : ObjectId("59ce411c2708c97154d13150"),
    "doc1" : [ 
        {
            "nodeValue" : "AAA"
        }, 
        {
            "nodeValue" : "BBB"
        }, 
        {
            "nodeValue" : "CCC"
        }, 
        {
            "nodeValue" : "DDD"
        }
    ],
    "doc1_without_last" : [ 
        {
            "nodeValue" : "AAA"
        }, 
        {
            "nodeValue" : "BBB"
        }, 
        {
            "nodeValue" : "CCC"
        }
    ],
    "field2" : 20170102,
    "field3" : 4,
}

I tried something like this, but I didn't find a operator that can $pop the array and remove the last item from it.

Aggregation agg = Aggregation.newAggregation(
                      Aggregation.project()
                        .andInclude("doc1","field2","field3"),
                       Aggregation.project().and(ArrayOperators.arrayOf("doc1")..).as("doc1_without_last")

                       new OutOperation("newCollection")
                ).withOptions(Aggregation.newAggregationOptions().allowDiskUse(true).build());

Thank you for your help

7
  • There is a $slice there. Commented Nov 22, 2017 at 13:22
  • the $slice just controls the number of items of an array to project. $slice(-n) project just the last n elements and $slice(n) for the first n elements. But how to project all datas of the array except the last one. Commented Nov 22, 2017 at 13:35
  • Sorry, forgot to mention that there is also $size. All but the last is $slice($size - 1). Just in case docs.mongodb.com/manual/reference/operator/aggregation-array has all of them. Commented Nov 22, 2017 at 13:40
  • there is no way to do it by spring data in pipeline $slice in java get only a numeric integer parameter. I had the idea to reverse the array to get simply the last one as the first like this .and(ArrayOperators.arrayOf(doc1).reverse()).as("Reversed_doc1) But how to exclude the Reversed_doc1.0.nodeValue from the projection Commented Nov 22, 2017 at 14:11
  • I am pretty sure you can build a custom $slice with serverside calculated $size - 1 , see stackoverflow.com/questions/39393672/… for inspiration Commented Nov 22, 2017 at 15:05

2 Answers 2

3

The shell query can be like this:

db.collection.aggregate([
    { $project: {
        doc1: 1,
        doc1_without_last: { $slice: [ 
            "$doc1", 
            { $subtract: [ { $size: "$doc1" }, 1 ] } 
        ] }
    } }
])

Docs:

If there are any limitations in spring that don't let you build such query, you may employ $let to use query variables:

db.collection.aggregate([
    { $project: {
        doc1: 1,
        doc1_without_last: { $let: {
            vars: { size: { $subtract: [ { $size: "$doc1" }, 1 ] } },
            in: { $slice: [ "$letters", "$$size"] }
        } }    
    } }
])
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you Alex for your help, I tried something like that : .and(VariableOperators .define(VariableOperators.Let.ExpressionVariable.newVariable("SSIZE") .forExpression(AggregationFunctionExpressions.SUBTRACT.of(Fields.field("pathLengthInInteraction"),1))) .andApply(ArrayOperators.Slice.sliceArrayOf("doc").offset(?))).as("WITHOUT_LAST") "pathLengthInInteraction is a the size already projected, the offset function support juste integer value, I can"t recuperate the SSIZE value
2

Finally, After Blowing my mind, the trick was simply that :

Aggregation agg = Aggregation.newAggregation(
Aggregation.project()
.andInclude("doc1","field1","field2")
.andExpression("slice(doc1, size(doc1)-1)").as("doc1_without_last"),
new OutOperation("newCollection")
                ).withOptions(Aggregation.newAggregationOptions().allowDiskUse(true).build());

the andExpression in projections serve to include some mongo operators in a String format.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.