Ruby on Rails:合并Mongoid标准和分页的结果 [英] Ruby on Rails: Concatenate results of Mongoid criterias and paging
问题描述
我很确定自己做错了什么.考虑以下代码:
I'm pretty sure that I'm doing something wrong. Consider the following code:
criteria1 = Model.where(...)
criteria2 = Model.where(...)
results = (criteria1.to_a + criteria2.to_a)[offset..(offset + count_per_page - 1)]
此代码将两个不同条件的结果连接起来,并在给定偏移量(分页)下获得一定数量的结果.
This code concatenates results of two different criterias and get a certain number of results with a given offset (paging).
此代码中的问题是隐式的. to_a
方法调用实际上将条件的所有结果作为数组加载到内存中.
The problem in this code is implicit. The to_a
method call actually loads all results of a criteria to the memory as an array.
现在考虑一个非常庞大的集合... to_a
调用会大大降低所有内容的速度.
Now consider a really huge collection... The to_a
call slows all things down dramatically.
我想做的是这样的:
criteria1 = Model.where(...)
criteria2 = Model.where(...)
# A criteria, which returns results of the first criteria concatenated with results of the second criteria
criteria = criteria1 + criteria2
results = criteria.offset(offset).limit(count_per_page)
重要的是,第二个条件的结果排在第一个条件的结果之后.
The important thing is that results of the second criteria goes after results of the first criteria.
有什么线索可以证明使用Mongoid可以实现吗?
Any clues how is it possible to achieve with Mongoid?
谢谢!
更新
Gergo Erdosi建议使用merge
方法.我试图使用它,这不是我想要的.这里的问题如下:
Gergo Erdosi suggested to use merge
method. I've tried to use this and it is not what I'm looking for. The problem here is the following:
criteria1 = Model.where(:name => "John", :age => "23")
criteria2 = Model.where(:name => "Bob", :gender => "male")
criteria = criteria1.merge(criteria2)
p criteria.selector
# prints: { "name" => "Bob", :age => 23, :gender => "male" }
所以这是两个问题:
-
merge
不产生OR,它用第二个覆盖第一个查询的公用键; - 即使我们使用
Model.or({ :name => "John" }, { :name => "Bob" })
或Model.in(:name => ["John", "Bob"])
,结果也不会具有正确的顺序.我希望第一个条件的结果优先,然后第二个条件的结果之后.
merge
doesn't produce OR, it overrides common keys of the first query with the second;- Even if we use
Model.or({ :name => "John" }, { :name => "Bob" })
orModel.in(:name => ["John", "Bob"])
results won't have the right order. I wish results of the first criteria go first and then results of the second criteria go after.
我可能不了解某些内容,而Gergo的回答是正确的.你还有其他建议吗?谢谢.
It is possible that I don't understand something and Gergo's answer is right. Do you have any other ideas? Thanks.
更新2
谢谢Gergo在这里帮助我.让我们在Mongo shell中尝试一个简单的示例:
Thank you Gergo for helping me out here. Let's try a simple example in Mongo shell:
// Fill out test db with some simple documents.
for (var i = 0; i < 10; ++i) { db.users.insert({ name: i % 2 ? "John" : "Bob", age: Math.round(Math.random() * 100) }); }
// These queries give me the same order of documents.
db.users.find({ name: { $in: ["Bob", "John"] } });
db.users.find({ $or: [{ name: "Bob" }, { name: "John" }] });
// Like this:
{ "_id" : ObjectId("53732076b110ab9be7619a8e"), "name" : "Bob", "age" : 69 }
{ "_id" : ObjectId("53732076b110ab9be7619a8f"), "name" : "John", "age" : 63 }
{ "_id" : ObjectId("53732076b110ab9be7619a90"), "name" : "Bob", "age" : 25 }
{ "_id" : ObjectId("53732076b110ab9be7619a91"), "name" : "John", "age" : 72 }
// ...
// But I wish to get concatenated results of these queries:
db.users.find({ name: "Bob" });
db.users.find({ name: "John" });
// Like this (results of the first criteria go first):
{ "_id" : ObjectId("53732076b110ab9be7619a8e"), "name" : "Bob", "age" : 69 }
{ "_id" : ObjectId("53732076b110ab9be7619a90"), "name" : "Bob", "age" : 25 }
// ...
{ "_id" : ObjectId("53732076b110ab9be7619a8f"), "name" : "John", "age" : 63 }
{ "_id" : ObjectId("53732076b110ab9be7619a91"), "name" : "John", "age" : 72 }
// ...
请注意,我不能在这里使用简单的排序,因为实际应用程序中的数据更加复杂.在实际的应用程序中,条件如下所示:
Notice, that I cannot use a simple sorting here, because the data in the real application is more complex. In the real application criterias look like these:
// query variable is a string
exact_match_results = Model.where(:name => query)
inexact_match_results = Model.where(:name => /#{query}/i)
所以我们不能在这里按字母顺序排序.
So we cannot just sort alphabetically here.
推荐答案
使用merge
方法:
criteria = criteria1.merge(criteria2)
results = criteria.offset(offset).limit(count_per_page)
您可以在方法中查看详细信息说明.
编辑:如前所述,merge
不会产生OR查询.
Edit: As pointed out, merge
doesn't produce an OR query.
irb(main):010:0> Model.where(name: 'John').merge(Model.where(name: 'Bob'))
=> #<Mongoid::Criteria
selector: {"name"=>"Bob"}
options: {}
class: Model
embedded: false>
在这种情况下,这不是预期的行为.原因是merge
使用Hash.merge
以这种方式运行. Criteria.merge
中的相关代码:
Which is not the expected behavior in this case. The reason is that merge
uses Hash.merge
which behaves this way. The relevant code from Criteria.merge
:
selector.merge!(criteria.selector)
这可以说明为:
irb(main):011:0> {name: 'John'}.merge({name: 'Bob'})
=> {:name=>"Bob"}
因此,要对如何以结果为OR查询的方式合并两个条件给出一般性建议并不容易.但是只要对标准稍作更改,就有可能.例如:
Because of this, it's not easy to give a general advice on how to merge two criteria in a way that the result is an OR query. But with a little change in the criteria, it's possible. For example:
criteria1 = Model.any_of(name: 'John').where(age: '23')
criteria2 = Model.any_of(name: 'Bob').where(gender: 'male')
合并的结果是一个包含两个名称的OR查询:
The result of the merge is an OR query which contains both names:
irb(main):014:0> criteria1.merge(criteria2)
=> #<Mongoid::Criteria
selector: {"$or"=>[{"name"=>"John"}, {"name"=>"Bob"}], "age"=>"23", "gender"=>"male"}
options: {}
class: Model
embedded: false>
这篇关于Ruby on Rails:合并Mongoid标准和分页的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!