1. Общ преглед
Когато използваме Spring Data MongoDB, може да се наложи да ограничим свойствата, картографирани от обект на база данни. Обикновено може да се нуждаем от това, например от съображения за сигурност – за да избегнем разкриването на чувствителна информация, съхранявана на сървър. Или също, например, може да се наложи да филтрираме част от данните, показани в уеб приложение.
В този кратък урок ще видим как MongoDB прилага ограничение на полето.
2. Ограничение на полетата на MongoDB с помощта на проекция
MongoDB използва Projection, за да посочи или ограничи полета за връщане от заявка . Ако обаче използваме Spring Data, искаме да приложим това с MongoTemplate или MongoRepository .
Затова искаме да създадем тестови случаи и за MongoTemplate и MongoRepository където можем да приложим ограничения на полето.
3. Внедряване на проекция
3.1. Настройка на обекта
Първо, нека създадем инвентар клас:
@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. Настройка на хранилището
След това, за да тествате MongoRepository , създаваме InventoryRepository . Ще използваме и къде условие с @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. Добавяне на зависимостите на Maven
Ще използваме и Embedded MongoDB. Нека добавим spring-data-mongodb и de.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. Тествайте с MongoRepository и MongoTemplate
За MongoRepository , ще видим примери с @Query и прилагане на ограничение на полето, докато за MongoTemplate , ще използваме Запитване клас.
Ще се опитаме да обхванем всички различни комбинации от включване и изключване. По-специално, ще видим как да ограничим вградените полета или, което е по-интересно, масивите с помощта на срез имота .
За всеки тест ще добавим MongoRepository първо пример, последван от този за MongoTemplate .
4.1. Включете само полета
Нека започнем с включването на някои полета. Всички изключени ще бъдат нулеви . Проекцията добавя _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. Само изключване на полета
Нека продължим, като изключим някои полета. Всички останали полета ще бъдат ненулеви:
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. Включете вградени полета в масив с помощта на срез
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);