mongoId 的 any_of activerecord 实现问题
Issue with mongoId's any_of implementation for activerecord
我的设置模型数据是这样的
[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
产生不同的结果,我的第三种语法方法是获得我想要的结果的唯一正确方法吗?
我不能 100% 确定,因为我真的不知道你的数据库里有什么。但是,我对这里的问题很有信心。
由于您没有分享您的设置模型定义,我猜是这样的:
class Setting
include Mongoid::Document
field :courses, type: Array
end
如果这不正确,请告诉我,我会更新答案。给定那个结构,看看这个 pry session:
[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+ 放弃了助力车 gem。
我们可以创建两个不同的 object 来表示相同的标识符。一个是 String 实例,另一个是 class Moped::BSON::ObjectId,在内部由 Mongoid 用来表示同一事物,即 Mongo ObejctId.
为了使事情变得更简单,Mongoid(和 mongo 本身)允许您在某些上下文中使用字符串而不是 ObjectId,但并非总是如此。在某些情况下,从上面的设置 class 开始,它无法知道您是要将 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
object 保存的课程字段包含一个 id,表示为 Moped::BSON::ObjectId
实例,而 st2
包含相同 id 的纯字符串表示形式。所以,这两件事看起来是一样的,而且很难发现,因为 Moped::BSON::ObjectID#to_s
方法 returns 与 String#to_s
完全相同的 id,所以如果你尝试将它打印成 shell session 您将无法发现差异。要发现差异,您必须调用 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 objects 或仅使用 ObjectId objects,否则您 将来会遇到 问题。解决这个问题。我建议您使用 Moped::BSON::ObjectId
,因为它映射到自然的 ObjectId Mongo 表示,并允许您做一些有趣的事情,如按生成日期查询 ObjectId:
[21] pry(main)> object_id.generation_time
=> 2015-11-05 10:40:36 UTC
希望对您有所帮助:)
我的设置模型数据是这样的
[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
产生不同的结果,我的第三种语法方法是获得我想要的结果的唯一正确方法吗?
我不能 100% 确定,因为我真的不知道你的数据库里有什么。但是,我对这里的问题很有信心。
由于您没有分享您的设置模型定义,我猜是这样的:
class Setting
include Mongoid::Document
field :courses, type: Array
end
如果这不正确,请告诉我,我会更新答案。给定那个结构,看看这个 pry session:
[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+ 放弃了助力车 gem。
我们可以创建两个不同的 object 来表示相同的标识符。一个是 String 实例,另一个是 class Moped::BSON::ObjectId,在内部由 Mongoid 用来表示同一事物,即 Mongo ObejctId.
为了使事情变得更简单,Mongoid(和 mongo 本身)允许您在某些上下文中使用字符串而不是 ObjectId,但并非总是如此。在某些情况下,从上面的设置 class 开始,它无法知道您是要将 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
object 保存的课程字段包含一个 id,表示为 Moped::BSON::ObjectId
实例,而 st2
包含相同 id 的纯字符串表示形式。所以,这两件事看起来是一样的,而且很难发现,因为 Moped::BSON::ObjectID#to_s
方法 returns 与 String#to_s
完全相同的 id,所以如果你尝试将它打印成 shell session 您将无法发现差异。要发现差异,您必须调用 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 objects 或仅使用 ObjectId objects,否则您 将来会遇到 问题。解决这个问题。我建议您使用 Moped::BSON::ObjectId
,因为它映射到自然的 ObjectId Mongo 表示,并允许您做一些有趣的事情,如按生成日期查询 ObjectId:
[21] pry(main)> object_id.generation_time
=> 2015-11-05 10:40:36 UTC
希望对您有所帮助:)