2

I have a document.

{
"errors" : [
    {
        "priority" : 3,
        "category" : "aaa"
        "other":"nothing"
    },
    {
        "priority" : 4,
        "category" : "bbb"
        "other":"nothing"
    },
    {
        "priority" : 2,
        "category" : "ccc"
        "other":"nothing"
    },
    {
        "priority" : 3,
        "category" : "ddd"
        "other":"nothing"
    },
    {
        "priority" : 2,
        "category" : "eee"
        "other":"nothing"
    }
    ],
"file_name" : "xxx.json",
"vehicle_id" : "esdf",
"day" : "2022-03-08"
}

I execute a command with js client.

db.wty_test.aggregate({
    $project: {
        '_id': 0, 'errors.priority': 1, 'errors.category': 1, 'file_name': 1, 'vehicle_id': 1,
    }
})

I get the result I want.enter image description here The errors is an array containing objects.

Now I need to overwrite this command with java client(springboot-data-mongo). This is my java code.

import org.springframework.data.mongodb.core.MongoTemplate;
...
Aggregation aggregation = Aggregation.newAggregation(Aggregation.project("errors.priority", "errors.category", "file_name", "vehicle_id"));
mongoTemplate.aggregate(aggregation, "wty_test", HashMap.class).getMappedResults();

enter image description here The priority and category is not in errors.

How to use java to get the same result as js?

I try the nested.But it's not what I want.

enter image description here


enter image description here

wtyork
  • 112
  • 7

3 Answers3

1

Here is a way to get the desired result.

Document projectn = new Document("$project", 
                        new Document("_id", 0L)
                        .append("file_name", 1L)
                        .append("vehicle_id", 1L)
                        .append("errors", 
                            new Document("$map", 
                                new Document("input", "$errors")
                                .append("in", 
                                    new Document("priority", "$$this.priority")
                                    .append("category", "$$this.category")
                                )
                            )
                        )
                    );

List<Document> pipeline = Arrays.asList(projectn);
List<Document> results = mongoOps.getCollection("collection_name")
                                 .aggregate(pipeline)
                                 .into(new ArrayList<>());

Note that this uses MongoDB Java Driver API query syntax, and Document is org.bson.Document. The conversion of the native query to Java Driver uses $map aggregation array operator (and it looks like thats the (maybe only) way).

With MongoTemplate.aggregate the code is:

    Aggregation agg = newAggregation(
            project() 
            .and( 
                VariableOperators.Map.itemsOf("errors")
                    .as("e")
                    .andApply(ctx -> new Document("priority", "$$e.priority").append("category", "$$e.category") ))
            .as("errors")
            .andExclude("_id")
            .andInclude("file_name", "vehicle_id")
    );

    AggregationResults<Document> results = mongoOps.aggregate(agg, "collection_name", Document.class);


Alternate Method:

In case your query is just about the projection, you can use the following query using MongoTemplate#find method. This is much simpler to construct and understand:

db.collection.find(
    {}, // your query filter
    { _id: 0, 'errors.category': 1, 'errors.priority': 1, file_name: 1, vehicle_id: 1 }
)

The MongoTemplate version of it:

Query query = new Query();
query.fields()
     .include("errors.priority", "errors.category", "file_name", "vehicle_id")
     .exclude("_id");
List<Document> results = mongoOps.find(query, Document.class, "collection_name");
prasad_
  • 12,755
  • 2
  • 24
  • 36
0

You want to be using the nested() function, like so:

AggregationOperation project = Aggregation.project("file_name", "vehicle_id").
    and("errors").nested(Fields.fields("priority","category"))

Aggregation aggregation = Aggregation.newAggregation(project);
Tom Slabbaert
  • 21,288
  • 10
  • 30
  • 43
0
List<String> projectFieldSet = Arrays.asList("errors.category","errors.priority");
List<Field> fieldList = projectFields.stream().map(f->{return Fields.field(f,f);}).collect(Collectors.toList());        
ProjectionOperation projectAggOp = Aggregation.project(Fields.from(fieldList.toArray(new Field[fieldList.size()])));
  • 1
    While this code snippet may solve the problem, **[including an explanation](//meta.stackexchange.com/q/114762) really helps to improve the quality of your post**. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, as this reduces the readability of both the code and the explanations! – Vasiliy Artamonov Nov 19 '22 at 18:45
  • This is a simple but effective solution. https://stackoverflow.com/questions/51597458/how-to-project-array-element-field-in-spring-data-mongo-db-aggregation – state.wu Dec 09 '22 at 05:38