mongoid在一个哈希数组内的数组中搜索

时间:2014-03-12 10:27:29

标签: mongodb mongoid

Object embeds_many searched_items

以下是文件:

{"_id": { "$oid" : "5320028b6d756e1981460000" },

"searched_items": [
{
  "_id": { "$oid" : "5320028b6d756e1981470000" },
   "hotel_id": 127,
  "room_info": [
    {
       "price": 10,
      "amenity_ids": [
        1,
        2
      ]
    },
    {
      "price": 160,
      "amenity_ids": null
    }
  ]
  },
  {
  "_id": { "$oid" : "5320028b6d756e1981480000" },
  "hotel_id": 161,
  "room_info": [
    {
      "price": 400,
      "amenity_ids": [4,5]
    }
   ]
  }
 ] 
}

我想找到room_info.amenity_ids IN [2,3]的“searching_items”。

我试过

object.searched_items.where('room_info.amenity_ids' => [2, 3])

object.searched_items.where('room_info.amenity_ids' =>{'$in' => [2,3]}

没有运气

2 个答案:

答案 0 :(得分:5)

mongoid提供 elem_match 方法,用于在数组类型的对象中进行搜索

e.g。

class A
  include Mongoid::Document
  field :some_field, type: Array
end

A.create(some_field: [{id: 'a', name: 'b'}, {id: 'c', name: 'd'}])

A.elem_match(some_field: { :id.in=> ["a", "c"] }) => will return the object

如果您有任何其他疑问,请告诉我。

<强>更新

class SearchedHotel
  include Mongoid::Document
  field :hotel_id, type: String
  field :room_info, type: Array
end

SearchedHotel.create(hotel_id: "1", room_info: [{id: 1, amenity_ids: [1,2], price: 600},{id: 2, amenity_ids: [1,2,3], price: 1000}])
SearchedHotel.create(hotel_id: "2", room_info: [{id: 3, amenity_ids: [1,2], price: 600}])

SearchedHotel.elem_match(room_info: {:amenity_ids.in => [1,2]})

Mongoid::Criteria
 selector: {"room_info"=>{"$elemMatch"=>{"amenity_ids"=>{"$in"=>[1, 2]}}}}
 options:  {}
 class:    SearchedHotel
 embedded: false

它返回两个记录。我错过了你的问题/要求。如果是,请告诉我。

答案 1 :(得分:0)

区分发送到MongoDB服务器的顶级查询和 由Mongoid实现的嵌入式文档的客户端操作。 这是原始问题与@ sandeep-kumar和相关评论的答案之间潜在的混淆。 在获取查询结果之后,最初的问题是关于嵌入式文档的where子句。 答案@ sandeep-kumar和评论都是关于顶级查询。

以下测试涵盖两者,显示@ sandeep-kumar的答案如何处理您评论中的示例, 以及在原始问题上有什么作用和什么作用。 总而言之,Sandeep的答案适用于顶级查询。 请检查您的代码,如果仍有问题,请发布总结问题的确切Ruby代码。

对于您的原始问题,请注意“对象”已经从MongoDB获取, 并且您可以通过查看log / test.log文件来验证这一点。 随后的“where”操作都是Mongoid的客户端执行。 简单的“where”子句在嵌入式文档级别起作用。 涉及嵌套数组值的复杂“where”子句似乎不起作用 - 我并不认为Mongoid会在客户端重新实现'$ in'。 知道“对象”已经有查询结果, 并且关联“searching_items”可以方便地访问嵌入的文档, 你可以编写Ruby代码来选择你想要的东西,如下面的测试。

希望这会有所帮助。

测试/单元/ my_object_test.rb

require 'test_helper'
require 'pp'

class MyObjectTest < ActiveSupport::TestCase
  def setup
    MyObject.delete_all
    A.delete_all
    SearchedHotel.delete_all
  end

  test "original question with client-side where operation on embedded documents" do
    doc = {"_id"=>{"$oid"=>"5320028b6d756e1981460000"}, "searched_items"=>[{"_id"=>{"$oid"=>"5320028b6d756e1981470000"}, "hotel_id"=>127, "room_info"=>[{"price"=>10, "amenity_ids"=>[1, 2]}, {"price"=>160, "amenity_ids"=>nil}]}, {"_id"=>{"$oid"=>"5320028b6d756e1981480000"}, "hotel_id"=>161, "room_info"=>[{"price"=>400, "amenity_ids"=>[4, 5]}]}]}
    MyObject.create(doc)
    puts
    object = MyObject.first
    <<-EOT.split("\n").each{|line| puts "#{line}:"; eval "pp #{line}"}
    object.searched_items.where('hotel_id' => 127).to_a
    object.searched_items.where(:hotel_id.in => [127,128]).to_a
    object.searched_items.where('room_info.amenity_ids' => {'$in' => [2,3]}).to_a
    object.searched_items.where('room_info.amenity_ids'.to_sym.in => [2,3]).to_a
    object.searched_items.select{|searched_item| searched_item.room_info.any?{|room_info| room_info['amenity_ids'] && !(room_info['amenity_ids'] & [2,3]).empty?}}.to_a
    EOT
  end

  test "A comment - top-level queries" do
    A.create(some_field: [{id: 'a', name: 'b', tag_ids: [6,7,8]}, {id: 'c', name: 'd'}, tag_ids: [5,6,7]])
    A.create(some_field: [{id: 'a', name: 'b', tag_ids: [1,2,3]}, {id: 'c', name: 'd'}, tag_ids: [2,3,4]])
    puts
    pp A.where('some_field.tag_ids'.to_sym.in => [2,3]).to_a
    pp A.elem_match(some_field: { :tag_ids.in => [2,3,4] }).to_a
  end

  test "SearchedHotel comment - top-level query" do
    s = <<-EOT
      [#<SearchedHotel _id: 53253c246d756e49a7030000, hotel_id: \"1\", room_info: [{\"id\"=>1, \"amenity_ids\"=>[1, 2], \"price\"=>600}, {\"id\"=>2, \"amenity_ids\"=>[1, 2, 3], \"price\"=>1000}]>, #<SearchedHotel _id: 53253c246d756e49a7040000, hotel_id: \"2\",  room_info: [{\"id\"=>3, \"amenity_ids\"=>[1, 2], \"price\"=>600}]>]
    EOT
    a = eval(s.gsub('#<SearchedHotel ', '{').gsub(/>,/, '},').gsub(/>\]/, '}]').gsub(/_id: \h+, /, ''))
    SearchedHotel.create(a)
    puts
    <<-EOT.split("\n").each{|line| puts "#{line}:"; eval "pp #{line}"}
    SearchedHotel.elem_match(room_info: {:amenity_ids.in => [1,2]}).to_a
    EOT
  end
end

$ ruby​​ -Ilib -Itest test / unit / my_object_test.rb

Run options:

# Running tests:

[1/3] MyObjectTest#test_A_comment_-_top-level_queries
[#<A _id: 5359329d7f11ba034b000002, some_field: [{"id"=>"a", "name"=>"b", "tag_ids"=>[1, 2, 3]}, {"id"=>"c", "name"=>"d"}, {"tag_ids"=>[2, 3, 4]}]>]
[#<A _id: 5359329d7f11ba034b000002, some_field: [{"id"=>"a", "name"=>"b", "tag_ids"=>[1, 2, 3]}, {"id"=>"c", "name"=>"d"}, {"tag_ids"=>[2, 3, 4]}]>]
[2/3] MyObjectTest#test_SearchedHotel_comment_-_top-level_query
    SearchedHotel.elem_match(room_info: {:amenity_ids.in => [1,2]}).to_a:
[#<SearchedHotel _id: 5359329d7f11ba034b000003, hotel_id: "1", room_info: [{"id"=>1, "amenity_ids"=>[1, 2], "price"=>600}, {"id"=>2, "amenity_ids"=>[1, 2, 3], "price"=>1000}]>,
 #<SearchedHotel _id: 5359329d7f11ba034b000004, hotel_id: "2", room_info: [{"id"=>3, "amenity_ids"=>[1, 2], "price"=>600}]>]
[3/3] MyObjectTest#test_original_question_with_client-side_where_operation_on_embedded_documents
    object.searched_items.where('hotel_id' => 127).to_a:
[#<SearchedItem _id: 5359329d7f11ba034b000006, hotel_id: 127, room_info: [{"price"=>10, "amenity_ids"=>[1, 2]}, {"price"=>160, "amenity_ids"=>nil}]>]
    object.searched_items.where(:hotel_id.in => [127,128]).to_a:
[#<SearchedItem _id: 5359329d7f11ba034b000006, hotel_id: 127, room_info: [{"price"=>10, "amenity_ids"=>[1, 2]}, {"price"=>160, "amenity_ids"=>nil}]>]
    object.searched_items.where('room_info.amenity_ids' => {'$in' => [2,3]}).to_a:
[]
    object.searched_items.where('room_info.amenity_ids'.to_sym.in => [2,3]).to_a:
[]
    object.searched_items.select{|searched_item| searched_item.room_info.any?{|room_info| room_info['amenity_ids'] && !(room_info['amenity_ids'] & [2,3]).empty?}}.to_a:
[#<SearchedItem _id: 5359329d7f11ba034b000006, hotel_id: 127, room_info: [{"price"=>10, "amenity_ids"=>[1, 2]}, {"price"=>160, "amenity_ids"=>nil}]>]
Finished tests in 0.089544s, 33.5031 tests/s, 0.0000 assertions/s.
3 tests, 0 assertions, 0 failures, 0 errors, 0 skips