【问题标题】:Union two mongo results联合两个mongo结果
【发布时间】:2012-09-01 12:01:56
【问题描述】:

我有一种情况,我需要从 mongo 返回一个对象集合,但需要使用两个查询来获取结果。这些结果的顺序很重要,因为它们是分页的。

这是第一个查询:(基于类别和价格范围的列表)

my_listings = MoListing.where(criteria_a)

第二个查询需要使用第一个查询的结果作为过滤器。就像这样:

everything_else = MoListing.where(criteria_b)

然后联合结果:

my_listings << everything_else

最后,返回分页结果:

my_listings.page(1).per(25)

似乎我的部分问题是 mongo 查询在需要之前不会执行。有没有办法让我在给定点触发查询的执行?或者在构建这个结果集时我应该采取其他方法吗?

更新更多信息

我看到的行为是返回的只是listings 中的结果。我还确认everything_else 确实包含预期的记录(my_listings 中有 48 条记录,everything_else 中有 52 条记录)。

.all 应用于我在 cmets 中提到的查询时,不会产生任何影响。 puts listings.inspect 导致

10:57:00 web.1   |    #<Mongoid::Criteria
10:57:00 web.1   |    selector: {"price"=>{"$gte"=>25, "$lte"=>75}},
10:57:00 web.1   |    options:  {},
10:57:00 web.1   |    class:    MoListing,
10:57:00 web.1   |    embedded: false>

但是,listings.count 确实会导致 48。我只是错过了一些合并这些结果的愚蠢简单方法吗?一旦我在一个集合中得到结果,这将如何影响随后的分页功能。我正在使用kaminari 进行分页。

更新 2

根据下面的答案和我自己的反复试验,我发现 to_a 是一个解决方案,但不是一个理想的解决方案。这确实起作用:

#merge the results together as an Array
results = (listings.to_a | everything_else.to_a)

这导致通过 Kaminari 的分页必须更改,因为我们不再使用 mongo 标准,而是使用标准数组。这是新的分页方法:

Kaminari.paginate_array(results).page(page).per(per_page)

使用包含 100 条记录的小型数据集,这很好而且花花公子 - 54 毫秒

"debug":{"success":true,"pre_render_duration":54.808775999999995,"overall_duration":86.36554100000001,"count":25},"pagination":{"total_pages":4,"current_page":1}}

但是,使用 .to_a 方法来组合这些数据时,我发现使用更大的数据集时,时间明显变慢。虽然这些例子并不完全是一对苹果,但如此大的差异表明 to_a 返回所有内容的问题,迫使 Kaminari 处理更多的实际数据:

我没有 to_a 的结果,只返回应用了条件的所有记录 - 15 毫秒

"debug":{"success":true,"pre_render_duration":15.107164,"overall_duration":18.267599,"count":25},"pagination":{"total_pages":81,"current_page":1}}

我的结果与 to_a,合并两个结果集 - 415ms

"debug":{"success":true,"pre_render_duration":415.258199,"overall_duration":450.66537800000003,"count":25},"pagination":{"total_pages":81,"current_page":1}}

总而言之,这不是一个有效的选项。即使使用大型数据集,单独返回每个数据集也需要

在 SQL 中,我会做类似的事情

select
  *
from
  listings
where
  field = "blah"
union all
select
  *
from
  listings
where
  field <> "blah"

在 Mongo 中可以做到这一点吗?

【问题讨论】:

  • 你的问题是什么意思?你得到不完整的结果吗?你只得到列表?是这样吗?请详细说明这里有什么问题?
  • 在 Active Record 中触发执行的解决方案是使用 .all 函数。只需在查询后添加.all,它们就会在那里自行执行,不会有延迟加载。

标签: ruby-on-rails ruby mongodb sinatra kaminari


【解决方案1】:

试试这个:

my_listings = MoListing.where(criteria_a)
everything_else = MoListing.where(criteria_b)
all_listings = MoListing.or(my_listings.selector).or(everything_else.selector).page(1).per(25)

【讨论】:

    【解决方案2】:

    也许您可以创建一个类来封装有关如何为该特定数组检索数据的详细信息,并且通过使用 Mongo 驱动程序,您可以跳过和限制查询选项以减少传输的数据大小。

    使用这种方法,你可以使用这样的东西(命名不是很好,我没有测试代码,但你会明白这一点):

    class MoListingDataRetriever
      def initialize(page_size)
        @page_size = page_size / 2 #since you'll have two queries
        driver_instance = MoListing.db #just an exemple. You could use any of your classes that are mongo documents to do this
        @collection_driver = driver_instance.collection("mo_listing") #or whatever you collection name is on mongo
      end
    
      def retrieve_mo_listings(query_param_a, query_param_b, current_page)
        query_options = {
          limit: @page_size,
          page: current_page,
          skip: (@page_size * (current_page - 1)) #to skip a number of records already retrieved from the query
        }
        results_from_query_a = @driver_instance.find(query_param_a, query_options)
        results_from_query_b = @driver_instance.find(query_param_a, query_options)
        results_from_query_b.to_a.concat(results_from_query_b.to_a)    
      end
    end
    

    【讨论】:

    • 这最终成为了答案。我不需要使用 Kaminari 进行分页,而是需要自己滚动并执行逻辑来确定是否甚至需要来自任一结果集的记录。不理想,但它确实有效并且速度相对较快。
    【解决方案3】:

    虽然这可能是一种粗略的方式:

    # Let us say the listings is obtained using listing_query_params
    listings = MoListing.where(listing_query_params)
    
    # and everything else is from everything_else_query_params
    everything_else = MoListing.where(everything_else_query_params)
    
    results = [listings.to_a, everything_else.to_a].flatten
    
    results.page(1).per(25)
    

    这是你想要的吗?我已经在我的一个 mongoid 模型上尝试过它,并且似乎以这种方式工作。

    PS:但是 .to_a 对性能有影响 - 整个结果集都被提取和合并。但是看看你提到的记录数量(大约 50 条),应该没问题。

    【讨论】:

    • 这与我提出的解决方案非常相似。唯一的区别是我使用了 Array union vs flatten - results = (listings | everything_else)。这对 Kaminari 的工作方式产生了影响,因此我将在更多测试后更新我的帖子以澄清细节。此外,在现实世界中的使用将使用 1000 条记录,因此您提出了一个关于性能的好观点。在将此答案标记为正确并授予赏金之前,我将使用更多数据进行测试以查看它是否仍然可以接受。似乎应该有比 .to_a 更好的解决方案。
    • 我对此的回答已添加到原始问题中。
    • 我可以写得很好 results = MoListing.any_of(listing_query_params, everything_else_query_params) 但您也需要对结果进行排序,所以我不确定如何在同一个查询中对它们进行排序!
    猜你喜欢
    • 1970-01-01
    • 2019-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多