Sping Boot MongoDb有条件地返回文档列表中的项

9o685dep  于 2022-12-12  发布在  Go
关注(0)|答案(2)|浏览(100)

我有一个mongo文档,看起来像下面的JSON对象。我尝试使用mongoDb Query builder返回用户1读过的所有书籍:错误。
例如:

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: []
  }
}
x4shl7ld

x4shl7ld1#

您可以在聚合管道中将filter与项目阶段一起使用。

db.collection.aggregate([
  {
    $match: {
      "id": 1
    }
  },
  {
    $project: {
      "books": {
        $filter: {
          input: "$books",
          as: "b",
          cond: {
            $eq: ["$$b.read",false]
          }
        }
      }
    }
  }
])

Mongo Playground
查询将转换到管道的此项目阶段。

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

wmvff8tz2#

这是一个使用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;
}

相关问题