As informed by @loicmathieu there is no direct way to do populate() in quarkus as you are able to do it in mongoose.
If you set mongoose.set('debug', true) then you can check in console that first
find query on collection is executed
in query on reference collection is executed.
Example json document:
{
"_id" : ObjectId("612f6c5c081d0796761ec2e4"),
"content" : "cannot restart Mongodb even when memory space is available",
"created_by" : ObjectId("612cb8608d22afb0e404adf6"),
"tags" : [
ObjectId("612f6c5c081d0796761ec2e2")
],
"createdAt" : ISODate("2021-09-01T12:04:44.361Z"),
"updatedAt" : ISODate("2021-09-10T05:30:51.384Z"),
"__v" : 2,
"likes" : [],
"replies" : []
}
Tag Document
{
"_id" : ObjectId("612f6c5c081d0796761ec2e2"),
"name" : "mongodb",
"created_by" : ObjectId("612cb8608d22afb0e404adf6"),
"createdAt" : ISODate("2021-09-01T12:04:44.358Z"),
"updatedAt" : ISODate("2021-09-01T12:04:44.358Z"),
"__v" : 0
}
And finally user document
{
"_id" : ObjectId("612cb8608d22afb0e404adf6"),
"username" : "silentsudo",
"password" : "sosimplebcrypted",
"name" : "Ashish",
"__v" : 0
}
To get all documents i execute following query:
exports.getRecentTweets = async () => {
return Tweet.find()
.populate('created_by', '_id name username mediaId')
.populate('tags', '_id name')
.sort({'createdAt': -1});
}
Mongoose internally executing this in the following manner
Mongoose: tweets.find({}, { sort: { createdAt: -1 }, projection: {} })
Mongoose: users.find({ _id: { '$in': [ new ObjectId("612cb8608d22afb0e404adf6"), new ObjectId("612f57ca9ea5c5cabbd89b38"), new ObjectId("612f57ae9ea5c5cabbd89b32"), new ObjectId("6134cb55328b793a1dacb9c2"), new ObjectId("612cb86a8d22afb0e404adfc") ] }}, { skip: undefined, limit: undefined, perDocumentLimit: undefined, projection: { _id: 1, name: 1, username: 1, mediaId: 1 }})
Mongoose: tags.find({ _id: { '$in': [ new ObjectId("6144d9ef02f6da33f39fe134"), new ObjectId("612f6c5c081d0796761ec2e2"), new ObjectId("612f6e9f081d0796761ec343"), new ObjectId("612dc4623c401072c8098a84"), new ObjectId("612f6cb1081d0796761ec2f1"), new ObjectId("612f6c72081d0796761ec2eb"), new ObjectId("612f6e5b081d0796761ec337"), new ObjectId("612f6e42081d0796761ec330"), new ObjectId("612f6df9081d0796761ec320"), new ObjectId("612f6e0a081d0796761ec328"), new ObjectId("612f6de8081d0796761ec316"), new ObjectId("612f6de8081d0796761ec319"), new ObjectId("612f6dc9081d0796761ec30c"), new ObjectId("612f6dc9081d0796761ec30f"), new ObjectId("612f6db0081d0796761ec303"), new ObjectId("612f6db0081d0796761ec306"), new ObjectId("612f6cc7081d0796761ec2f7") ] }}, { skip: undefined, limit: undefined, perDocumentLimit: undefined, projection: { _id: 1, name: 1 }})
I was experimenting ab tool for benchmarking. So i decided to go with quarkus as i never built any production application using it before. So i quickly created model and arrived at this thread only to know there is no direct way to achieve populate in java based driver. But we can use $lookup with aggregation pipeline what we need. First and simplest way is to use mongodb compass tool to build the query and then simply export the result in java language. my json query to produce output similar to populate query looked like this.
[
{
'$unwind': {
'path': '$tags',
'preserveNullAndEmptyArrays': true
}
}, {
'$lookup': {
'from': 'tags',
'localField': 'tags',
'foreignField': '_id',
'as': 'tweet_tags'
}
}, {
'$unwind': {
'path': '$tweet_tags',
'preserveNullAndEmptyArrays': true
}
}, {
'$group': {
'_id': '$_id',
'content': {
'$first': '$content'
},
'user': {
'$first': '$created_by'
},
'tags': {
'$push': '$tweet_tags'
}
}
}, {
'$lookup': {
'from': 'users',
'localField': 'user',
'foreignField': '_id',
'as': 'createdByUser'
}
}, {
'$unwind': {
'path': '$createdByUser',
'preserveNullAndEmptyArrays': false
}
}, {
'$set': {
'createdByUser.password': null
}
}
]
Which when converted into equivalent java gives
public static List<Document> getTweetByUserQuery() {
return Arrays.asList(new Document("$unwind",
new Document("path", "$tags")
.append("preserveNullAndEmptyArrays", true)),
new Document("$lookup",
new Document("from", "tags")
.append("localField", "tags")
.append("foreignField", "_id")
.append("as", "tweet_tags")),
new Document("$unwind",
new Document("path", "$tweet_tags")
.append("preserveNullAndEmptyArrays", true)),
new Document("$group",
new Document("_id", "$_id")
.append("content",
new Document("$first", "$content"))
.append("user",
new Document("$first", "$created_by"))
.append("tags",
new Document("$push", "$tweet_tags"))),
new Document("$lookup",
new Document("from", "users")
.append("localField", "user")
.append("foreignField", "_id")
.append("as", "createdByUser")),
new Document("$unwind",
new Document("path", "$createdByUser")
.append("preserveNullAndEmptyArrays", false)),
new Document("$set",
new Document("createdByUser.password",
new BsonNull())));
}
quarkus Controller code as below:
@Path("/hello")
public class ExampleResource {
@Inject
private MongoClient mongoClient;
@GET
@Produces(MediaType.APPLICATION_JSON)
public Map<Object, Object> hello() {
MongoCollection<Tweet> tweetsCollection = mongoClient.getDatabase("simple-node-js")
.getCollection("tweets", Tweet.class);
List<Document> groupByQueryUser = Tweet.GroupByIdDetails.getTweetByUserQuery();
MongoCollection<Document> collections = mongoClient.getDatabase("simple-node-js")
.getCollection("tweets");
AggregateIterable<Document> aggregate = collections.aggregate(groupByQueryUser);
List<Document> data = new ArrayList<>();
for (Document value : aggregate) {
System.out.println(value);
data.add(value);
}
return Map.of("size", data.size(), "data", data);
}
}
There are many other things needs to be done with the response like removing unwanted fields but this ways i could achieve same result on node+mongoose api(except few more things which i skipped here)