简体   繁体   中英

Rails Elasticsearch - multiple filter terms

I have a model Campaign which can be created by a user of types: "NonProfit" and "Individual".

Now I want to search campaigns who are created by either NonProfit or Individual only, or by any of them.

Here is my model:

class Campaign < ActiveRecord::Base
after_commit lambda { __elasticsearch__.index_document  },  on: :create
after_commit lambda { __elasticsearch__.update_document },  on: :update

belongs_to :user

enum status: { waiting: 0, approved: 1, disapproved: 2, expired: 3, canceled: 4, draft: 5, published: 6}


def user_type
   self.user.rolable_type
end

def as_json(options={})
  super(:only => [:id, :status, :title, :description],
        :methods => [:user_type])
end

settings index: {
number_of_shards: 1,
analysis: {
  filter: {
    trigrams_filter: {
      type: 'ngram',
      min_gram: 2,
      max_gram: 10
    },
    content_filter: {
      type: 'ngram',
      min_gram: 4,
      max_gram: 20
    }
  },
  analyzer: {
    index_trigrams_analyzer: {
      type: 'custom',
      tokenizer: 'standard',
      filter: ['lowercase', 'trigrams_filter', 'asciifolding']
    },
    search_trigrams_analyzer: {
      type: 'custom',
      tokenizer: 'whitespace',
      filter: ['lowercase']
    },
    english: {
      tokenizer: 'standard',
      filter: ['standard', 'lowercase', 'content_filter']
    },
    czech: {
      tokenizer: 'standard',
      filter: ['standard','lowercase','content_filter', 'asciifolding']
    }
  }
}
} do
mappings dynamic: 'false' do
  indexes :status, type: 'string'
  indexes :user_type, type: 'string'
  indexes :en_title, index_analyzer: 'english', search_analyzer: 'english'
  indexes :ma_title, index_analyzer: 'czech', search_analyzer: 'czech'
  indexes :cs_title, index_analyzer: 'czech', search_analyzer: 'czech'
  indexes :en_description, index_analyzer: 'english', search_analyzer: 'english'
  indexes :ma_description, index_analyzer: 'czech', search_analyzer: 'czech'
  indexes :cs_description, index_analyzer: 'czech', search_analyzer: 'czech'
end
end

  def as_indexed_json(options={})
{ id: id,
  status: status,
  user_type: user_type,
  ma_title: ma_title,
  cs_title: cs_title,
  en_title: en_title,
  ma_description: ma_description,
  cs_description: cs_description,
  en_description: en_description  
}
  end

   def self.search(query, user_type)
__elasticsearch__.search(
  {
    query: {
      filtered: {
        query: {
          multi_match: {
            query: query,
            fields: ['ma_title^10', 'cs_title^10', 'en_title^10', 'ma_description', 'cs_description', 'en_description']
          }
        },
        filter: {
          term: {
            status: "published"               
          },
          term: {
            user_type: user_type
          }
        }
      }
    }       
  }
)
 end

end

My Controller would look something like this:

Campaign.search(params[:q], 'NonProfit') # to search only NonProfit campaigns
Campaign.search(params[:q], 'Individual') # to search only Individual campaigns
Campaign.search(params[:q], ['NonProfit','Individual']) # to search any of the campaigns

However I always get 0 results. I works just fine when I remove the filter term 'user_type'. I am not succesffull to get the user_type filter term working as expected.

Any idea, how to make it working? Thanks, Miroslav

UPDATE 1 (the filter user_type still does not work - 0 results):

def as_json(options={})
  super(:only => [:id, :status, :title, :description],
        :methods => [:user_type]
     #   :include => {
     #     :employers => {:only => [:title]},
     #     :roles => {:only => [:name]}
     #   }
  )
end

settings index: {
number_of_shards: 1,
analysis: {
  filter: {
    trigrams_filter: {
      type: 'ngram',
      min_gram: 2,
      max_gram: 10
    },
    content_filter: {
      type: 'ngram',
      min_gram: 4,
      max_gram: 20
    }
  },
  analyzer: {
    index_trigrams_analyzer: {
      type: 'custom',
      tokenizer: 'standard',
      filter: ['lowercase', 'trigrams_filter', 'asciifolding']
    },
    search_trigrams_analyzer: {
      type: 'custom',
      tokenizer: 'whitespace',
      filter: ['lowercase']
    },
    english: {
      tokenizer: 'standard',
      filter: ['standard', 'lowercase', 'content_filter']
    },
    czech: {
      tokenizer: 'standard',
      filter: ['standard','lowercase','content_filter', 'asciifolding']
    }
  }
}
} do
mappings dynamic: 'false' do
  indexes :status, type: 'string', index: 'not_analyzed'
  indexes :user_type, type: 'string', index: 'not_analyzed'
  indexes :en_title, index_analyzer: 'english', search_analyzer: 'english'
  indexes :ma_title, index_analyzer: 'czech', search_analyzer: 'czech'
  indexes :cs_title, index_analyzer: 'czech', search_analyzer: 'czech'
  indexes :en_description, index_analyzer: 'english', search_analyzer: 'english'
  indexes :ma_description, index_analyzer: 'czech', search_analyzer: 'czech'
  indexes :cs_description, index_analyzer: 'czech', search_analyzer: 'czech'
end
end

def as_indexed_json(options={})
{ id: id,
  status: status,
  ma_title: ma_title,
  cs_title: cs_title,
  en_title: en_title,
  ma_description: ma_description,
  cs_description: cs_description,
  en_description: en_description,
  :methods => [:user_type]
}
end




def self.search(query, user_type)
__elasticsearch__.search(
  {
    query: {
      filtered: {
        query: {
          multi_match: {
            query: query,
            fields: ['ma_title^10', 'cs_title^10', 'en_title^10', 'ma_description', 'cs_description', 'en_description']
          }
        },
        filter: {
          bool: {
            must: [
              {
                term: {
                  status: "published"
                }
              },
              {
                terms: {
                  user_type: user_type
                }
              }
            ]
          }
        }
      }
    }       
  }
)
end

You need to add "index" : "not_analyzed" to your mapping for the fields status and user_type . As you did not specify any analyzer ES was using standard analyzer for those fields.

mappings dynamic: 'false' do
  indexes :status, type: 'string', index : 'not_analyzed' <--- here
  indexes :user_type, type: 'string', index : 'not_analyzed' <--- here

If you need to use term filter, you need to make sure it is exact match as term queries do not perform any analysis.

Also for Campaign.search(params[:q], ['NonProfit','Individual']) # to search any of the campaigns you need to use terms filter as you are searching for more than one value.

EDIT Terms Query

Terms Query expect array of values

Campaign.search(params[:q], ['NonProfit'])

Try this

{
   "terms": {
      "user_type": ['NonProfit']
   }
 }

I hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM