linguisticexplorer/Linguistic-Explorer

View on GitHub
app/models/search_results/keyword_filter.rb

Summary

Maintainability
A
35 mins
Test Coverage
module SearchResults

  class KeywordFilter < Filter
    attr_accessor :strategy

    def initialize(filter, query)
      super
      yield self if block_given?
    end

    def strategy
      @strategy ||= :ling
    end

    def depth_0_vals
      @depth_0_vals ||= filter_vals(Depth::PARENT)
    end

    def depth_1_vals
      @depth_1_vals ||= filter_vals(Depth::CHILD)
    end

    private

    def strategy_class
      "SearchResults::#{@strategy.to_s.camelize}KeywordStrategy".constantize
    end

    def filter_vals(depth)
      @filter_strategy_instance ||= strategy_class.new(@filter, @query)
      result = @filter_strategy_instance.vals_at(depth)

      ##########################################################################
      # Trick to solve issue #1                                                #
      # This shows a NO_DEPTH_1_RESULT as result if no result is retrieved     #
      # in depth 1 search.                                                     #
      ##########################################################################
      is_depth_0?(depth) & any_error?(result) ? [] : result
    end

  end

  class KeywordStrategy

    def initialize(filter, query)
      @filter, @query = filter, query
    end

    def query_key
      "#{model_class.name.downcase.underscorize}_keywords".to_sym
    end

    def keyword(depth)
      @query[query_key][depth.to_s]
    end

    def group
      @query.group
    end

    def vals_at(depth)
      vals = @filter.vals_at(depth)
      keyword(depth).present? ? select_vals_by_keyword(vals, keyword(depth)) : vals
    end

    def select_vals_by_keyword(vals, keyword)
      result = LingsProperty.select_ids.where(:id => vals) & search_scope_name_by_keyword(keyword)

      result.empty? ? Filter::NO_DEPTH_1_RESULT : result
    end

    def search_scope_name_by_keyword(keyword)
      model_class.in_group(group).unscoped.
          where({:name.matches => "#{keyword}%"} | { :name.matches => "%#{keyword}%"})
    end

  end

  class LingKeywordStrategy < KeywordStrategy

    def model_class
      Ling
    end

  end

  class PropertyKeywordStrategy < KeywordStrategy

    def model_class
      Property
    end

    def keyword(category_id)
      @query[query_key][category_id.to_s]
    end

    def map_selected_vals_id(selected_vals)
      selected_vals == Filter::NO_DEPTH_1_RESULT ? [-1] : selected_vals.map(&:id)
    end

    def vals_at(depth)
      vals          = @filter.vals_at(depth)
      category_ids  = @query.group_prop_category_ids(depth)

      collected_ids = category_ids.collect do |category_id|
        if keyword(category_id).present?
          map_selected_vals_id(select_vals_by_keyword(vals, keyword(category_id)))
        else
          raise Exceptions::ResultTooBigError if vals.size > Search::RESULTS_FLATTEN_THRESHOLD
          map_selected_vals_id(vals)
        end
      end.flatten

      return collected_ids if collected_ids == Filter::NO_DEPTH_1_RESULT
      LingsProperty.with_id(collected_ids).select_ids
    end

  end

  class ExampleKeywordStrategy < KeywordStrategy
    # query: :example_fields=>{"0"=>["origin"], "1"=>["text"]}, :example_keywords=>{"0"=>"gold", "1"=>""}}

    def model_class
      Example
    end

    def keyword(depth)
      @query[query_key][depth.to_s]
    end

    def vals_at(depth)
      vals = @filter.vals_at(depth)
      keyword(depth).present? ? select_vals_by_keyword(vals, keyword(depth), depth) : vals
    end

    def select_vals_by_keyword(vals, keyword, depth)
      example_attribute = @query[:example_fields][depth.to_s].to_sym
      keyword_scope = case example_attribute
                        when :description
                          search_scope_name_by_keyword(keyword)
                        else
                          # keyword search by stored value key/pair
                          search_scope_value_by_stored_value_key_pair(keyword, example_attribute)
                      end

      LingsProperty.select_ids.where(:id => vals) & keyword_scope
    end

    def search_scope_value_by_stored_value_key_pair(keyword, key)
      model_class.unscoped.where(:group_id => group.id) &
        StoredValue.unscoped.with_key(key).
        where({:value.matches => "#{keyword}%"} | { :value.matches => "%#{keyword}%"})
    end

  end

end