获取按集合属性长度排序的 MongoDB 文档列表 - Spring Boot

发布于 2025-01-10 12:42:21 字数 2383 浏览 0 评论 0原文

因此,我在 MongoDB 中有一个存储库,其中包含具有以下结构的电影:

title: String
description: String
likes: Set<String>
hates: Set<String>

likes & hates 是一个 Set,因为它们包含一个 UserIds 列表 - 其中具有这些 UserIds 的用户就是那些喜欢/讨厌这部电影。

我试图让我的服务从数据库中获取所有电影,按喜欢/讨厌的数量排序。以前,我的结构不同,喜欢/讨厌只是Integer。然后,获取所有排序的电影就很容易了:

public List<MovieDocument> getSortedMovies(SortProperty sortBy, Order order) {
    return moviesRepository.findAll(Sort.by(fromString(order.toString()), sortBy.toString()))
}

其中 sortBylikeshates,而 order>ascdesc,由 API 客户端提供。

在上述情况下,MoviesRepository 没有任何自定义方法:

@Repository
public interface MoviesRepository extends MongoRepository<MovieDocument, String> {}

既然 likeshates 都是 ,我该怎么做? >设置对象?

同样,我想要的是让所有电影按喜欢/讨厌集的大小排序。

  1. 我可以使用任何内置的 MongoRepository 方法来做到这一点吗?我看了一下,没有看到任何有用的东西。
  2. 查看其他 StackOverflow 帖子,我发现有一个选项可以使用 Aggregation 注释向我的 MoviesRepository 添加方法。这看起来像:
@Aggregation("{$project: { 'like_count': { $size: '$likes' } }}, {$sort: {'like_count': -1}}]")
List<String> getSortedMovieIdsByLikesDesc();

但是,这不会返回整个 MovieDocument,而是返回喜欢的数量。除此之外,看起来我必须为每个属性/顺序组合创建一个新的自定义方法,即 likes-asc、likes-desc、hates-asc、hates-desc。这感觉很乏味并且可扩展性不太好。

我将如何解决上述问题以返回整个文档,还有其他我没有考虑的方法吗?

编辑

我根据@rickhg12hs的输入尝试了以下操作。

@Aggregation("{$set: { like_count: { $size: $likes } }}, {$sort: {like_count: -1}}")
List<MovieDocument> getSortedMovieIdsByLikesDesc();

@Aggregation("{$set: { like_count: { $size: $likes } }}, {$sort: {like_count: 1}}")
List<MovieDocument> getSortedMovieIdsByLikesAsc();

@Aggregation("{$set: { hate_count: { $size: $hates } }}, {$sort: {hate_count: -1}}")
List<MovieDocument> getSortedMovieIdsByHatesDesc();

@Aggregation("{$set: { hate_count: { $size: $hates } }}, {$sort: {hate_count: 1}}")
List<MovieDocument> getSortedMovieIdsByHatesAsc();

不幸的是,所有这四个方法在调用时似乎都返回完全相同的内容。具体来说,它们返回数据库中无序的两个项目。

So, I have this repository in MongoDB that holds movies with this structure:

title: String
description: String
likes: Set<String>
hates: Set<String>

The likes & hates are a Set because they hold a list of UserIds - where the user with those UserIds are the ones that liked/hated the movie.

I am trying to have my service get all movies from the database, sorted by the number of likes/hates. Previously, my structure was different, and likes/hates were just Integers. Then, getting all sorted movies was easy:

public List<MovieDocument> getSortedMovies(SortProperty sortBy, Order order) {
    return moviesRepository.findAll(Sort.by(fromString(order.toString()), sortBy.toString()))
}

Where sortBy was either likes or hates and order was either asc or desc, provided by the client of the API.

In the above case, MoviesRepository didn't have any custom methods:

@Repository
public interface MoviesRepository extends MongoRepository<MovieDocument, String> {}

How am I supposed to do that now that likes and hates are Set objects?

Again, what I want is to get all movies sorted by the size of the likes/hates sets.

  1. Can I do that using any of the built-in MongoRepository methods? I had a look and didn't see anything useful.
  2. Looking at other StackOverflow posts, I saw there is an option to add methods to my MoviesRepository with an Aggregation annotation. This would look something like:
@Aggregation("{$project: { 'like_count': { $size: '$likes' } }}, {$sort: {'like_count': -1}}]")
List<String> getSortedMovieIdsByLikesDesc();

However, this does not return the whole MovieDocument, but rather it returns the number of likes. In addition to that, it looks like I'd have to create a new custom method for each property/order combination i.e. likes-asc, likes-desc, hates-asc, hates-desc. This feels tedious and not very extensible.

How would I fix the above to return whole documents and is there any other way to do this I'm not considering?

EDIT

I tried the following based on input from @rickhg12hs.

@Aggregation("{$set: { like_count: { $size: $likes } }}, {$sort: {like_count: -1}}")
List<MovieDocument> getSortedMovieIdsByLikesDesc();

@Aggregation("{$set: { like_count: { $size: $likes } }}, {$sort: {like_count: 1}}")
List<MovieDocument> getSortedMovieIdsByLikesAsc();

@Aggregation("{$set: { hate_count: { $size: $hates } }}, {$sort: {hate_count: -1}}")
List<MovieDocument> getSortedMovieIdsByHatesDesc();

@Aggregation("{$set: { hate_count: { $size: $hates } }}, {$sort: {hate_count: 1}}")
List<MovieDocument> getSortedMovieIdsByHatesAsc();

Unfortunately, all four of those methods seem to return the exact same thing when called. Specifically they return the two items that are in the database unordered.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

清秋悲枫 2025-01-17 12:42:21

你似乎几乎所有事情都做对了。这是一个我认为你想要的例子。

db.collection.aggregate([
  {
    // Count likes and hates
    "$set": {
      "likeCount": {
        "$size": "$likes"
      },
      "hateCount": {
        "$size": "$hates"
      }
    }
  },
  {
    // Most likes first, split ties with
    // least hates first
    "$sort": {
      "likeCount": -1,
      "hateCount": 1
    }
  },
  //  {
  //    "$project": {
  //      "likeCount": 0,
  //      "hateCount": 0
  //    }
  //  }
])

如果您也想删除计数,可以取消注释 "$project" 阶段。

mongoplayground.net 上尝试一下。

You seem to be doing almost everything right. Here's an example that does what I think you want.

db.collection.aggregate([
  {
    // Count likes and hates
    "$set": {
      "likeCount": {
        "$size": "$likes"
      },
      "hateCount": {
        "$size": "$hates"
      }
    }
  },
  {
    // Most likes first, split ties with
    // least hates first
    "$sort": {
      "likeCount": -1,
      "hateCount": 1
    }
  },
  //  {
  //    "$project": {
  //      "likeCount": 0,
  //      "hateCount": 0
  //    }
  //  }
])

You can uncomment the "$project" stage if you want to remove the counts too.

Try it on mongoplayground.net.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文