3

我有一种情况,我需要从 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如评论中提到的那样应用我的查询时,不会产生任何影响。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 的结果,只返回应用了标准的所有记录 - 15ms

"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}}

总而言之,这不是一个有效的选择。即使使用大型数据集,单独返回每个数据集也需要 <15ms,所以我认为我需要完成的是一种将条件合并在一起的方法,以便针对 Mongo 运行单个查询,从而允许在 db 上发生分页,它应该是。

在 SQL 中,我会做一些大致类似的事情

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

是否可以在 Mongo 中执行此操作?

4

3 回答 3

4

也许您可以创建一个类来封装有关如何为该特定数组检索数据的详细信息,并且通过使用 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
于 2012-09-13T17:32:57.143 回答
2

不过,这可能是一种粗略的方法:

# 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 条),应该没问题。

于 2012-09-10T22:28:36.123 回答
0

试试这个:

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)
于 2015-01-15T12:52:14.957 回答