Elasticsearch 自动补全功能实现

运行环境

sh es版本:7.12.1

ik分词器版本:7.12.1

拼音分词器:7.12.1

尽量版本一致有兼容问题

首先我们分析为什么用户输入xiao 的时候为什么下面能提示出以xiao 开头的数据?

我们在存入数据的时候需要拼音分词器对要存进es数据库的词条进行分词,这样用户就可以通过拼音搜索出对应的词条,需要额外的加一个字段存放用户输入时补全的数据,再通过 completion suggest 查询出补全的数据

1 拼音分词器的使用

拼音分词器简单使用示例

POST /_analyze
{
  "text": ["小米手机"],
  "analyzer": "pinyin"
}

可以看到响应的结果是有缺点的,不符合我们的实际应用

1.一字一分

2.汉字没有分词

3.用户用汉字查询多,这里都是拼音

1.1 解决方案  自定义分词器

自定义分词器分为三部分别是character filter 字符过滤器, tokenizer 分词器, tokenizer fliter 过滤器

character filter 字符过滤器是分词之前对词条进行处理

tokenizer 分词器:指定分词器 

fliter 过滤器:对分好词条再次进行处理

1.2 声明自定义分词器的语法如下:

创建自定义分词器的索引,自定义分词器只能在当前索引下使用

  1. PUT /test:这是请求的路径,表示创建一个名为test的索引。

  2. "settings":这是索引设置的参数,用于配置索引的分析器和过滤器。

  3. "analysis":这是分析器和过滤器的设置。

  4. "analyzer":这是分析器(analyzer)的配置,可以自定义名称。

  5. "my_analyzer":这是分析器的名称,可以自定义。

  6. "tokenizer": "ik_max_word":这是分析器使用的分词器(tokenizer),这里使用的是IK分词器,它可以将中文文本切分成单个词语。

  7. "filter": "py":这是使用的分析器的过滤器(filter),这里使用的是名为py的过滤器。

  8. "filter":这是过滤器(filter)的配置。

  9. "py":这是过滤器的名称,可以自定义。

  10. "type": "pinyin":这是过滤器的类型,表示要将中文转换为拼音。

  11. "keep_full_pinyin": false:这是一个设置参数,表示拼音转换后是否保留完整拼音的设置,这里设置为不保留。

  12. "keep_joined_full_pinyin": true:这是一个设置参数,表示拼音转换后是否保留拼音之间的连字符的设置,这里设置为保留。

  13. "keep_original": true:这是一个设置参数,表示是否保留原始文本的设置,这里设置为保留。

  14. "limit_first_letter_length": 16:这是一个设置参数,表示拼音转换后首字母的长度限制,这里设置为16。

  15. "remove_duplicated_term": true:这是一个设置参数,表示是否去除重复的结果的设置,这里设置为去除。

  16. "none_chinese_pinyin_tokenize": false:这是一个设置参数,表示是否对非中文文本进行拼音切分的设置,这里设置为不切分。

  17. "mappings":这是索引的映射(mapping)设置。

  18. "properties":这是映射中的字段属性设置。

  19. "name":这是字段名称,可以自定义。

  20. "type": "text":这是字段的类型,表示该字段包含文本内容。

  21. "analyzer": "my_analyzer":这是字段的分析器设置,表示使用之前定义的my_analyzer分析器来处理该字段的文本内容。

PUT /test
{
  "settings": {//配置	
    "analysis": {//分析
      "analyzer": { // 自定义分词器
        "my_analyzer": {  // 分词器名称
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      "filter": { // 自定义tokenizer filter
        "py": { // 过滤器名称
          "type": "pinyin", // 过滤器类型,这里是pinyin
		  "keep_full_pinyin": false,//如果设置为true,则会将完整的拼音保留下来
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "my_analyzer",
        "search_analyzer": "ik_smart"
      }
    }
  }
}

加入两条数据

POST /test/_doc/1
{
  "id": 1,
  "name": "狮子"
}
POST /test/_doc/2
{
  "id": 2,
  "name": "虱子"
}

模拟用户搜索

GET /test/_search
{
  "query": {
    "match": {
      "name": "掉入狮子笼咋办"
    }
  }
}

响应结果

当用户查询:掉入狮子笼咋办时返回的数据同音字一并返回了,还是有缺陷

解决办法

我们在创建索引时指定分词时用自定义分词,用户搜索时用ik分词器

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": { 
        "my_analyzer": { 
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      "filter": {
        "py": { 
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name":{
        "type": "text",
        "analyzer": "my_analyzer",
        "search_analyzer": "ik_smart"
      }
    }
  }
}

再看结果

2 completion suggest 查询

es提供了completion suggest 查询来实现自动补全的功能,这个查询会匹配用户输入开头的词条并返回 

参与补全查询的字段必须是completion类型的

字段里内容就是补全的多个词条

2.1 completion suggest示例

# 自动补全的索引库
PUT /test2
{
  "mappings": {
    "properties": {
      "title":{
        "type": "completion"
      }
    }
  }
}

添加数据

# 示例数据
POST test2/_doc
{
  "title": ["Sony", "WH-1000XM3"]
}
POST test2/_doc
{
  "title": ["SK-II", "PITERA"]
}
POST test2/_doc
{
  "title": ["Nintendo", "switch"]
}

2.2 suggest 查询语法

  1. "suggest":这是一个建议(suggestion)查询的参数,用于实现搜索建议功能。

  2. "title_suggest":这是建议查询的名称,可以自定义。

  3. "text": "s":这是用户输入的搜索文本,表示用户输入的内容是"s"。

  4. "completion":这是建议查询的类型,表示希望通过补全(completion)的方式提供搜索建议。

  5. "field": "title":这是需要进行建议的字段名称,表示希望对title字段进行建议。

  6. "skip_duplicates": true:这是一个可选参数,表示在搜索建议中是否跳过重复结果。

  7. "size": 10:这是可选参数,表示希望返回的建议结果数量,这里设置为10个。

# 自动补全查询
GET /test2/_search
{
  "suggest": {
    "title_suggest": {
      "text": "s", 
      "completion": {
        "field": "title", 
        "skip_duplicates": true, 
        "size": 10 
      }
    }
  }
}

查询结果如下, 我们只需要取suggest下的title_suggest下的 options下的text字段就是我们要补全的数据

2.3 RestAPI实现自动补全

3 自动补全实现综合案例

 3.1 创建hotel自定义分词器索引
# 酒店数据索引库
PUT /hotel
{
  "settings": {
    "analysis": {
      "analyzer": {
        "text_anlyzer": {
          "tokenizer": "ik_max_word",
          "filter": "py"
        },
        "completion_analyzer": {
          "tokenizer": "keyword",
          "filter": "py"
        }
      },
      "filter": {
        "py": {
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "id":{
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "text_anlyzer",
        "search_analyzer": "ik_smart",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword",
        "index": false
      },
      "price":{
        "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
        "type": "keyword"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword",
        "copy_to": "all"
      },
      "location":{
        "type": "geo_point"
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "text_anlyzer",
        "search_analyzer": "ik_smart"
      },
      "suggestion":{
          "type": "completion",
          "analyzer": "completion_analyzer"
      }
    }
  }
}
3.2 添加数据库数据
/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 50528
 Source Host           : localhost:3306
 Source Schema         : hotel

 Target Server Type    : MySQL
 Target Server Version : 50528
 File Encoding         : 65001

 Date: 22/10/2023 14:04:32
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_hotel
-- ----------------------------
DROP TABLE IF EXISTS `tb_hotel`;
CREATE TABLE `tb_hotel`  (
  `id` bigint(20) NOT NULL COMMENT '酒店id',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '酒店名称',
  `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '酒店地址',
  `price` int(10) NOT NULL COMMENT '酒店价格',
  `score` int(2) NOT NULL COMMENT '酒店评分',
  `brand` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '酒店品牌',
  `city` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '所在城市',
  `star_name` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '酒店星级,1星到5星,1钻到5钻',
  `business` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商圈',
  `latitude` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '纬度',
  `longitude` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '经度',
  `pic` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '酒店图片',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;

-- ----------------------------
-- Records of tb_hotel
-- ----------------------------
INSERT INTO `tb_hotel` VALUES (36934, '7天连锁酒店(上海宝山路地铁站店)', '静安交通路40号', 336, 37, '7天酒店', '上海', '二钻', '四川北路商业区', '31.251433', '121.47522', 'https://m.tuniucdn.com/fb2/t1/G1/M00/3E/40/Cii9EVkyLrKIXo1vAAHgrxo_pUcAALcKQLD688AAeDH564_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (38609, '速8酒店(上海赤峰路店)', '广灵二路126号', 249, 35, '速8', '上海', '二钻', '四川北路商业区', '31.282444', '121.479385', 'https://m.tuniucdn.com/fb2/t1/G2/M00/DF/96/Cii-TFkx0ImIQZeiAAITil0LM7cAALCYwKXHQ4AAhOi377_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (38665, '速8酒店上海中山北路兰田路店', '兰田路38号', 226, 35, '速8', '上海', '二钻', '长风公园地区', '31.244288', '121.422419', 'https://m.tuniucdn.com/fb2/t1/G2/M00/EF/86/Cii-Tlk2mV2IMZ-_AAEucgG3dx4AALaawEjiycAAS6K083_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (38812, '7天连锁酒店(上海漕溪路地铁站店)', '徐汇龙华西路315弄58号', 298, 37, '7天酒店', '上海', '二钻', '八万人体育场地区', '31.174377', '121.442875', 'https://m.tuniucdn.com/fb2/t1/G2/M00/E0/0E/Cii-TlkyIr2IEWNoAAHQYv7i5CkAALD-QP2iJwAAdB6245_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (39106, '7天连锁酒店(上海莘庄地铁站店)', '闵行莘庄镇七莘路299号', 348, 41, '7天酒店', '上海', '二钻', '莘庄工业区', '31.113812', '121.375869', 'https://m.tuniucdn.com/fb2/t1/G2/M00/D8/11/Cii-T1ku2zGIGR7uAAF1NYY9clwAAKxZAHO8HgAAXVN368_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (39141, '7天连锁酒店(上海五角场复旦同济大学店)', '杨浦国权路315号', 349, 38, '7天酒店', '上海', '二钻', '江湾、五角场商业区', '31.290057', '121.508804', 'https://m.tuniucdn.com/fb2/t1/G2/M00/C7/E3/Cii-T1knFXCIJzNYAAFB8-uFNAEAAKYkQPcw1IAAUIL012_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (45845, '上海西藏大厦万怡酒店', '虹桥路100号', 589, 45, '万怡', '上海', '四钻', '徐家汇地区', '31.192714', '121.434717', 'https://m.tuniucdn.com/fb3/s1/2n9c/48GNb9GZpJDCejVAcQHYWwYyU8T_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (45870, '上海临港豪生大酒店', '新元南路555号', 896, 45, '豪生', '上海', '四星级', '滴水湖临港地区', '30.871729', '121.81959', 'https://m.tuniucdn.com/fb3/s1/2n9c/2F5HoQvBgypoDUE46752ppnQaTqs_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (46829, '上海浦西万怡酒店', '恒丰路338号', 726, 46, '万怡', '上海', '四钻', '上海火车站地区', '31.242977', '121.455864', 'https://m.tuniucdn.com/fb3/s1/2n9c/x87VCoyaR8cTuYFZmKHe8VC6Wk1_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (47066, '上海浦东东站华美达酒店', '施新路958号', 408, 46, '华美达', '上海', '四钻', '浦东机场核心区', '31.147989', '121.759199', 'https://m.tuniucdn.com/fb3/s1/2n9c/2pNujAVaQbXACzkHp8bQMm6zqwhp_w200_h200_c1_t0.jpg');
INSERT INTO `tb_hotel` VALUES (4747
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值