ElasticSearch中内置了许多分词器, standard, english, chinese等, 中文分词效果不佳, 所以使用ik, 以及pinyin
brew install elasticsearch
cd /usr/local/Cellar/elasticsearch/6.4.2/bin
➜ bin ./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.2/elasticsearch-analysis-ik-6.4.2.zip
➜ bin ./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v6.4.2/elasticsearch-analysis-pinyin-6.4.2.zip
elasticsearch 配置文件
/usr/local/etc/elasticsearch/elasticsearch.yml
cluster.name: 集群名字
node.name: 节点名
path.data: 数据存储目录
path.logs: 日志存储目录
action.auto_create_index: false
# /usr/local/etc/elasticsearch/jvm.options
# 指定使用内存
-Xms128m
-Xmx128m
brew services list
brew services stop elasticsearch
brew services start elasticsearch
http://localhost:9200/products/product/1382
{
error: {
root_cause: [
{
type: "index_closed_exception",
reason: "closed",
index_uuid: "opkeQfWbTKSrJzx4BxwoXA",
index: "products"
}
],
type: "index_closed_exception",
reason: "closed",
index_uuid: "opkeQfWbTKSrJzx4BxwoXA",
index: "products"
},
status: 400
}
➜ work-api git:(release) ✗ curl -XPOST 'localhost:9200/products/_open?pretty'
{
"acknowledged" : true,
"shards_acknowledged" : true
}
➜
irb(main):02:0> Product.import
gem 'elasticsearch-model', '~> 5.0', '>= 5.0.1'
gem 'elasticsearch-rails', '~> 5.0', '>= 5.0.1'
development:
elasticsearch:
host: "http://127.0.0.1:9200"
require 'yaml'
setting_path = Rails.root.join('config', 'setting.yml')
$setting = YAML.load_file(setting_path)[Rails.env]
require Rails.root.join('config', 'initializers', 'setting')
config = {
host: $setting['elasticsearch']['host'],
transport_options: {
request: { open_timeout: 5, timeout: 5 }
},
}
$elasticsearch = config
Elasticsearch::Model.client = Elasticsearch::Client.new(config)
class Order
SyncModerElasticsearch
end
require "elasticsearch/model"
module SyncModerElasticsearch
extend ActiveSupport::Concern
included do
include Elasticsearch::Model
extend ElasticsearchCallback
# include Elasticsearch::Model::Callbacks
synchronization
mapping{
indexes :suggested_price, type: :float
indexes :lowest_price, type: :float
indexes :sales_volume, type: :integer
}
after_commit :sync_elasticsearch, on: [:create, :update]
end
def as_indexed_json(options = {})
{
id: id,
name: name,
name_cn: name_cn,
order_item: {
id: _id,
name: _name
}
}
end
private
def sync_elasticsearch
self.import
_table_name, _id = self.class.table_name, self.id
EsSyncJob.set(table_name: _table_name, id: _id)
end
end
module ElasticsearchCallback
extend ActiveSupport::Concern
def synchronization
after_commit on: [:create] do
begin
$logger.info('同步创建', self)
self.__elasticsearch__.index_document
rescue => e
$logger.info(self, e)
begin
self.__elasticsearch__.index_document
rescue => e
$logger.info(self, e)
end
end
end
after_commit on: [:update] do
begin
$logger.info('同步更新', self)
self.__elasticsearch__.update_document
rescue => e
$logger.info(self, e)
begin
self.__elasticsearch__.update_document
rescue => e
$logger.info(self, e)
end
end
end
after_commit on: [:destroy] do
begin
$logger.info('同步删除', self)
self.__elasticsearch__.delete_document
rescue => e
$logger.info(self, e)
begin
self.__elasticsearch__.delete_document
rescue => e
$logger.info(self, e)
end
end
end
end
end
class EsSyncJob < ApplicationJob
queue_as :default
def perform table_name,id
$logger.info('begin sync_elasticsearch queue')
begin
table_name.classify.safe_constantize.where(id: id).import
rescue => e
$logger.info("sync_elasticsearch error: #{e.message}")
end
end
end
class ElasticsearchService
def initialize model
@model = model
end
def search_without_paginate query, sort=nil, page = 1, per_page = 10, is_highlight = false
_query = query.is_a?(Hash) ? hash_query(query) : multi_match_query(query)
_highlight = is_highlight ? fetch_highlight_hash : {}
_sort = sort.nil? ? {id: :desc} : sort
begin
response = @model.search query: _query,
highlight: { fields: _highlight },
sort: _sort,
from: per_page * (page - 1),
size: 1000 #TODO
return response.results
rescue => e
$logger.info("es search_without_paginate error", e)
return []
end
end
def search query, sort=nil, page = 1, per_page = 10, is_highlight = false
_query = query.is_a?(Hash) ? hash_query(query) : multi_match_query(query)
_highlight = is_highlight ? fetch_highlight_hash : {}
_sort = sort.nil? ? {id: :desc} : sort
begin
response = @model.search query: _query,
highlight: { fields: _highlight },
sort: _sort,
from: per_page * (page - 1),
size: per_page
return [response.response['hits']['total'], response.results]
rescue => e
$logger.info("es search error", e)
struct = Struct.new :results
return [0, struct.new([])]
end
end
private
def hash_query query
filtered = {
bool: {
must: query[:filter].select{|key, value| !value.include?(' OR ')}.keys.map do |key|
{ match: query[:filter].slice(key) }
end
}
}
if query[:filter].select{|key, value| value.include?(' OR ')}.present?
should = query[:filter].select{|key, value| value.include?(' OR ')}.map do |key, values|
values.split(" OR ").map do |value|
{match: {key.to_sym => value} }
end
end.flatten
filtered[:bool][:must] << { bool: { should: [should] } }
end
filtered[:bool].merge!({filter: { range: query[:range] }}) if query[:range].present?
filtered[:bool].merge!({must_not: { term: {status: 'preparing'} }}) if query[:only_show]
return filtered
end
def multi_match_query query
fileds = fetch_multi_match_fields
{
bool: {
must: {
multi_match:{
query: query,
type: "best_fields",
fields: fileds,
operator: "and"
}
},
# filter: filter_term,
must_not: must_not_term
}
}
end
def filter_term
case @model.to_s
when 'Product'
{
not:{
filter:{
term: {status: 'selling'}
}
}
}
end
end
def must_not_term
case @model.to_s
when 'Product'
{
term: {status: 'preparing'}
}
end
end
def fetch_multi_match_fields
case @model.to_s
when 'Model'
return [
'name_cn^7', "name_cn.ik^6", "name_cn.pinyin^5", "name_cn.standard^4", "name_cn.english^3"
]
end
end
def fetch_highlight_hash
case @model.to_s
when 'Model'
return {
name_cn: {}, "name_cn.ik"=>{}, "name_cn.pinyin"=>{}, "name_cn.standard"=>{}, "name_cn.english"=>{}
}
end
end
end
filter_hash = {"order_no": order_no, "category.id": category_id }
range_hash = {"order_amount": {"lte": params[:order_amount]}}
sort = {id: :desc}
page = 1
per_page = 30
total, products = ElasticsearchService.new(Model).search({filter: filter_hash, range: range_hash, only_show: true}, sort, page.to_i, per_page.to_i)