I am attempting to use Spring to Query a Mongo repository and filter an array subdocument. I have referenced how to filter array in subdocument with mongodb, but was wonder
Well, In Spring Data such kind of queries is not trivial
.
Bad news:
Spring Data Repository does not have solution for MongoDB Aggregation
. So, you cannot implement in MongoRepository any method to do so, like aggregateBy...
Good news:
Spring Data provides MongoTemplate
class which allows you to execute complex queries, like you would do in standard MongoDB shell.
So, as you just want to exclude
subdocument that does not match some condition, we need to define the aggregate pipelines
.
I assume:
zip codes are Numeric (In your example is string)
And, to exclude subdocument, we filter by `zip`
There is no any other filter
MongoDB aggregation would be:
db.person.aggregate([
{$unwind: "$address"},
{$match: {"address.zip": 12345}},
{$group: { _id: { "firstName":"$firstName", "lastName":"$lastName", _id:"$_id" }, address: { $push: "$address" } } },
{$project: {_id:0, "firstName":"$_id.firstName", "lastName":"$_id.lastName", "address": "$address"}}
])
If all filters success, we got:
[
{
"address" : [
{
"zip" : 12345
},
{
"zip" : 12345
}
],
"firstName" : "George",
"lastName" : "Washington"
}
]
First, find your mongo-config.xml
where you need to add:
<!-- Define the mongoDbFactory with your database Name -->
<mongo:db-factory uri="mongodb://user:pass@localhost:27017/db"/>
<!-- Define the MongoTemplate -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
MongoTemplate
is the central class of the Spring’s MongoDB support providing feature sets to interact with the database. The template ...
provides a mapping between your domain objects and MongoDB documents. More info
Second, in your @Service
class, add following code to be loaded in @PostConstruct
@Autowired
private MongoOperations mongoOperations;
...
public List<Person> findByAddressZipCode(int zip) {
List<AggregationOperation> list = new ArrayList<AggregationOperation>();
list.add(Aggregation.unwind("address"));
list.add(Aggregation.match(Criteria.where("address.zip").is(zip)));
list.add(Aggregation.group("firstName", "lastName").push("address").as("address"));
list.add(Aggregation.project("firstName", "lastName", "address"));
TypedAggregation<Person> agg = Aggregation.newAggregation(Person.class, list);
return mongoOperations.aggregate(agg, Person.class, Person.class).getMappedResults();
}
Note: Both, Person
and Address
should have default empty constructor!