アーカイブ

投稿者のアーカイブ

RailsでPGroongaを使う。その2 このエントリーを含むはてなブックマーク はてなブックマーク - RailsでPGroongaを使う。その2

2016 年 11 月 1 日 Comments off

RailsでPGroongaを使う。 » サイキョウライン

全文検索そのものと、キーワードのハイライトについてはよかったのですが、pgroonga.score関数でとれるスコアを使ったソートで問題が・・・(つづく)

前回の続き、この問題について。端的に言うと、せっかく作ったインデックスを使ってくれずスコアが取れないため、結果として正しくソートが出来ていません。

pgroonga.score関数の仕様はこの様になっています。

pgroonga.score関数 | PGroonga

pgroonga.score関数はインデックスを使わずに全文検索した場合は常に0.0を返します。言い換えると、pgroonga.score関数はシーケンシャルスキャンで全文検索を実行した場合は常に0.0を返します。

そして、チュートリアルでは作ったインデックスを使わせる対策として、enable_seqscanをoffにしていました。

チュートリアル | PGroonga

確実にpgroongaインデックスを使うためにシーケンシャルスキャンを無効にします。

直にクエリを発行するなら、SELECTの前後でenable_seqscanをoff→DEFAULTと一時的に切り替えてあげればよいと思うのですが、ActiveRecordを使っているとクエリが発行されるのは実際に結果が必要になったタイミングなので、この様に書いても意味がありません。

> Article.connection.execute 'SET enable_seqscan = off'
> articles = Article.full_text_search('桃太郎')
> Article.connection.execute 'SET enable_seqscan = DEFAULT'

SELECTの前後にコールバックがあればいいのですが、探してみても用意されてはいないみたい。
after_initialize and after_findは期待している動きとはちょっと違う。)

かと言って、postgresql.confに書いてしまうのはやり過ぎ、というか全体でシーケンシャルスキャンをoffにしてしまうのは問題だと思います。
このような場合、どう実装するのがいいのでしょうか。

[15:30 追記]
ActiveRecordが生成するSQLをpsqlで流すとインデックスが使われることは確認できているので、enable_seqscanをどこで切り替えるか、に悩んでいます。

> puts Article.full_text_search('桃太郎').to_sql
SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles"  WHERE (title %% '桃太郎' OR body %% '桃太郎')  ORDER BY pgroonga_score DESC
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles"  WHERE (title %% '桃太郎' OR body %% '桃 太郎')  ORDER BY pgroonga_score DESC;
                               QUERY PLAN
-------------------------------------------------------------------------
 Sort  (cost=1.14..1.15 rows=4 width=242)
   Sort Key: (score(articles.*)) DESC
   ->  Seq Scan on articles  (cost=0.00..1.10 rows=4 width=242)
         Filter: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
(4 rows)

=> SET enable_seqscan = off;
SET
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles"  WHERE (title %% '桃太郎' OR body %% '桃太郎')  ORDER BY pgroonga_score DESC;
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Sort  (cost=4.08..4.09 rows=4 width=242)
   Sort Key: (score(articles.*)) DESC
   ->  Bitmap Heap Scan on articles  (cost=0.00..4.04 rows=4 width=242)
         Recheck Cond: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
         ->  BitmapOr  (cost=0.00..0.00 rows=2 width=0)
               ->  Bitmap Index Scan on index_articles_on_id_and_title_and_body  (cost=0.00..0.00 rows=2 width=0)
                     Index Cond: (title %% '桃太郎'::text)
               ->  Bitmap Index Scan on index_articles_on_id_and_title_and_body  (cost=0.00..0.00 rows=1 width=0)
                     Index Cond: (body %% '桃太郎'::text)
(9 rows)

=> SET enable_seqscan = DEFAULT;
SET
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles"  WHERE (title %% '桃太郎' OR body %% '桃太郎')  ORDER BY pgroonga_score DESC;
                               QUERY PLAN
-------------------------------------------------------------------------
 Sort  (cost=1.14..1.15 rows=4 width=242)
   Sort Key: (score(articles.*)) DESC
   ->  Seq Scan on articles  (cost=0.00..1.10 rows=4 width=242)
         Filter: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
(4 rows)
カテゴリー: PostgreSQL, Rails タグ:

「措法28の2」のやつが延長されている件について。 このエントリーを含むはてなブックマーク はてなブックマーク - 「措法28の2」のやつが延長されている件について。

2016 年 10 月 28 日 Comments off

No.5408 中小企業者等の少額減価償却資産の取得価額の損金算入の特例|法人税|国税庁

中小企業者等が、取得価額が30万円未満である減価償却資産を平成18年4月1日から平成30年3月31日までの間に取得などして事業の用に供した場合には、一定の要件のもとに、その取得価額に相当する金額を損金の額に算入することができます。

法人向けにはこのようなページがあるのですが、個人事業主向けのものが見つからなかったため、(税務署に掛けるとリダイレクトされる)国税局電話相談センターに聞いてみました。

結果から言うと個人事業主向けのページは用意されておらず、下記の様にPDFの中を辿って解釈してください、とのことでした。

パンフレット・手引き|国税庁

平成28年分 所得税の改正のあらまし(平成28年4月)(PDF/515KB)

該当するのは、このPDFの6ページにあるこの部分で、

⒀ 中小事業者の少額減価償却資産の取得価額の必要経費算入の特例(措法 28 の2)について、その
適用期限が平成 30 年3月 31 日まで2年延長されました(措法 28 の2①)。

ん?中小事業者?と思いながらこのブロックの上の方に戻ると、

3 事業所得等関係

とあり、事業所得は所得税の区分のひとつなので、個人事業主も対象だよ、ということになりますよ、と。

すごく・・・分かりづらいです・・・。

カテゴリー: 日記 タグ:

RailsでPGroongaを使う。 このエントリーを含むはてなブックマーク はてなブックマーク - RailsでPGroongaを使う。

2016 年 10 月 27 日 Comments off

とあるRailsアプリで全文検索が必要になったため、PGroongaを試してみました。

環境はそれぞれこんな感じ。(表示は一部省略)

% cat /etc/lsb-release
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"

% sudo aptitude show postgresql
パッケージ: postgresql
バージョン: 9.5+173

% sudo aptitude show postgresql-9.5-pgroonga
パッケージ: postgresql-9.5-pgroonga
バージョン: 1.1.5-2~xenial1

まず、参考にしたのはこのページです。

Ruby on RailsでPostgreSQLとPGroongaを使って日本語全文検索を実現する方法 – ククログ(2015-11-09)

この記事ではRuby on Railsで作ったアプリケーションからPGroongaを使って日本語全文検索
機能を実現する方法を説明します。

そして、使ったそれぞれの関数のリファレンスはこちら。

実際のコードはこんな感じです。
PostgreSQLにエクステンションを追加するマイグレーション

PGroongaを使わせるためのインデックスを追加するマイグレーション
カラムがこうなっている理由はこちらです。

pgroonga.score関数 | PGroonga

pgroonga.score関数を使うには、pgroongaインデックスにプライマリーキーに指定したカラムを追加する必要があります。もし、プライマリーキーに指定したカラムをpgroongaインデ>ックスに追加していない場合は、pgroonga.score関数は常に0.0を返します。

全文検索をしているクラス
titleとbodyを検索対象にしています。

実際に使用するとこうなります。

> article = Article.full_text_search('桃太郎', true).first
> article.highlighted_body
=> むかしむかしあるところで<span class="keyword">桃太郎</span>という少年が鬼ヶ島に行って鬼を退治しました。

全文検索そのものと、キーワードのハイライトについてはよかったのですが、pgroonga.score関数でとれるスコアを使ったソートで問題が・・・(つづく)

ヒント: インデックス

カテゴリー: PostgreSQL, Rails タグ:

ラジコのタイムフリーが始まった。 このエントリーを含むはてなブックマーク はてなブックマーク - ラジコのタイムフリーが始まった。

2016 年 10 月 11 日 Comments off

radiko.jp

過去1週間以内に放送された番組を後から聴くことのできる機能です。

ヘッドホンをしている時はほぼTBSラジオが流れているような生活を送っているのですが、録音した深夜番組が主なため周りにおすすめしづらかったので、始まるのを楽しみに待っていました。試してみたのはこんな感じ。

ただ、いま確認したら聴けなくなっていて、よく読むとこんな制限がありました。

radiko.jp

聴取可能時間(3時間)とは、あるひとつの番組を選んで、その番組を再生した段階から、3時間が経過するまでの間、聴取することができるということです。例えば、12時に再生ボタンを押した場合、その番組は15時まで聴くことができます。

これって、おすすめリンクを送った時に相手がその場で再生してしまったら、そこから3時間以内に聴き終わらないとダメよってことですよね。2時間番組だと、全部聴くには1時間しか猶予がない。その場でちょっと聴いて、残りはあとで落ち着いてってことができない。
権利関係やらリソースやらで制限を設けないといけないんだとは思いますが、シェアを勧めているのにこういう仕様なのは、ちょっと気軽にリンクを投げにくい。

ダウンロード購入のアプリみたいに3回まではOKみないなの、付かないですかねぇ。

カテゴリー: 日記 タグ:

Auto Layoutの本がよかった。 このエントリーを含むはてなブックマーク はてなブックマーク - Auto Layoutの本がよかった。

2016 年 9 月 25 日 Comments off

なんとなく制約を付けて実際の動きを確認して、なんてことをやっていたのですが、ちゃんと仕組みを理解したくて買ったこの本が分かり易かったのでおすすめです。
買ってしばらく経つのですが、やっぱり必要になった時にしか頭に入らないもんですね。

カテゴリー: iOS タグ:

Rails4→5で目にしたdeprecationたち。 このエントリーを含むはてなブックマーク はてなブックマーク - Rails4→5で目にしたdeprecationたち。

2016 年 8 月 24 日 Comments off

なにかの役に立つかも知れないので、淡淡と書いておきます。

DEPRECATION WARNING: Method map is deprecated and will be removed in Rails 5.1, as `ActionController::Parameters` no longer inherits from hash. Using this deprecated behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.0.1/classes/ActionController/Parameters.html

DEPRECATION WARNING: uniq is deprecated and will be removed from Rails 5.1 (use distinct instead)

DEPRECATION WARNING: `redirect_to :back` is deprecated and will be removed from Rails 5.1. Please use `redirect_back(fallback_location: fallback_location)` where `fallback_location` represents the location to use if the request has no HTTP referer information.

DEPRECATION WARNING: `render :text` is deprecated because it does not actually render a `text/plain` response. Switch to `render plain: 'plain text'` to render as `text/plain`, `render html: '<strong>HTML</strong>'` to render as `text/html`, or `render body: 'raw'` to match the deprecated behavior and render with the default Content-Type, which is `text/plain`.

DEPRECATION WARNING: xhr and xml_http_request methods are deprecated in favor of `get :index, xhr: true` and `post :create, xhr: true`

DEPRECATION WARNING: ActionController::TestCase HTTP request methods will accept only keyword arguments in future Rails versions.
Examples:
get :show, params: { id: 1 }, session: { user_id: 1 }
process :update, method: :post, params: { id: 1 }

カテゴリー: Rails タグ:

belongs_to_required_by_defaultが効かない。 このエントリーを含むはてなブックマーク はてなブックマーク - belongs_to_required_by_defaultが効かない。

2016 年 8 月 17 日 Comments off

Rails 5.0.0からbelongs_toがデフォルトで必須になったはずなのだけれど、そうなっていなかったので調べた。
結論から言うと「config/initializers/new_framework_defaults.rb」に置いたままではダメで、「config/application.rb」に書かないといけない。
理由は読まれるタイミングと使われるタイミング。(またこれか)

最初に、belongs_toの必須が定義されるのはここで、「model.belongs_to_required_by_default」が元になっている。
activerecord-5.0.0.1/lib/active_record/associations/builder/belongs_to.rb

128       if reflection.options[:optional].nil?
129         required = model.belongs_to_required_by_default
130       else
131         required = !reflection.options[:optional]
132       end

で、上の「model.belongs_to_required_by_default」がどこでセットされるかというと、ここ。追加したコメントはあとで出てきます。
activesupport-5.0.0.1/lib/active_support/core_ext/module/attribute_accessors.rb

123       class_eval(<<-EOS, __FILE__, __LINE__ + 1)
124         @@#{sym} = nil unless defined? @@#{sym}
125
126         def self.#{sym}=(obj)
127 #{if sym == :belongs_to_required_by_default
128 'puts "#{name}.#{__method__} # ここで決まる!"'
129 end}
130           @@#{sym} = obj
131         end
132       EOS

って書いても分かりにくいので、コメントを仕込んでタイミングを表示してみた。こんな感じ。

config/application.rb

20 puts "#{__FILE__.sub("#{Rails.root}/", '')}:#{__LINE__} Rails.application.config.active_record.belongs_to_required_by_default #=> #{Rails.application.config.active_record.belongs_to_required_by_default}"
 21   config.active_record.belongs_to_required_by_default = true # ここに書かないとダメ!
 22 puts "#{__FILE__.sub("#{Rails.root}/", '')}:#{__LINE__} Rails.application.config.active_record.belongs_to_required_by_default #=> #{Rails.application.config.active_record.belongs_to_required_by_default}"

config/initializers/new_framework_defaults.rb

17 # Require `belongs_to` associations by default. Previous versions had false.
 18 puts "#{__FILE__.sub("#{Rails.root}/", '')}:#{__LINE__} Rails.application.config.active_record.belongs_to_required_by_default #=> #{Rails.application.config.active_record.belongs_to_required_by_default}"
 19 Rails.application.config.active_record.belongs_to_required_by_default = true
 20 puts "#{__FILE__.sub("#{Rails.root}/", '')}:#{__LINE__} Rails.application.config.active_record.belongs_to_required_by_default #=> #{Rails.application.config.active_record.belongs_to_required_by_default}"

まずは「config/application.rb」に書かなかった場合

% bundle exec rails c
config/application.rb:20 Rails.application.config.active_record.belongs_to_required_by_default #=>
config/application.rb:22 Rails.application.config.active_record.belongs_to_required_by_default #=>
config/initializers/new_framework_defaults.rb:18 Rails.application.config.active_record.belongs_to_required_by_default #=>
config/initializers/new_framework_defaults.rb:20 Rails.application.config.active_record.belongs_to_required_by_default #=> true
Loading development environment (Rails 5.0.0.1)
irb(main):001:0> ActiveRecord::Base.belongs_to_required_by_default
=> nil

つぎに「config/application.rb」に書いた場合

% bundle exec rails c
config/application.rb:20 Rails.application.config.active_record.belongs_to_required_by_default #=>
config/application.rb:22 Rails.application.config.active_record.belongs_to_required_by_default #=> true
ActiveRecord::Base.belongs_to_required_by_default= # ここで決まる!
config/initializers/new_framework_defaults.rb:18 Rails.application.config.active_record.belongs_to_required_by_default #=> true
config/initializers/new_framework_defaults.rb:20 Rails.application.config.active_record.belongs_to_required_by_default #=> true
Loading development environment (Rails 5.0.0.1)
irb(main):001:0> ActiveRecord::Base.belongs_to_required_by_default
=> true

config/application.rb」に書かないとnilのままなんだね・・・。

って、すぐ遭遇しそうなことなので、自分が基本的なことを見落としている気がしてきたぞ。

カテゴリー: Rails タグ:

time_zoneはconfig/initializersに移しちゃ(まだ?)ダメっぽい。 このエントリーを含むはてなブックマーク はてなブックマーク - time_zoneはconfig/initializersに移しちゃ(まだ?)ダメっぽい。

2016 年 8 月 11 日 Comments off
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'

config/application.rbにあったこのコメントがRails 5.0.0から消えたので、config/initializersに移動すべきと解釈してファイルを分けたら効かなくなってしまった。
理由は、config/initializersが読まれる前にconfig.time_zoneが使われてしまうから。

なので、まだconfig.time_zoneはconfig/application.rbに書いておかないといけない。
気になってconfig.i18n.default_localeも調べたらこっちは大丈夫だった。

それぞれの読まれる/使われる順番はこんな感じ。

% bundle exec rails c
config/application.rb:21
vendor/bundle/gems/activesupport-5.0.0/lib/active_support/railtie.rb:32 Time.zone #=>
vendor/bundle/gems/activesupport-5.0.0/lib/active_support/railtie.rb:33 Time.zone #=> (GMT+00:00) UTC
config/initializers/i18n.rb:1
config/initializers/time_zone.rb:1
vendor/bundle/gems/activesupport-5.0.0/lib/active_support/i18n_railtie.rb:49 I18n.default_locale #=> en
vendor/bundle/gems/activesupport-5.0.0/lib/active_support/i18n_railtie.rb:51 I18n.default_locale #=> ja
Loading development environment (Rails 5.0.0)
irb(main):001:0> Time.zone
=> #<ActiveSupport::TimeZone:0x007f97bc001968 @name="UTC", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>>
irb(main):002:0> I18n.default_locale
=> :ja
カテゴリー: Rails タグ:

per_form_csrf_tokensはどう影響するのか。 このエントリーを含むはてなブックマーク はてなブックマーク - per_form_csrf_tokensはどう影響するのか。

2016 年 8 月 10 日 Comments off

Configuring Rails Applications — Ruby on Rails Guides

config.action_controller.per_form_csrf_tokens configures whether CSRF tokens are only valid for the method/action they were generated for.

5.0から加わったこれ、tokenがmethodとactionに関連付くようになるオプションということで、自分でform_authenticity_tokenを直に呼んでいるところでは影響を受けるのかが気になってコードを読んでみた。
結果から言うと、自分でform_authenticity_tokenを呼んでいる場合には影響を受けない。

rails/request_forgery_protection.rb at master · rails/rails

# Sets the token value for the current session.
def form_authenticity_token(form_options: {})
  masked_authenticity_token(session, form_options: form_options)
end

form_authenticity_tokenはform_optionsを引数で受け取っていて、デフォルトは空になってる。

rails/request_forgery_protection.rb at master · rails/rails

# Creates a masked version of the authenticity token that varies
# on each request. The masking is used to mitigate SSL attacks
# like BREACH.
def masked_authenticity_token(session, form_options: {})
  action, method = form_options.values_at(:action, :method)
  raw_token = if per_form_csrf_tokens && action && method
    action_path = normalize_action_path(action)
    per_form_csrf_token(session, action_path, method)
  else
    real_csrf_token(session)
  end

form_authenticity_tokenで受け取った引数はこんな感じで使われる。
ここのper_form_csrf_tokensは、config/initializers/new_framework_defaults.rbにあるやつそのもの。

ではform_optionsはどこから来るのかっていうと、ここ。

rails/url_helper.rb at master · rails/rails

def token_tag(token=nil, form_options: {})
  if token != false && protect_against_forgery?
    token ||= form_authenticity_token(form_options: form_options)
    tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
  else
    "".freeze
  end
end

url_helperの中だ。

ひとまず影響がないことが分かったけれど変わりやすそうな場所ではあるので、注意が必要かな。

カテゴリー: Rails タグ:

nginx.confの変数は読みやすさのために使うな。 このエントリーを含むはてなブックマーク はてなブックマーク - nginx.confの変数は読みやすさのために使うな。

2016 年 7 月 21 日 Comments off

Is there a proper way to use nginx variables to make sections of the configuration shorter, using them as macros for making parts of configuration work as templates?

Variables should not be used as template macros. Variables are evaluated in the run-time during the processing of each request, so they are rather costly compared to plain static configuration. Using variables to store static strings is also a bad idea. Instead, a macro expansion and “include” directives should be used to generate configs more easily and it can be done with the external tools, e.g. sed + make or any other common template mechanism.

意訳:リクエストの度に余計な負荷が掛かるから、変数使わないでベタに書け。読みやすくしたいなら他の方法を使え。

capistrano-puma/puma.rake at master · seuros/capistrano-puma

upload! StringIO.new(ERB.new(erb, nil, ‘-‘).result(binding)), to

ということで、同じ時期に触ってたcapistrano-pumaが同じようなことでerb使ってたのでマネしました。

カテゴリー: プログラミング一般 タグ: