@kyanny's blog

My thoughts, my life. Views/opinions are my own.

Treasure Data (Presto) で MongoDB の Array[BSON::ObjectId] 型のデータを string 型として入れてしまったとき Array にバラして展開する

  1. substr で文字列からゴミを取り除く
  2. split で string -> array にする
  3. UNNEST で展開する 7.16. SELECT — Presto 0.147 Documentation
SELECT 
   *
FROM
  (
  VALUES(
    1,
    '[573ca5ca6200b048be000001, 573ca5cc6200b048be000002]'
  ),
  (
    2,
    '[573ca5ca6200b048be000003, 573ca5cc6200b048be000004]'
  )
  ) AS t(
    id,
    object_ids
  ) CROSS
JOIN
  unnest(
    split(
      substr(object_ids,
        2,
        length(object_ids) -2),
      ','
    )
  ) AS t(object_id)

f:id:a666666:20160519023725p:plain

Hive の場合は LATERAL VIEW というので最後の展開のステップができるらしい; 元々データエンジニアの人に「array の要素の一つに対して = で JOIN するにはどうすればよいか」と聞いて LATERAL VIEW を教えてもらい、 Presto でのやり方を調べた、という順番。

Embulk の設定とかで Array 型で入れられるなら当然そうしたほうがよい(できるのかどうか知らない)

Rails の Strong Parameters は Array[BSON::ObjectId] 型のデータを許可しない

params.permit(:object_ids) としたとき、

  • :object_ids => [BSON::ObjectId('573d8ac76200b0176c000001')] はダメ
    • 空の Array [] が返る
  • :object_ids => ["573d8ac76200b0176c000001"] なら ok

BSON::ObjectId のリストをフォーム入力などから受け取って BSON::ObjectId 型にキャストして処理するプログラムを書く場合は、コントローラーのレイヤでキャストせず、文字列表現で受け取ってモデルなど下層でキャストする。

https://github.com/kyanny/playground/blob/gh-pages/strong_parameters_reject_array_of_bson_objectid/app/controllers/users_controller.rb

  def user_params
    params.require(:user).permit({
                                   reference_ids: []
                                 })
  end

  def user_params2
    _params = params.require(:user)
    _params[:reference_ids] = _params[:reference_ids].map { |id| BSON::ObjectId(id) }
    _params.permit(reference_ids: [])
  end

https://github.com/kyanny/playground/blob/gh-pages/strong_parameters_reject_array_of_bson_objectid/test/controllers/users_controller_test.rb

  test "should create user" do
    id = BSON::ObjectId.new
    post :create, user: { reference_ids: [id] }
    data = JSON.parse(response.body)
    pp data
    assert_equal data['user_params']['reference_ids'], [id.to_s]
  end

  test "should create user (2)" do
    id = BSON::ObjectId.new
    post :create, user: { reference_ids: [id] }
    data = JSON.parse(response.body)
    pp data
    assert_equal data['user_params2']['reference_ids'], [id.to_s]
  end
# Running:

{"params"=>
  {"user"=>{"reference_ids"=>[{"$oid"=>"573d8b776200b017d6000001"}]},
   "controller"=>"users",
   "action"=>"create"},
 "user_params"=>{"reference_ids"=>["573d8b776200b017d6000001"]},
 "user_params2"=>{}}
F......{"params"=>
  {"user"=>{"reference_ids"=>[{"$oid"=>"573d8b776200b017d6000002"}]},
   "controller"=>"users",
   "action"=>"create"},
 "user_params"=>{"reference_ids"=>["573d8b776200b017d6000002"]},
 "user_params2"=>{}}
.

Finished in 0.062714s, 127.5633 runs/s, 31.8908 assertions/s.

  1) Failure:
UsersControllerTest#test_should_create_user_(2) [/Users/kyanny/playground/strong_parameters_reject_array_of_bson_objectid/test/controllers/users_controller_test.rb:26]:
Expected: nil
  Actual: ["573d8b776200b017d6000001"]

8 runs, 2 assertions, 1 failures, 0 errors, 0 skips