MongoDB字段限制教程
当使用Spring Data MongoDB时,我们可能需要限制从数据库对象映射的属性。通常,这可能是出于安全原因——避免在服务器上暴露敏感信息。或者,在Web应用中,我们也可能需要过滤掉部分数据显示。
在这个简短的教程中,我们将了解MongoDB如何应用字段限制。
1. 概述
MongoDB使用投影(Projection)来指定或限制查询结果返回的字段。然而,在Spring Data中,我们希望使用MongoTemplate
或MongoRepository
来应用这个功能。
因此,我们将创建针对MongoTemplate
和MongoRepository
的测试用例,以展示如何进行字段限制。
2. 使用MongoDB进行字段限制
2.1. 使用投影
MongoDB使用投影来指定查询结果返回的字段。但在Spring Data中,我们通常通过MongoTemplate
或MongoRepository
实现这一操作。
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
。我们还将使用@Query
的where
条件,例如筛选库存状态:
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-mongodb
和de.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. 使用MongoRepository
和MongoTemplate
进行测试
对于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
注解,以及MongoTemplate
和Query
类进行操作。
如往常一样,这些示例的代码可以在GitHub上找到。