商品搜索

配置索引 Mapping

  1. PUT goods
  2. {
  3. "mappings": {
  4. "dynamic": false,
  5. "properties": {
  6. "goods_id": {
  7. "type": "integer"
  8. },
  9. "store_id": {
  10. "type": "integer"
  11. },
  12. "goods_name": {
  13. "type" : "text",
  14. "analyzer" : "ik_max_word",
  15. "fields": {
  16. "word": {
  17. "type": "text",
  18. "analyzer": "standard"
  19. }
  20. }
  21. },
  22. "cate_id": {
  23. "type": "integer"
  24. },
  25. "brand_id": {
  26. "type": "integer"
  27. },
  28. "closed": {
  29. "type": "byte"
  30. },
  31. "is_deleted": {
  32. "type": "byte"
  33. },
  34. "if_show_b2b": {
  35. "type": "byte"
  36. },
  37. "wholesale_price": {
  38. "type": "double"
  39. },
  40. "sale_region": {
  41. "type": "integer"
  42. },
  43. "recommend_sort": {
  44. "type": "integer"
  45. },
  46. "last_update": {
  47. "type": "integer"
  48. },
  49. "cate_name": {
  50. "type" : "text",
  51. "analyzer" : "ik_max_word",
  52. "fields": {
  53. "word": {
  54. "type": "text",
  55. "analyzer": "standard"
  56. }
  57. }
  58. },
  59. "wholesale_sales": {
  60. "type": "integer"
  61. }
  62. }
  63. },
  64. "settings": {
  65. "analysis": {
  66. "analyzer": {
  67. "ik_max_word_html_strip": {
  68. "tokenizer": "ik_max_word",
  69. "char_filter": ["html_strip"]
  70. }
  71. }
  72. }
  73. }
  74. }

Logstash 配置同步 Mysql 数据

为了方便管理,建议每个 Index 索引设置一个配置文件夹

  1. # 创建索引goods配置目录
  2. mkdir /logstash/config/goods
  3. # 创建JDBC同步配置文件
  4. touch /logstash/config/goods/jdbc.conf
  5. # 创建JDBC同步配置SQL文件
  6. touch /logstash/config/goods/jdbc.sql
  1. input {
  2. jdbc {
  3. # mysql 数据库链接,db_fengche为数据库名 ?tinyInt1isBit=false 禁止整型转换为bool
  4. jdbc_connection_string => "jdbc:mysql://192.168.9.201/db_fengche?tinyInt1isBit=false"
  5. # 用户名和密码
  6. jdbc_user => "db_fengche"
  7. jdbc_password => "HbDZF63D5865BjjE"
  8. # 驱动
  9. jdbc_driver_library => "/logstash/logstash-core/lib/jars/mysql-connector-java-8.0.28.jar"
  10. # 驱动类名
  11. jdbc_driver_class => "com.mysql.jdbc.Driver"
  12. jdbc_paging_enabled => "true"
  13. jdbc_page_size => "50000"
  14. # 启动追踪,如果为true,则需要指定 tracking_colum
  15. use_column_value => true
  16. # 指定追踪的字段
  17. tracking_column => "last_update"
  18. # 追踪字段的类型,目前只有数字(numeric)和时间类型(timestamp),默认是 numeric
  19. tracking_column_type => "numeric"
  20. # 记录最后一次运行的结果
  21. record_last_run => true
  22. # 上次运行结果的保存位置
  23. last_run_metadata_path => "/logstash/config/goods/jdbc-position.txt"
  24. # 执行的sql 文件路径+名称
  25. statement_filepath => "/logstash/config/goods/jdbc.sql"
  26. # 设置监听间隔 各字段含义(由左至右)分、时、天、月、年,全部为*默认含义为每分钟都更新
  27. schedule => "* * * * *"
  28. }
  29. }
  30. output {
  31. elasticsearch {
  32. # ES的IP地址及端口
  33. hosts => ["192.168.9.220:9200"]
  34. # 索引名称
  35. index => "goods"
  36. document_type => "_doc"
  37. # 自增ID 需要关联的数据库中有有一个id字段,对应索引的id号
  38. document_id => "%{goods_id}"
  39. }
  40. stdout {
  41. # JSON格式输出
  42. codec => json_lines
  43. }
  44. }
  1. SELECT
  2. g.goods_id,
  3. g.store_id,
  4. g.goods_name,
  5. g.cate_id,
  6. g.brand_id,
  7. g.closed,
  8. g.is_deleted,
  9. g.if_show_b2b,
  10. g.wholesale_price,
  11. g.sale_region,
  12. g.recommend_sort,
  13. g.last_update,
  14. gc.cate_name,
  15. st.wholesale_sales
  16. FROM
  17. fc_goods AS g
  18. INNER JOIN fc_gcategory AS gc
  19. ON g.cate_id = gc.cate_id
  20. LEFT JOIN fc_goods_statistics AS st
  21. ON st.goods_id = g.goods_id
  22. WHERE g.last_update > :sql_last_value
  1. /logstash/bin/logstash -f /logstash/config/goods/jdbc.conf

PHP 项目开发

安装 Elasticsearch 插件

  1. composer require elasticsearch/elasticsearch "elasticsearch/elasticsearch":"7.1.0"

控制器测试代码

基于 thinkphp6.0 框架

  1. <?php
  2. namespace app\controller;
  3. use app\BaseController;
  4. use think\facade\Request;
  5. use app\logic\ElasticsearchLogic;
  6. class Elasticsearch extends BaseController
  7. {
  8. public function get_goods_list()
  9. {
  10. $keyword = '车灯';
  11. $where = array(
  12. 'closed' => 0,
  13. 'keyword' => $keyword,
  14. 'is_deleted' => 0,
  15. 'if_show_b2b' => 1,
  16. 'cate_id' => [],
  17. 'brand_id' => [],
  18. 'goods_id' => [],
  19. 'store_id' => []
  20. );
  21. $page_no = Request::post('page_no/d', 1);
  22. $page_size = Request::post('page_size/d', 10);
  23. $elasticsearchLogic = new ElasticsearchLogic();
  24. $elasticsearchLogic->goodsSearch($where, build_limit($page_no, $page_size));
  25. }
  26. }

逻辑代码

根据关键字搜索商品名称进行关键字相关度搜索,结合商品上下架等匹配和排序,完成搜索。

  1. <?php
  2. namespace app\logic;
  3. use \Elasticsearch\ClientBuilder;
  4. class ElasticsearchLogic
  5. {
  6. public $client;
  7. public function __construct()
  8. {
  9. $params = array(
  10. '192.168.9.220:9200'
  11. );
  12. $this->client = ClientBuilder::create()->setHosts($params)->build();
  13. }
  14. public function goodsSearch(array $where = [], array $limit = [])
  15. {
  16. // 索引名称
  17. $params['index'] = 'goods';
  18. // 文档类型
  19. $params['type'] = '_doc';
  20. // 返回字段
  21. $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"];
  22. // 请求体
  23. $params['body']['query'] = [];
  24. // 关键字相关度匹配搜索
  25. if ($where['keyword']) {
  26. // should:选择性匹配,其中一项匹配即可,贡献算分
  27. $should = [];
  28. $keyword = $where['keyword'];
  29. $should[] = $this->bulid_match('goods_name', $keyword);
  30. $should[] = $this->bulid_match('goods_name.word', $keyword, 0.2);
  31. $params['body']['query']['bool']['should'] = $should;
  32. }
  33. // filter:必须匹配,但是不贡献算分,高速缓存
  34. $filter = [];
  35. if (isset($where['closed'])) {
  36. $filter[] = $this->bulid_match('closed', $where['closed']);
  37. }
  38. if (isset($where['is_deleted'])) {
  39. $filter[] = $this->bulid_match('is_deleted', $where['is_deleted']);
  40. }
  41. if (isset($where['if_show_b2b'])) {
  42. $filter[] = $this->bulid_match('if_show_b2b', $where['if_show_b2b']);
  43. }
  44. if (isset($where['cate_id']) && $where['cate_id']) {
  45. $filter[] = $this->bulid_terms('cate_id', $where['cate_id']);
  46. }
  47. if (isset($where['brand_id']) && $where['brand_id']) {
  48. $filter[] = $this->bulid_terms('brand_id', $where['brand_id']);
  49. }
  50. if (isset($where['goods_id']) && $where['goods_id']) {
  51. $filter[] = $this->bulid_terms('goods_id', $where['goods_id']);
  52. }
  53. if (isset($where['store_id']) && $where['store_id']) {
  54. $filter[] = $this->bulid_terms('store_id', $where['store_id']);
  55. }
  56. $params['body']['query']['bool']['filter'] = $filter;
  57. $params['body']['sort'] = [["_score"=> ['order'=> 'desc']], ['wholesale_sales'=> ['order'=> 'desc']], ['last_update'=> ['order'=> 'desc']]];
  58. $response = $this->client->search($params);
  59. }
  60. protected function bulid_match($queryName, $queryValue, $boost = 1)
  61. {
  62. $type = 'match';
  63. if (is_array($queryValue)) {
  64. $type = 'terms';
  65. }
  66. if ($boost === 1) {
  67. return [$type => [$queryName => $queryValue]];
  68. } else {
  69. return [$type => [$queryName => ["query" => $queryValue, 'boost' => 0.2]]];
  70. }
  71. }
  72. }