I am new to MongoDB. I have a collection like:
[
{
"_id":"1",
"travels":[
{
"_id":"1"
},
{
"_id":"2"
},
{
"_id":"3"
}
]
},
{
"_id":"2",
"travels":[
{
"_id":"4"
},
{
"_id":"5"
},
{
"_id":"6"
}
]
}
]
My Java model is more or less like this (using public modifiers for simplicity):
public class TravelsDocument<T> {
@Id
public String id;
public List<Travel> travels;
}
public class Travel {
public String id;
}
All ids in travels arrays are unique. In my Spring application I would like to select a single travel
element matching specified id.
I've finally figured out how to do that in MongoDB. First, I applied a filter:
{travels: {$elemMatch: { _id: 'someId'}}}
and then a projection:
{
"_id":0,
"travel":{
"$arrayElemAt":[
"$travels",
{
"$indexOfArray":[
"$travels._id",
"someId"
]
}
]
}
}
Then I tried to use it in an aggregation:
@Aggregation(pipeline = {
"{'$match' :{'travels': {$elemMatch: { _id: :#{#id}}}}}",
"{'$project' :{_id:0, 'travel':{$arrayElemAt: [ '$travels', {$indexOfArray :['$travels._id', :#{#id}]} ] }}}"})
Mono<Travel> findSingleTravelByTenantAndId(@Param("id") String id);
The issue is that this method doesn't return my Travel
object but rather org.bson.Document
object. Is there a way to return my custom object using Aggregation annotation? Is this the way to go?
The final projection stage of the aggregation pipeline would return documents as { travel: { _id: 123 } }
- note the outer travel
field. But that's not the structure of your Travel
class, it only defines id
(which I presume maps to _id
), and probably other fields/members.
So you need to add a $replaceWith
stage:
{ $replaceWith: "$travel" }
Or better, remove the project stage and use replaceWith instead:
{
$replaceWith: {
"$arrayElemAt":[
"$travels",
{
"$indexOfArray":[
"$travels._id",
"someId"
]
}
]
}
}
So the full pipeline would be:
db.collection.aggregate([
{
$match: {
travels: { $elemMatch: { _id: "someId" } }
}
},
{
$replaceWith: {
$arrayElemAt: [
"$travels",
{ $indexOfArray: ["$travels._id", "someId"] }
]
}
}
])