【问题标题】:Ruby on Rails: Concatenate results of Mongoid criterias and pagingRuby on Rails:连接 Mongoid 标准和分页的结果
【发布时间】:2014-05-13 17:55:39
【问题描述】:

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

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"]) 结果也不会有正确的顺序。我希望第一个标准的结果首先出现,然后第二个标准的结果之后。

有可能我有什么不明白的地方,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)

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

【问题讨论】:

    标签: ruby-on-rails ruby mongodb mongoid


    【解决方案1】:

    使用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.mergerelevant代码:

    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>
    

    【讨论】:

    • @GeorgoErdosi 考虑第一个标准的结果是 [2, 4, 8],第二个标准的结果是 [1, 3, 7]。我需要获取串联结果的页面:[2, 4, 8, 1, 3, 7]。所以有这样的顺序很重要,因为第二个条件的结果比第一个条件的结果相关性低(我的项目中的搜索功能需要这个)。但是如果不将 Mongoid 标准转换为数组,我就无法连接结果,这对于庞大的 Mongo 集合来说是不好的。
    • 我得到了你想要的,我只是不明白为什么会出现问题。 {"$or"=&gt;[{"name"=&gt;"John"}, {"name"=&gt;"Bob"}], "age"=&gt;"23", "gender"=&gt;"male"}{"$or"=&gt;[{"name"=&gt;"John"}, {"name"=&gt;"Bob"}], "gender"=&gt;"male", "age"=&gt;"23"} 相同。可以举个真实的例子吗?
    • 对不起,我还是不明白。数据库中的数据没有排序。如果你想得到一个有序列表,那么你需要按一些东西排序。此查询也有效:db.users.find({ name: /(bob|john)/i }).sort({ name: 1})
    • 请再看一遍我帖子的结尾。我们可能有一组名称:[ "Bob", "Good Bob", "Bob", "Bob the Best", "Evil Bob", "Bob", "Bob" ]。第一个条件得到名称的完全匹配:[ "Bob", "Bob", "Bob", "Bob" ],但第二个条件得到所有带有“Bob”子字符串的名称。我需要按相关性对结果进行排序(首先完全匹配,然后是不完全匹配):[ "Bob", "Bob", "Bob", "Bob", "Good Bob", "Bob the Best", "Evil Bob" ]。我无法按字母顺序对这些结果进行排序。它应该按相关性排序。
    猜你喜欢
    • 2013-12-23
    • 2015-08-13
    • 2013-04-10
    • 2016-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    相关资源
    最近更新 更多