Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB java API aggregate $lookup and pipeline use variable

I have 2 Collections assume as 1 to many relation, e.g. user and comments. requirement is to merge user with their latest comment as single object and return the list of the newly merged object as result.

aggregation works perfect in the mongo console

db.user.aggregate(
        {
            $addFields:{
                cId: {$toString: '$_id'},
                user: '$$ROOT'
            }
        },
        {$match: {'invitees': {$elemMatch: {'user.userId': '5e70e82532044a5e4e7cbc8d'}}}},
        {$lookup: {
                from: 'comment',
                let: {'cc': '$cId'},
                pipeline: [
                    {$match: {$expr: {$and: [{$eq: ['$$cc', '$userId']},{$gte: ['$time', '$$NOW']}]}}},
                    {$sort: {'time': -1}},
                    {$limit: 1}
                ],
                as: 'next'
            }
        },
        {$unwind: {path: '$next'}},
        {
            $project: {
                _id: 0,
                user: 1,
                next: 1,
                status: {
                    $map: {
                        input: {
                            $filter: {
                                input: '$invitees',
                                as: 'item',
                                cond: {
                                    $eq: ['$$item.user.userId', '$userId']
                                }
                            }
                        },
                        as: 'invitee',
                        in: '$$invitee.status'
                    }
                }
            }
        },
        {$unwind: {path: '$status'}},
        {$match: {$expr: {$in: ['$status', ['Accepted', 'Pending']]}}},
        {$sort: {status: 1}}
        )

I tried translating this to spring-data Aggregations but had issues with $toString. then I tried using mongodb java API Aggregations API to run this.

Arrays.asList(
        Aggregates.addFields(Arrays.asList(
            new Field<>("cId", Document.parse("{$toString: '$_id'}")),
            new Field<>("user", "$$ROOT"),
            new Field<>("userId", userId)
            )
        ),
        Aggregates.match(Filters.elemMatch("invitees", Filters.eq("user.userId", userId))),
        Aggregates.lookup(
            "comment",
            Collections.singletonList(new Variable<>("cc", "$cId")),
            Arrays.asList(
                Aggregates.match(
                    Filters.expr(
                        Filters.and(
                            Filters.eq("$$cc", "$userId")
                        ))),
                Aggregates.sort(Sorts.ascending("time")),
                Aggregates.limit(1)
            ),
            "next"
        )
    );

    AggregateIterable<Document> r = mongoClient
        .getDatabase("my-db")
        .getCollection("user")
        .aggregate(l);

receiving error:

Command failed with error 168 (InvalidPipelineOperator): 'Unrecognized expression '$$cc'' on server localhost:27017. The full response is {"ok": 0.0, "errmsg": "Unrecognized expression '$$cc'", "code": 168, "codeName": "InvalidPipelineOperator"}

using:

    <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongodb-driver</artifactId>
      <version>3.12.3</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-core -->
    <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongodb-driver-core</artifactId>
      <version>3.12.3</version>
    </dependency>


    <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>bson</artifactId>
      <version>3.12.3</version>
    </dependency>
  • java version: 8
  • mongod version: 4.2.2
  • mongo shell version: 4.2.3

any suggestion is much appreciated.

like image 334
Arthur Kazemi Avatar asked Oct 16 '22 04:10

Arthur Kazemi


People also ask

Which is the aggregate command in MongoDB that uses a pipeline approach?

The aggregate command in MongoDB is designed with specific goals of improving performance and usability for aggregation tasks. It uses a �pipeline� approach where objects are transformed as they pass through a series of pipeline operators such as $group, $match, and $sort.

What is aggregation pipeline in MongoDB?

What is the Aggregation Pipeline in MongoDB? The aggregation pipeline refers to a specific flow of operations that processes, transforms, and returns results. In a pipeline, successive operations are informed by the previous result. Let's take a typical pipeline: Input -> $match -> $group -> $sort -> output.

What is $lookup in MongoDB?

The $lookup operator is an aggregation operator or an aggregation stage, which is used to join a document from one collection to a document of another collection of the same database based on some queries. Both the collections should belong to the same databases.

How do I merge two MongoDB collections?

For performing MongoDB Join two collections, you must use the $lookup operator. It is defined as a stage that executes a left outer join with another collection and aids in filtering data from joined documents. For example, if a user requires all grades from all students, then the below query can be written: Students.


1 Answers

I thought the key point is Filters.eq can not read variables which prefix with $, like $$cc or $userId. I try to rewrite this part and it can work.

Aggregates.match(
    Filters.expr(
        Filters.and(
             new Document("$eq", Arrays.asList("$userId", "$$cc"))
        )
    )
)
like image 149
bcjohn Avatar answered Oct 19 '22 00:10

bcjohn