我遇到需要从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条记录,所有内容中的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中执行此操作?
答案 0 :(得分:4)
也许您可以创建一个类来封装有关如何为该特定数组检索数据的详细信息,并且通过使用Mongo驱动程序,您可以跳过并限制查询选项以减少传输的数据大小。
使用这种方法,你可以使用这样的东西(namings不是很好,我没有测试代码,但你会明白这一点):
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
答案 1 :(得分: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片),应该没问题。
答案 2 :(得分: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)