#------------------------------------------- # OpenurlQuerySyntaxErrorクラス # 文法上の誤りをエラーとする #------------------------------------------- class OpenurlQuerySyntaxError < RuntimeError; end #------------------------------------------- # Openurlクラス #------------------------------------------- class Openurl # 一致条件ごとの分類 MATCH_EXACT = [:ndl_dpid] # 完全一致 MATCH_PART = [:aulast, :aufirst, :au, :title, :atitle, :jtitle, :btitle, :pub] # 部分一致 MATCH_AHEAD = [:issn, :isbn, :ndl_jpno] # 前方一致 # ある項目に対して複数語指定時の検索方法ごとの分類 LOGIC_MULTI_AND = [:au, :title, :atitle, :jtitle, :btitle, :pub] # AND検索 LOGIC_MULTI_OR = [:ndl_dpid] # OR検索 # 桁チェックが必要な項目 NUM_CHECK = {issn: 8, isbn: 13} # 集約される項目 SYNONYMS = [:title, :aulast, :aufirst] # enjuのフィールド名(検索用)管理 ENJU_FIELD = {aulast: 'au_text', # aulast=au aufirst: 'au_text', # aufirst=au au: 'au_text', title: 'btitle_text', # title=btitle atitle: 'atitle_text', btitle: 'btitle_text', jtitle: 'jtitle_text', pub: 'publisher_text', issn: 'issn_sm', isbn: 'isbn_sm', ndl_jpno: 'ndl_jpno_text', # TODO:現在対応項目はないので保留。 ndl_dpid: 'ndl_dpid_sm', # TODO:現在対応項目はないので保留。これのみ完全一致であることに注意。 associate: '' # TODO:フィールド名ではないので削除? } def initialize(params) # @openurl_queryに検索項目ごとの検索文を格納 if params.key?(:any) then # anyの場合は他に条件が指定されていても無視 @openurl_query = [params[:any].strip] else @openurl_query = to_sunspot(params) end @manifestations = [] end attr_reader :openurl_query, :manifestations, :query_text # 検索 def search search = Sunspot.new_search(Manifestation) @query_text = build_query query = @query_text search.build{ fulltext query } search.execute!.results end private # 検索文を結合, def build_query @openurl_query.join(" AND ") end # params OpenURLのリクエストのパラメータ # 個々のパラメータに合わせて検索文を組立てメソッドを呼ぶ。この時、引数は、enjuのフィールド名と値。 def to_sunspot(params) query = [] params.each_pair do |key, _value| key = key.to_sym if MATCH_EXACT.include?(key) then # 完全一致 query << to_sunspot_match_exact(ENJU_FIELD[key], params[key].strip) elsif MATCH_PART.include?(key) then # 部分一致 query << to_sunspot_match_part(key, ENJU_FIELD[key], params[key].strip) elsif MATCH_AHEAD.include?(key) then # 前方一致 query << to_sunspot_match_ahead(key, ENJU_FIELD[key], params[key].strip) end end # queryにあるau_textの統合 @openurl_query = unite_au_query(query) end # 完全一致の項目の検索文組立て # TODO 完全一致の項目はndl_dpidだけで、これはデータプロバイダについて議論中なので保留する def to_sunspot_match_exact(field, val) # 途中に空白がある場合は複数語が指定されているとみなし、ORでつなぐ。 if /\s+/ =~ val "%s:%s" % [field, val.gsub(/\s+/, ' OR ')] else "%s:%s" % [field, val] end end # 部分一致の項目の検索文組立て # key パラメータのキー(複数指定の検索方法判断に使う) # field 項目名 # val 値 def to_sunspot_match_part(key, field, val) # 途中に空白がある場合は複数語が指定されているとみなし、ANDでつなぐ。 if /\s+/ =~ val if LOGIC_MULTI_AND.include?(key) then "%s:(%s)" % [field, val.gsub(/\s+/, ' AND ')] else raise OpenurlQuerySyntaxError, "the key \"#{key}\" not allow multi words" end else "%s:%s" % [field, val] # fieldに対して語が1つならばこれ end end # 前方一致の項目の検索文組立て def to_sunspot_match_ahead(key, field, val) # 既定の桁より大きい場合、または、数値でない文字列の場合エラー # 数値でない文字列には空白がある場合も含むので複数指定はエラーとなる # このチェックはANY検索の時外す if NUM_CHECK.include?(key) then if [:issn, :isbn].include?(key.to_sym) val.gsub!('-', '') end raise OpenurlQuerySyntaxError unless /\A\d{1,#{NUM_CHECK[key]}}X?\Z/i =~ val end "%s:%s*" % [field, val] end # au項目の統合 # au、aufirst、aulastはau_textに統合する def unite_au_query(query) new_query = [] au_item = [] str = "au_text:" au_flg = false reg = Regexp.compile(/\A#{str}/) query.each do |q| if reg =~ q then au_flg = true au_item.push(q[str.length..q.length]) else new_query.push(q) end end if au_flg then if au_item.size > 1 then str = str + "(%s)" % [au_item.join(' AND ')] else str = str + au_item[0] end new_query.push(str) end return new_query end end