商品搜索
配置索引 Mapping
PUT goods{"mappings": {"dynamic": false,"properties": {"goods_id": {"type": "integer"},"store_id": {"type": "integer"},"goods_name": {"type" : "text","analyzer" : "ik_max_word","fields": {"word": {"type": "text","analyzer": "standard"}}},"cate_id": {"type": "integer"},"brand_id": {"type": "integer"},"closed": {"type": "byte"},"is_deleted": {"type": "byte"},"if_show_b2b": {"type": "byte"},"wholesale_price": {"type": "double"},"sale_region": {"type": "integer"},"recommend_sort": {"type": "integer"},"last_update": {"type": "integer"},"cate_name": {"type" : "text","analyzer" : "ik_max_word","fields": {"word": {"type": "text","analyzer": "standard"}}},"wholesale_sales": {"type": "integer"}}},"settings": {"analysis": {"analyzer": {"ik_max_word_html_strip": {"tokenizer": "ik_max_word","char_filter": ["html_strip"]}}}}}
Logstash 配置同步 Mysql 数据
为了方便管理,建议每个 Index 索引设置一个配置文件夹
# 创建索引goods配置目录mkdir /logstash/config/goods# 创建JDBC同步配置文件touch /logstash/config/goods/jdbc.conf# 创建JDBC同步配置SQL文件touch /logstash/config/goods/jdbc.sql
input {jdbc {# mysql 数据库链接,db_fengche为数据库名 ?tinyInt1isBit=false 禁止整型转换为booljdbc_connection_string => "jdbc:mysql://192.168.9.201/db_fengche?tinyInt1isBit=false"# 用户名和密码jdbc_user => "db_fengche"jdbc_password => "HbDZF63D5865BjjE"# 驱动jdbc_driver_library => "/logstash/logstash-core/lib/jars/mysql-connector-java-8.0.28.jar"# 驱动类名jdbc_driver_class => "com.mysql.jdbc.Driver"jdbc_paging_enabled => "true"jdbc_page_size => "50000"# 启动追踪,如果为true,则需要指定 tracking_columuse_column_value => true# 指定追踪的字段tracking_column => "last_update"# 追踪字段的类型,目前只有数字(numeric)和时间类型(timestamp),默认是 numerictracking_column_type => "numeric"# 记录最后一次运行的结果record_last_run => true# 上次运行结果的保存位置last_run_metadata_path => "/logstash/config/goods/jdbc-position.txt"# 执行的sql 文件路径+名称statement_filepath => "/logstash/config/goods/jdbc.sql"# 设置监听间隔 各字段含义(由左至右)分、时、天、月、年,全部为*默认含义为每分钟都更新schedule => "* * * * *"}}output {elasticsearch {# ES的IP地址及端口hosts => ["192.168.9.220:9200"]# 索引名称index => "goods"document_type => "_doc"# 自增ID 需要关联的数据库中有有一个id字段,对应索引的id号document_id => "%{goods_id}"}stdout {# JSON格式输出codec => json_lines}}
SELECTg.goods_id,g.store_id,g.goods_name,g.cate_id,g.brand_id,g.closed,g.is_deleted,g.if_show_b2b,g.wholesale_price,g.sale_region,g.recommend_sort,g.last_update,gc.cate_name,st.wholesale_salesFROMfc_goods AS gINNER JOIN fc_gcategory AS gcON g.cate_id = gc.cate_idLEFT JOIN fc_goods_statistics AS stON st.goods_id = g.goods_idWHERE g.last_update > :sql_last_value
/logstash/bin/logstash -f /logstash/config/goods/jdbc.conf
PHP 项目开发
安装 Elasticsearch 插件
composer require elasticsearch/elasticsearch "elasticsearch/elasticsearch":"7.1.0"
控制器测试代码
基于 thinkphp6.0 框架
<?phpnamespace app\controller;use app\BaseController;use think\facade\Request;use app\logic\ElasticsearchLogic;class Elasticsearch extends BaseController{public function get_goods_list(){$keyword = '车灯';$where = array('closed' => 0,'keyword' => $keyword,'is_deleted' => 0,'if_show_b2b' => 1,'cate_id' => [],'brand_id' => [],'goods_id' => [],'store_id' => []);$page_no = Request::post('page_no/d', 1);$page_size = Request::post('page_size/d', 10);$elasticsearchLogic = new ElasticsearchLogic();$elasticsearchLogic->goodsSearch($where, build_limit($page_no, $page_size));}}
逻辑代码
根据关键字搜索商品名称进行关键字相关度搜索,结合商品上下架等匹配和排序,完成搜索。
<?phpnamespace app\logic;use \Elasticsearch\ClientBuilder;class ElasticsearchLogic{public $client;public function __construct(){$params = array('192.168.9.220:9200');$this->client = ClientBuilder::create()->setHosts($params)->build();}public function goodsSearch(array $where = [], array $limit = []){// 索引名称$params['index'] = 'goods';// 文档类型$params['type'] = '_doc';// 返回字段$params['body']['_source'] = ["closed", "goods_name", "cate_name", "is_deleted", "if_show_b2b", "cate_id", "brand_id", "goods_id", "store_id", "wholesale_sales", "last_update"];// 请求体$params['body']['query'] = [];// 关键字相关度匹配搜索if ($where['keyword']) {// should:选择性匹配,其中一项匹配即可,贡献算分$should = [];$keyword = $where['keyword'];$should[] = $this->bulid_match('goods_name', $keyword);$should[] = $this->bulid_match('goods_name.word', $keyword, 0.2);$params['body']['query']['bool']['should'] = $should;}// filter:必须匹配,但是不贡献算分,高速缓存$filter = [];if (isset($where['closed'])) {$filter[] = $this->bulid_match('closed', $where['closed']);}if (isset($where['is_deleted'])) {$filter[] = $this->bulid_match('is_deleted', $where['is_deleted']);}if (isset($where['if_show_b2b'])) {$filter[] = $this->bulid_match('if_show_b2b', $where['if_show_b2b']);}if (isset($where['cate_id']) && $where['cate_id']) {$filter[] = $this->bulid_terms('cate_id', $where['cate_id']);}if (isset($where['brand_id']) && $where['brand_id']) {$filter[] = $this->bulid_terms('brand_id', $where['brand_id']);}if (isset($where['goods_id']) && $where['goods_id']) {$filter[] = $this->bulid_terms('goods_id', $where['goods_id']);}if (isset($where['store_id']) && $where['store_id']) {$filter[] = $this->bulid_terms('store_id', $where['store_id']);}$params['body']['query']['bool']['filter'] = $filter;$params['body']['sort'] = [["_score"=> ['order'=> 'desc']], ['wholesale_sales'=> ['order'=> 'desc']], ['last_update'=> ['order'=> 'desc']]];$response = $this->client->search($params);}protected function bulid_match($queryName, $queryValue, $boost = 1){$type = 'match';if (is_array($queryValue)) {$type = 'terms';}if ($boost === 1) {return [$type => [$queryName => $queryValue]];} else {return [$type => [$queryName => ["query" => $queryValue, 'boost' => 0.2]]];}}}
