问题与mongoId的active_ord的any_of实现有关

时间:2016-01-12 11:57:08

标签: ruby-on-rails arrays ruby mongoid

我有我的设置模型这样的数据

[52] pry(main)> Setting.all.to_a
    => [#<Setting _id: 561cc75b25917fdc2300003c, courses: ["521c4578ef8b6038ba000069", "55f909c825917f1ac000003e"], user_id: "55f908d725917f5157000036">,
     #<Setting _id: 563b322425917f8117000025, courses: ["561cddbb25917fa26e0000b9", "5614e62225917fbb1300005f", "5617a6bd25917f7aa100005d", "560ca6b325917f158d000002", "5617a5f325917ff38c000036", "5632eebc25917f1ace000038", "55f909c825917f1ac000003e", "521c4578ef8b6038ba000069"], user_id: "56125d7b25917fb8c0000001">,
     #<Setting _id: 5641d3b125917fa02f000009, courses: ["5614e62225917fbb1300005f", "55f909c825917f1ac000003e", "521c4578ef8b6038ba000069"], user_id: "5264b629ef8b604f96000001">,
     #<Setting _id: 565d541925917f10da000013, courses: ["561cddbb25917fa26e0000b9", "5614e62225917fbb1300005f", "5617a6bd25917f7aa100005d", "5617a5f325917ff38c000036", "560ca6b325917f158d000002", "55f909c825917f1ac000003e", "5632eebc25917f1ace000038", "521c4578ef8b6038ba000069"], user_id: "565d53f525917f10da000012">]

现在我想要一系列具有特定课程的设置

    pry(main)> course = Course.find("5614e62225917fbb1300005f")
    pry(main)> Setting.any_of(courses: course.id).to_a
=> [#<Setting _id: 565d541925917f10da000013, courses: ["561cddbb25917fa26e0000b9", "5614e62225917fbb1300005f", "5617a6bd25917f7aa100005d", "5617a5f325917ff38c000036", "560ca6b325917f158d000002", "55f909c825917f1ac000003e", "5632eebc25917f1ace000038", "521c4578ef8b6038ba000069"], user_id: "565d53f525917f10da000012">]

它只产生了一个不是所需结果的数组元素,然后

[53] pry(main)> Setting.any_of(courses: course.id.to_s).to_a
=> [#<Setting _id: 563b322425917f8117000025, courses: ["561cddbb25917fa26e0000b9", "5614e62225917fbb1300005f", "5617a6bd25917f7aa100005d", "560ca6b325917f158d000002", "5617a5f325917ff38c000036", "5632eebc25917f1ace000038", "55f909c825917f1ac000003e", "521c4578ef8b6038ba000069"], user_id: "56125d7b25917fb8c0000001">,
 #<Setting _id: 5641d3b125917fa02f000009, courses: ["5614e62225917fbb1300005f", "55f909c825917f1ac000003e", "521c4578ef8b6038ba000069"], user_id: "5264b629ef8b604f96000001">]

产生了两个数组元素,但是所需的结果是三个数组元素,可以通过添加这两个结果或遵循语法来实现

[57] pry(main)> Setting.any_of({courses: course.id.to_s}, {courses: course.id}).to_a
=> [#<Setting _id: 563b322425917f8117000025, courses: ["561cddbb25917fa26e0000b9", "5614e62225917fbb1300005f", "5617a6bd25917f7aa100005d", "560ca6b325917f158d000002", "5617a5f325917ff38c000036", "5632eebc25917f1ace000038", "55f909c825917f1ac000003e", "521c4578ef8b6038ba000069"], user_id: "56125d7b25917fb8c0000001">,
 #<Setting _id: 5641d3b125917fa02f000009, courses: ["5614e62225917fbb1300005f", "55f909c825917f1ac000003e", "521c4578ef8b6038ba000069"], user_id: "5264b629ef8b604f96000001">,
 #<Setting _id: 565d541925917f10da000013, courses: ["561cddbb25917fa26e0000b9", "5614e62225917fbb1300005f", "5617a6bd25917f7aa100005d", "5617a5f325917ff38c000036", "560ca6b325917f158d000002", "55f909c825917f1ac000003e", "5632eebc25917f1ace000038", "521c4578ef8b6038ba000069"], user_id: "565d53f525917f10da000012">]

我的困惑是为什么

Setting.any_of(courses: course.id).to_a

Setting.any_of(courses: course.id.to_s).to_a

产生不同的结果,是我的第三种语法方法是获得我想要的结果的唯一正确方法吗?

1 个答案:

答案 0 :(得分:4)

我无法100%确定,因为我真的不知道你的数据库里面有什么。但是,我对这个问题非常有信心。

由于您没有分享您的设置模型定义,我猜它是这样的:&/ p>

class Setting
  include Mongoid::Document
  field :courses, type: Array
end

如果这不正确,请告诉我,我会更新答案。鉴于这种结构,请看看这个pry会话:

[4] pry(main)> string_id = "563b322425917f8117000025"   
=> "563b322425917f8117000025"
[5] pry(main)> object_id = Moped::BSON::ObjectId.from_string("563b322425917f8117000025")   
=> "563b322425917f8117000025"
[6] pry(main)> string_id.class.name   
=> "String"
[7] pry(main)> object_id.class.name   
=> "Moped::BSON::ObjectId"

请注意Moped::BSON::ObjectId适用于Mongoid 3(我碰巧在这里安装的那个)。在较新的版本中,它只是BSON::ObjectId,因为Mongoid 4+放弃了Moped宝石。

我们可以创建两个不同的对象来表示相同的标识符。一个是String实例,另一个是类Moped :: BSON :: ObjectId,由Mongoid内部使用来表示相同的东西,即Mongo ObejctId。

为了使事情更容易,Mongoid(和mongo本身)允许你在某些情况下使用字符串而不是ObjectIds,但并非总是如此。在某些上下文中,从上面的Setting类开始,它无法知道是否要将String实例或ObjectId实例保存到数据库中。因此,它将保存您发送给它的任何内容:

[10] pry(main)> st = Setting.new(:courses => [object_id])
=> #<Setting _id: 5694ff9a04572eeb16000001, courses: ["563b322425917f8117000025"]>
[11] pry(main)> st.save
=> true
[12] pry(main)> st2 = Setting.new(:courses => [string_id])
=> #<Setting _id: 5694ffa904572eeb16000002, courses: ["563b322425917f8117000025"]>
[13] pry(main)> st2.save
=> true
[14] pry(main)> Setting.where(:courses => string_id).to_a
=> [#<Setting _id: 5694ffa904572eeb16000002, courses: ["563b322425917f8117000025"]>]
[15] pry(main)> Setting.where(:courses => string_id).to_a == [st2]
=> true
[17] pry(main)> Setting.where(:courses => object_id).to_a == [st]
=> true

因此,变量st指向一个Setting对象,该对象使用包含一个id的course字段保存,表示为Moped::BSON::ObjectId实例,而st2包含一个普通相同id的字符串表示形式。所以,这两件事看起来是一样的,并且很难发现,因为Moped::BSON::ObjectID#to_s方法返回与String#to_s完全相同的ID,所以如果你尝试将它打印到shell会话中,你就赢了& #39;能够发现差异。要发现差异,您必须致电object_id.class.name并与string_id.class.name进行比较。

你可以通过运行:

来证明你的情况
all_settings = Setting.any_of({courses: course.id.to_s}, {courses: course.id})
all_settings.each do |a_setting|
  puts "Setting #{a_setting.id}:"

  a_setting.courses.each do |string_or_object_id|
    puts "   #{string_or_object_id.class.name} #{string_or_object_id}"
  end
end

理想情况下,您应该在此数组中仅使用String对象或仅使用ObjectId对象,否则将来会出现问题。解决这个问题。我建议您使用Moped::BSON::ObjectId,因为它映射到自然的ObjectId Mongo表示,并允许您做一些有趣的事情,比如查询ObjectId的生成日期:

[21] pry(main)> object_id.generation_time
=> 2015-11-05 10:40:36 UTC

我希望这会有所帮助:)