使用 Spring Data MongoDB时,我们可能需要限制从数据库对象映射的属性。通常,出于安全原因,我们可能需要这样做—以避免暴露存储在服务器上的敏感信息。或者,例如,我们可能需要过滤掉 Web 应用程序中显示的部分数据。
在这个简短的教程中,我们将看到 MongoDB 如何应用字段限制。
MongoDB 使用Projection来指定或限制从查询返回的字段。但是,如果我们使用 Spring Data,我们希望将其与MongoTemplate或MongoRepository一起应用。
因此,我们希望为MongoTemplate和MongoRepository创建测试用例,我们可以在其中应用字段限制。
首先,让我们创建一个Inventory类:
@Document(collection = "inventory")
public class Inventory {
@Id
private String id;
private String status;
private Size size;
private InStock inStock;
// standard getters and setters
}
然后,为了测试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);
}
@Query
里的value:?0
标识匹配方法的第一个参数,?1
第二个参数
我们还将使用嵌入式 MongoDB。让我们将 spring-data-mongodb 和 de.flapdoodle.embed.mongo 依赖添加到我们的pom.xml文件中:
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-mongodbartifactId>
<version>3.0.3.RELEASEversion>
dependency>
<dependency>
<groupId>de.flapdoodle.embedgroupId>
<artifactId>de.flapdoodle.embed.mongoartifactId>
<version>3.2.6version>
<scope>testscope>
dependency>
对于MongoRepository,我们将看到使用@Query
和应用字段限制(Field Restriction)的示例,而对于MongoTemplate, 我们将使用Query 类。
我们将尝试涵盖包含和排除的所有不同组合。特别是,我们将看到如何限制嵌入字段,或者更有趣的是,使用slice
属性限制数组。
对于每个测试,我们将首先添加MongoRepository示例,然后添加MongoTemplate示例。
让我们从包括一些字段开始。所有排除的都将为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");
这一次,我们将看到明确包含某些字段但排除其他字段的示例—在这种情况下,我们将排除*_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");
让我们继续排除一些字段。所有其他字段将是非空的:
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");
同样,包含嵌入字段会将它们添加到我们的结果中:
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");
同样,排除嵌入字段会将它们排除在我们的结果之外,但是,它会添加其余的嵌入字段:
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");
与其他字段类似,我们也可以添加数组字段的投影:
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");
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());
});
让我们使用MongoTemplate执行相同的查询:
Query query = new Query();
query.fields()
.include("item")
.include("status")
.slice("inStock", -1);
在本文中,我们研究了 Spring Data MongoDB 中的投影。
我们已经看到了使用 字段 的示例,包括MongoRepository 接口和 @Query注释,以及MongoTemplate 和 Query 类。