简体   繁体   English

Spring Boot Data和MongoDB - 过滤子文档数组查询

[英]Spring Boot Data and MongoDB - Filter Subdocument Array Query

I am attempting to use Spring to Query a Mongo repository and filter an array subdocument. 我试图使用Spring来查询Mongo存储库并过滤数组子文档。 I have referenced how to filter array in subdocument with mongodb , but was wondering if there was a more appropriate or java structured method to do so using Spring. 我已经引用了如何使用mongodb过滤子文档中的数组 ,但是想知道是否有更合适的或java结构化的方法来使用Spring。

I am currently using the shorthand repository interface notation, but I am getting back the complete document with the array not filtered. 我目前正在使用速记存储库接口表示法,但我正在使用未过滤的数组返回完整的文档。

PersonRepository.java PersonRepository.java

@Repository
public interface PersonRepository extends MongoRepository <Person, String> {
    List<Person> findByAddressZipCode(@Param("zip") int zip);
}

Person.java Person.java

@Document
public class Person {
    @Id
    private String id;

    private String firstName;
    private String lastName;
    private List<Address> address;
}

Address.java Address.java

public class Address {
    private int zip;
}

Sample Input 样本输入

{
 "firstName":"George",
 "lastName":"Washington",
 "address":[{
     "zip":"12345"
  },{
     "zip":"98765"
  },{
     "zip":"12345"
  }]
}

Expected Output 预期产出

{
 "firstName":"George",
 "lastName":"Washington",
 "address":[{
     "zip":"12345"
  },{
     "zip":"12345"
  }]
}

Actual Output 实际产出

{
 "firstName":"George",
 "lastName":"Washington",
 "address":[{
     "zip":"12345"
  },{
     "zip":"98765"
  },{
     "zip":"12345"
  }]
}

Well, In Spring Data such kind of queries is not trivial . 那么,在Spring Data中,这种查询并不trivial

Bad news: 坏消息:
Spring Data Repository does not have solution for MongoDB Aggregation . Spring Data Repository没有MongoDB Aggregation解决方案。 So, you cannot implement in MongoRepository any method to do so, like aggregateBy... 所以,你不能在MongoRepository中实现任何方法,比如aggregateBy...

Good news: 好消息:
Spring Data provides MongoTemplate class which allows you to execute complex queries, like you would do in standard MongoDB shell. Spring Data提供了MongoTemplate类,允许您执行复杂查询,就像在标准MongoDB shell中一样。

So, as you just want to exclude subdocument that does not match some condition, we need to define the aggregate pipelines . 因此,由于您只想exclude与某些条件不匹配的子文档,我们需要定义聚合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: MongoDB聚合将是:

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"
    }
]


Now, in Spring Data way, you need add some changes in your project: 现在,以Spring Data方式,您需要在项目中添加一些更改:

First, find your mongo-config.xml where you need to add: 首先,找到你需要添加的mongo-config.xml

 <!-- 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. MongoTemplate是Spring的MongoDB支持的中心类,提供与数据库交互的功能集。 The template ... provides a mapping between your domain objects and MongoDB documents . 模板...提供域对象MongoDB文档之间的映射。 More info 更多信息

Second, in your @Service class, add following code to be loaded in @PostConstruct 其次,在@Service类中,添加以下代码以加载到@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! 注意: PersonAddress都应该有默认的空构造函数!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM