MongoDB字段限制教程

当使用Spring Data MongoDB时,我们可能需要限制从数据库对象映射的属性。通常,这可能是出于安全原因——避免在服务器上暴露敏感信息。或者,在Web应用中,我们也可能需要过滤掉部分数据显示。

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

1. 概述

MongoDB使用投影(Projection)来指定或限制查询结果返回的字段。然而,在Spring Data中,我们希望使用MongoTemplateMongoRepository来应用这个功能。

因此,我们将创建针对MongoTemplateMongoRepository的测试用例,以展示如何进行字段限制。

2. 使用MongoDB进行字段限制

2.1. 使用投影

MongoDB使用投影来指定查询结果返回的字段。但在Spring Data中,我们通常通过MongoTemplateMongoRepository实现这一操作。

2.2. 实现投影

2.2.1. 设置实体

首先,创建一个Inventory类:

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

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

    // standard getters and setters    
}

2.2.2. 设置仓库

然后,为了测试MongoRepository,我们需要创建一个InventoryRepository。我们还将使用@Querywhere条件,例如筛选库存状态:

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);

}

2.2.3. 添加Maven依赖

此外,我们将使用嵌入式MongoDB。在pom.xml文件中添加spring-data-mongodbde.flapdoodle.embed.mongo的依赖:

<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>

3. 使用MongoRepositoryMongoTemplate进行测试

对于MongoRepository,我们将展示使用@Query和基于JSON的字段限制的例子,而MongoTemplate将使用Query类。

我们将涵盖包括和排除所有不同组合的示例,特别是如何限制嵌套字段,或者更有趣的是,如何使用slice属性处理数组。

4.1. 只包含字段

首先,只包含一些字段,所有被排除的字段将为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());
});

现在看看MongoTemplate版本:

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

4.2. 包含和排除字段

这次,我们将看到明确包含某些字段但排除其他字段的示例——这里我们排除_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());
});

对应的MongoTemplate查询将是:

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

4.3. 只排除字段

接下来,排除一些字段。所有其他字段将不为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());
});

来看看MongoTemplate版本:

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

4.4. 包含嵌套字段

再次,包含嵌套字段将它们添加到结果中:

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());
});

让我们看看如何使用MongoTemplate实现相同的功能:

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

4.5. 排除嵌套字段

同样地,排除嵌套字段会使它们不在结果中,但会包含其余的嵌套字段

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());
});

MongoTemplate版本如下:

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

4.6. 包含数组中的嵌套字段

类似其他字段,我们也可以对数组字段进行投影:

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());
});

使用MongoTemplate实现相同的功能:

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

4.7. 使用slice处理数组中的嵌套字段

MongoDB可以使用JavaScript函数来限制数组结果,例如,仅获取数组中的最后一个元素:

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());
});

使用MongoTemplate执行相同的查询:

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

5. 总结

在这篇文章中,我们探讨了Spring Data MongoDB中的投影功能。

我们展示了如何使用fields,以及如何结合MongoRepository接口、@Query注解,以及MongoTemplateQuery类进行操作。

如往常一样,这些示例的代码可以在GitHub上找到。