У меня есть документ монго, который выглядит как объект JSON ниже. То, что я пытаюсь сделать, используя построитель запросов mongoDb, возвращает все книги от пользователя 1, которые прочитаны: false.
Например:
var query = new Query();
query.addCriteria(Criteria.where("id").is(1));
query.fields().elemMatch("books", Criteria.where("read").is(false));
вернуть пользователей 1 и первую непрочитанную книгу, но мне нужен полный список непрочитанных ящиков.
Users:[
{
id: 1,
name: 'John Doe',
books: [
{
id: 1,
title: 'The Hobbit',
read: false
},
{
id: 2,
title: 'The Lord of the Rings',
read: false
},
{
id: 3,
title: 'The Silmarillion',
read: false
}
]
},
{
id: 2,
name: 'Jane Doe',
books: []
}
}

Вы можете использовать фильтр со стадией проекта в конвейере агрегации.
db.collection.aggregate([
{
$match: {
"id": 1
}
},
{
$project: {
"books": {
$filter: {
input: "$books",
as: "b",
cond: {
$eq: ["$$b.read",false]
}
}
}
}
}
])
Запрос преобразуется в этот этап проекта конвейера.
AggregationOperation matchStage = Aggregation
.match(Criteria.where("id").is(1));
AggregationOperation projectStage = Aggregation.project()
.and(ArrayOperators.Filter
.filter("books")
.as("b")
.by(Eq.valueOf("read").equalToValue(false)))
.as("books");
List<Users> users = mongoTemplate.aggregate(
Aggregation.newAggregation(matchStage, projectStage),
Users.class, //collection class
Users.class //return type class
).getMappedResults();
обновлено, чтобы добавить этап матча.
будет ли «класс возвращаемого типа» Book.class? К сожалению, не возвращает никаких результатов.
Users Ваш коллекционный класс? Поскольку форма документа не меняется, вы можете использовать Users.class для обоих.
это работает. хотя мне пришлось изменить Eq.valueOf("read").equalToValue(false) на Eq.valueOf("b.read").equalToValue(false)
Вот решение, использующее интерфейс MongoRepository<Users,Long> и конвейер @Aggregation. Более чистый, чем построитель запросов IMO, и поддерживает поиск и пейджинг.
@Aggregation(pipeline = {
"{'$match':{'userId': ?0 }}", // filter by userid
"{'$project':{'books':1}}", // only include books in the pipeline
"{'$unwind': '$books'}", // 'unwind' the books array
"{'$match':{'books.read': ?1, 'books.title': { '$regex' : ?2, '$options' : 'i'}}}", // filter by read and title
"{'$group': {'_id': $_id, 'count': {$sum: 1}, 'books': {'$push': '$books'}}}", // group by full count (for pagination) and books
"{'$project':{'_id': 0, 'count': 1, 'books': {$slice: ['$books', ?3, ?4]}}}", // define what to return (count, subset of books)
})
List<BookList> findByUserIdAndRead(Long userId, boolean read, String filter, long skip, int size);
Класс BookList
public class BookList {
private Integer count;
private List<Book> books;
}
как бы вы ограничились только пользователем 1?