Ruby on Rails:合并Mongoid标准和分页的结果 [英] Ruby on Rails: Concatenate results of Mongoid criterias and paging

查看:63
本文介绍了Ruby on Rails:合并Mongoid标准和分页的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很确定自己做错了什么.考虑以下代码:

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

所以这是两个问题:

  1. merge不产生OR,它用第二个覆盖第一个查询的公用键;
  2. 即使我们使用Model.or({ :name => "John" }, { :name => "Bob" })Model.in(:name => ["John", "Bob"]),结果也不会具有正确的顺序.我希望第一个条件的结果优先,然后第二个条件的结果之后.
  1. merge doesn't produce OR, it overrides common keys of the first query with the second;
  2. Even if we use Model.or({ :name => "John" }, { :name => "Bob" }) or Model.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆