Return Only Specific Fields for a Query in Spring Data MongoDB – 在Spring Data MongoDB中只返回查询的特定字段

最后修改: 2022年 4月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

When using Spring Data MongoDB, we may need to restrict the properties mapped from a database object. Typically, we may need this, for example, for security reasons – to avoid exposing sensitive information stored on a server. Or also, for example, we may need to filter out part of the data displayed in a web application.

在使用Spring Data MongoDB时,我们可能需要限制从数据库对象映射的属性。通常情况下,我们可能需要这样做,例如,出于安全原因 – 避免暴露存储在服务器上的敏感信息。或者,例如,我们可能需要过滤掉网络应用中显示的部分数据。

In this short tutorial, we’ll see how MongoDB applies field restriction.

在这个简短的教程中,我们将看到MongoDB如何应用字段限制。

2. MongoDB Fields Restriction Using Projection

2.使用投影法对MongoDB字段进行限制

MongoDB uses Projection to specify or restrict fields to return from a query. However, if we’re using Spring Data, we want to apply this with MongoTemplate or MongoRepository.

MongoDB 使用投影来指定或限制要从查询中返回的字段。但是,如果我们使用 Spring Data,我们要用MongoTemplateMongoRepository来应用。

Therefore, we want to create tests cases for both MongoTemplate and MongoRepository where we can apply field restrictions.

因此,我们要为MongoTemplateMongoRepository创建测试案例,在这里我们可以应用字段限制。

3. Implementing Projection

3.实施预测

3.1. Setting up the Entity

3.1.设置实体

First, let’s create an Inventory class:

首先,让我们创建一个Inventory类。

@Document(collection = "inventory")
public class Inventory {

    @Id
    private String id;
    private String status;
    private Size size;
    private InStock inStock;

    // standard getters and setters    
}

3.2. Setting up the Repository

3.2.设置存储库

Then, to test MongoRepository, we create an InventoryRepository. We’ll also use a where condition with @Query. For instance, we want to filter for the inventory status:

然后,为了测试MongoRepository,我们创建一个InventoryRepository。我们还将使用一个where条件与@Query。例如,我们想过滤库存状态。

public interface InventoryRepository extends MongoRepository<Inventory, String> {

    @Query(value = "{ 'status' : ?0 }", fields = "{ 'item' : 1, 'status' : 1 }")
    List<Inventory> findByStatusIncludeItemAndStatusFields(String status);

    @Query(value = "{ 'status' : ?0 }", fields = "{ 'item' : 1, 'status' : 1, '_id' : 0 }")
    List<Inventory> findByStatusIncludeItemAndStatusExcludeIdFields(String status);

    @Query(value = "{ 'status' : ?0 }", fields = "{ 'status' : 0, 'inStock' : 0 }")
    List<Inventory> findByStatusIncludeAllButStatusAndStockFields(String status);

    @Query(value = "{ 'status' : ?0 }", fields = "{ 'item' : 1, 'status' : 1, 'size.uom': 1 }")
    List<Inventory> findByStatusIncludeEmbeddedFields(String status);

    @Query(value = "{ 'status' : ?0 }", fields = "{ 'size.uom': 0 }")
    List<Inventory> findByStatusExcludeEmbeddedFields(String status);

    @Query(value = "{ 'status' : ?0 }", fields = "{ 'item' : 1, 'status' : 1, 'inStock.quantity': 1 }")
    List<Inventory> findByStatusIncludeEmbeddedFieldsInArray(String status);

    @Query(value = "{ 'status' : ?0 }", fields = "{ 'item' : 1, 'status' : 1, 'inStock': { $slice: -1 } }")
    List<Inventory> findByStatusIncludeEmbeddedFieldsLastElementInArray(String status);

}

3.3. Adding the Maven Dependencies

3.3.添加Maven依赖项

We’ll also use Embedded MongoDB. Let’s add the spring-data-mongodb and de.flapdoodle.embed.mongo dependencies to our pom.xml file:

我们还将使用Embedded MongoDB L让我们添加spring-data-mongodbde.flapdoodle.embed.mongo依赖项到我们的pom.xml文件

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-mongodb</artifactId>
    <version>3.0.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>de.flapdoodle.embed</groupId>
    <artifactId>de.flapdoodle.embed.mongo</artifactId>
    <version>3.2.6</version>
    <scope>test</scope>
</dependency>

4. Test Using MongoRepository and MongoTemplate

4.使用MongoRepositoryMongoTemplate进行测试

For MongoRepository, we’ll see examples using @Query and applying Field Restriction, while for MongoTemplate, we’ll use the Query class.

对于MongoRepository,我们将看到使用@Query和应用字段限制而对于MongoTemplate我们将使用 Query类。

We’ll try to cover all different combinations of include and exclude. In particular, we’ll see how to restrict embedded fields or, more interestingly, arrays using the slice property.

我们将尝试涵盖所有不同的include和exclude的组合。特别是,我们将看到如何使用slice属性来限制嵌入式字段或更有趣的数组

For every test, we’ll add the MongoRepository example first, followed by the one for MongoTemplate.

对于每个测试,我们将首先添加MongoRepository的例子,接着是MongoTemplate的例子。

4.1. Only Include Fields

4.1.只包括字段

Let’s start by including some fields. All excluded will be null. The projection adds the _id by default:

让我们从包括一些字段开始。所有被排除的将是null。投影添加了_id默认为:

List<Inventory> inventoryList = inventoryRepository.findByStatusIncludeItemAndStatusFields("A");

inventoryList.forEach(i -> {
  assertNotNull(i.getId());
  assertNotNull(i.getItem());
  assertNotNull(i.getStatus());
  assertNull(i.getSize());
  assertNull(i.getInStock());
});

Now, let’s check out the MongoTemplate version:

现在,让我们看看MongoTemplate版本。

Query query = new Query();
 query.fields()
   .include("item")
   .include("status");

4.2. Include and Exclude Fields

4.2.列入和排除字段

This time, we’ll see examples that explicitly include some fields but exclude others – in this case, we’ll exclude the _id field:

这一次,我们将看到明确包括一些字段但排除其他字段的例子–在这种情况下,我们将排除_id字段。

List<Inventory> inventoryList = inventoryRepository.findByStatusIncludeItemAndStatusExcludeIdFields("A");

inventoryList.forEach(i -> {
   assertNotNull(i.getItem());
   assertNotNull(i.getStatus());
   assertNull(i.getId());
   assertNull(i.getSize());
   assertNull(i.getInStock());
});

The equivalent query using MongoTemplate would be:

使用MongoTemplate的等效查询将是。

Query query = new Query();
query.fields()
  .include("item")
  .include("status")
  .exclude("_id");

4.3. Only Exclude Fields

4.3.只排除字段

Let’s continue by excluding some fields. All other fields will be non-null:

让我们继续排除一些字段。所有其他字段都将是非空的。

List<Inventory> inventoryList = inventoryRepository.findByStatusIncludeAllButStatusAndStockFields("A");

inventoryList.forEach(i -> {
  assertNotNull(i.getItem());
  assertNotNull(i.getId());
  assertNotNull(i.getSize());
  assertNull(i.getInStock());
  assertNull(i.getStatus());
});

And, let’s check out the MongoTemplate version:

而且,让我们看看MongoTemplate版本。

Query query = new Query();
query.fields()
  .exclude("status")
  .exclude("inStock");

4.4. Include Embedded Fields

4.4.包括嵌入式字段

Again, including embedded fields will add them to our result:

同样,包括嵌入式字段将把它们添加到我们的结果中。

List<Inventory> inventoryList = inventoryRepository.findByStatusIncludeEmbeddedFields("A");

inventoryList.forEach(i -> {
  assertNotNull(i.getItem());
  assertNotNull(i.getStatus());
  assertNotNull(i.getId());
  assertNotNull(i.getSize());
  assertNotNull(i.getSize().getUom());
  assertNull(i.getSize().getHeight());
  assertNull(i.getSize().getWidth());
  assertNull(i.getInStock());
});

Let’s see how to do the same with MongoTemplate:

让我们看看如何用MongoTemplate做同样的事情。

Query query = new Query();
query.fields()
  .include("item")
  .include("status")
  .include("size.uom");

4.5. Exclude Embedded Fields

4.5.排除嵌入式字段

Likewise, excluding embedded fields keeps them out of our result, however, it would add the rest of the embedded fields:

同样地,排除嵌入式字段可以使它们不在我们的结果中出现,但是,它将增加其余的嵌入式字段

List<Inventory> inventoryList = inventoryRepository.findByStatusExcludeEmbeddedFields("A");

inventoryList.forEach(i -> {
  assertNotNull(i.getItem());
  assertNotNull(i.getStatus());
  assertNotNull(i.getId());
  assertNotNull(i.getSize());
  assertNull(i.getSize().getUom());
  assertNotNull(i.getSize().getHeight());
  assertNotNull(i.getSize().getWidth());
  assertNotNull(i.getInStock());
});

Let’s have a look at the MongoTemplate version:

让我们看看MongoTemplate版本。

Query query = new Query();
query.fields()
  .exclude("size.uom");

4.6. Include Embedded Fields in Array

4.6.在数组中包括嵌入式字段

Similarly to other fields, we can also add a projection of an array’s field:

与其他字段类似,我们也可以添加一个数组字段的投影。

List<Inventory> inventoryList = inventoryRepository.findByStatusIncludeEmbeddedFieldsInArray("A");

inventoryList.forEach(i -> {
  assertNotNull(i.getItem());
  assertNotNull(i.getStatus());
  assertNotNull(i.getId());
  assertNotNull(i.getInStock());
  i.getInStock()
    .forEach(stock -> {
      assertNull(stock.getWareHouse());
      assertNotNull(stock.getQuantity());
     });
  assertNull(i.getSize());
});

Let’s implement the same using MongoTemplate:

让我们使用MongoTemplate实现同样的功能。

Query query = new Query();
query.fields()
  .include("item")
  .include("status")
  .include("inStock.quantity");

4.7. Include Embedded Fields in Array using slice

4.7.使用slice将嵌入式字段包含在数组中

MongoDB can use JavaScript functions to limit the results of an array – for example, getting only the last element in an array using slice:

MongoDB可以使用JavaScript函数来限制数组的结果–例如,使用slice只获得数组的最后一个元素。

List<Inventory> inventoryList = inventoryRepository.findByStatusIncludeEmbeddedFieldsLastElementInArray("A");

inventoryList.forEach(i -> {
  assertNotNull(i.getItem());
  assertNotNull(i.getStatus());
  assertNotNull(i.getId());
  assertNotNull(i.getInStock());
  assertEquals(1, i.getInStock().size());
  assertNull(i.getSize());
});

Let’s perform the same query using MongoTemplate:

让我们使用MongoTemplate执行同样的查询。

Query query = new Query();
query.fields()
  .include("item")
  .include("status")
  .slice("inStock", -1);

5. Conclusion

5.总结

In this article, we looked at projections in Spring Data MongoDB.

在这篇文章中,我们研究了Spring Data MongoDB中的预测。

We’ve seen examples using fields, both with the MongoRepository interface and the @Query annotation, as well as with the MongoTemplate and the Query class.

我们已经看到使用fields的例子。都使用了MongoRepository接口和@Query注释。以及MongoTemplateQuery 类。

As always, the code for these examples is available over on GitHub.

一如既往,这些示例的代码可在GitHub上获得