Ruby on Rails:连接Mongoid标准和分页的结果

时间:2014-05-13 17:55:40

标签: ruby-on-rails ruby mongodb mongoid

我很确定我做错了什么。请考虑以下代码:

criteria1 = Model.where(...)
criteria2 = Model.where(...)
results = (criteria1.to_a + criteria2.to_a)[offset..(offset + count_per_page - 1)]

此代码连接两个不同标准的结果,并使用给定的偏移量(分页)获得一定数量的结果。

此代码中的问题是隐含的。 to_a方法调用实际上将条件的所有结果作为数组加载到内存中。

现在考虑一个非常庞大的集合...... to_a调用会大大减慢所有事情。

我希望做的是这样的事情:

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)

重要的是第二个标准的结果在第一个标准的结果之后

有什么线索可以用Mongoid实现吗?

谢谢!

更新

Gergo Erdosi建议使用merge方法。我试过用它,这不是我想要的。这里的问题如下:

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"])结果,也不会有正确的订单。我希望第一个标准的结果先行,然后第二个标准的结果之后
  3. 我可能无法理解某些事情并且Gergo的答案是正确的。你还有其他建议吗?感谢。

    更新2

    谢谢Gergo帮助我。让我们在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 }
    // ...
    

    注意,我不能在这里使用简单的排序,因为实际应用程序中的数据更复杂。在实际应用中,标准如下:

    // query variable is a string
    exact_match_results = Model.where(:name => query)
    inexact_match_results = Model.where(:name => /#{query}/i)
    

    所以我们不能在这里按字母顺序排序。

1 个答案:

答案 0 :(得分:4)

使用merge方法:

criteria = criteria1.merge(criteria2)
results = criteria.offset(offset).limit(count_per_page)

您可以在method description

中查看详细信息

修改:正如所指出的,merge不会产生OR查询。

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的{​​{3}}代码:

selector.merge!(criteria.selector)

这可以说明为:

irb(main):011:0> {name: 'John'}.merge({name: 'Bob'})
=> {:name=>"Bob"}

因此,就如何以结果为OR查询的方式合并两个条件提出一般性建议并不容易。但随着标准的微小变化,这是可能的。例如:

criteria1 = Model.any_of(name: 'John').where(age: '23')
criteria2 = Model.any_of(name: 'Bob').where(gender: 'male')

合并的结果是OR查询,其中包含两个名称:

irb(main):014:0> criteria1.merge(criteria2)
=> #<Mongoid::Criteria
  selector: {"$or"=>[{"name"=>"John"}, {"name"=>"Bob"}], "age"=>"23", "gender"=>"male"}
  options:  {}
  class:    Model
  embedded: false>