电商场景下的搜索——基于ik分词进行es搜索的探索

836 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

前言

在第四天我们讲了一下如何使用kabana进行es的query调试。今天我们探索一下电商场景下的常用搜索,基于ik分词来搜索es数据

数据建模

在开发之前我们要确认需求。那些数据是需要搜索的?除了要实现搜索我们还要注意哪些问题?以淘宝为例,我们在搜索的时候有两个主要的地方,1是匹配词语,2是要实现排序。以下方的搜素结果我们分析一下需要在搜索的时候进行筛选和排序的字段 15774e5cd91b1fd5e34bd6a73401e3a.jpg

  • 搜索字段
  1. 规格(规格查询比较复杂,我们暂时先不讨论如何实现了)
  2. 名称
  3. 关键词或简介
  • 排序字段
  1. 如果投放广告话排序较前
  2. 销量
  3. 价格
  • 其他
  1. 分页搜索 基于以上我们给商品简单(goods)的建模为以下几个字段 id(唯一标识)、name(名称)、keyWord(关键字)、sellNum(销量)、price(价格)、sort(排序) 注意:我们这里没有深入分析,其实商品应该还有其他类似于上下架、是否推荐等信息

初始化数据

我们使用kabana初始化商品索引的过程如下

创建索引

PUT goods
{
	"settings": {
		"number_of_shards": 1,
		"number_of_replicas": 0
	}, 
        "mappings": {
	  "properties": {
	    "id":{
	      "type": "keyword"
	    },
	    "name":{
	      "type": "text",
	      "analyzer": "ik_max_word",
	      "search_analyzer": "ik_smart"
	    },
	    "keyWords":{
	      "type": "text",
	      "analyzer": "ik_max_word",
	      "search_analyzer": "ik_smart"
	    },
	    "sellNum":{
	      "type": "integer"
	    },
	    "price":{
	      "type": "double"
	    },
	    "sort":{
	      "type": "integer"
	    }
	  }
	}
}

常用的es数据结构可以参考这篇博文es常用数据结构,我简单介绍一下其中需要注意的点吧

  1. 分区和备份:这里大家在生产环境建议搞成多分区多备份的模式防止数据丢失
  2. id为什么是keyword类型:因为keyword是关键字数据类型,使用keyword类型的字段,其不会被分析,例如我们现在有一个数据的id为123456,另一个为12345,假如我们设置索引为text类型,如果我们想删除12345这条数据会同时把123456等含有12345的删除掉
  3. analyzer\search_analyzer分词类型选择:分词时我们分别插入文档时,将text类型的字段做最细的分词然后插入倒排索引,在查询时,先对要查询的text类型的输入做最粗分词,再去倒排索引搜索。举个例子:假如插入数据为"苹果手机"的时候我们尽可能多拆为"苹果","手机",我们在搜索的"小米手机"时候,就不会查询出苹果手机这条数据了

初始化数据

我们初始化9条数据验证一下我们的查询,其中3条广告,3条苹果,3条华为请求体如下所示

PUT goods/_doc/100001
{
  "id":"100001",
  "name":"广告位001",
  "keyWords":"手机 智能手机 5G手机 热搜预定手机",
  "sellNum":1,
  "price":12,
  "sort": 1
}
PUT goods/_doc/100002
{
  "id":"100002",
  "name":"广告位002",
  "keyWords":"手机 智能手机 5G手机 热搜预定手机",
  "sellNum":2,
  "price":11,
  "sort": 2
}
PUT goods/_doc/100003
{
  "id":"100003",
  "name":"广告位003",
  "keyWords":"手机 智能手机 5G手机 热搜预定手机",
  "sellNum":3,
  "price":10,
  "sort": 3
}
PUT goods/_doc/100004
{
  "id":"100004",
  "name":"苹果手机001",
  "keyWords":"手机 智能手机 IOS 苹果手机",
  "sellNum":4,
  "price":9,
  "sort": 4
}
PUT goods/_doc/100005
{
  "id":"100005",
  "name":"苹果手机002",
  "keyWords":"手机 智能手机 IOS 苹果手机",
  "sellNum":5,
  "price":8,
  "sort": 5
}
PUT goods/_doc/100006
{
  "id":"100006",
  "name":"苹果手机003",
  "keyWords":"手机 智能手机 IOS 苹果手机",
  "sellNum":6,
  "price":7,
  "sort": 6
}
PUT goods/_doc/100007
{
  "id":"100007",
  "name":"华为001",
  "keyWords":"手机 智能手机 麒麟 国产手机",
  "sellNum":7,
  "price":6,
  "sort": 7
}
PUT goods/_doc/100007
{
  "id":"100007",
  "name":"华为001",
  "keyWords":"手机 智能手机 麒麟 国产手机",
  "sellNum":7,
  "price":6,
  "sort": 7
}
PUT goods/_doc/100009
{
  "id":"100009",
  "name":"苹果手机003",
  "keyWords":"手机 智能手机 麒麟 国产手机",
  "sellNum":9,
  "price":4,
  "sort": 9
}

各个场景的实际探索

以下请求体都是放在kabana中执行的

最简单的搜索全部,按照销量倒排序,分页查询前5条,我们传入的传入的请求体如下

post goods/_search
{
  "query":{"match_all": {}},
  "from":0,
  "size":5,
  "sort":{
    "sort":"desc"
  }
}

这里解释一下几个关键属性吧

  • query:就是封装查询体的属性,例如匹配、匹配全部等等
  • from:从第几条开始,注意开始的位置是0
  • size:每页的条数
  • sort:外层的sort代表排序,,内层的sort指定排序字段就是sort并且按照desc排序 我们通过查看响应可以看到查询出所有的total为9hits显示了5条,证明我们实现了分页效果

输入手机且默认查询:默认排序,匹配名称或者关键字

例如输入智能手机,我们应该会查询出所有的数据(匹配到了keyWords)且前几条按照默认排序的话应该是广告位

POST goods/_search
{
	"query": {
		"bool": {
			"should": [{
					"match": {
						"name": "智能手机"
					}
				},
				{
					"match": {
						"keyWords": "智能手机"
					}
				}
			]
		}
	},
	"from": 0,
	"size": 5,
	"sort": {
		"sort": "asc"
	}
}

这里我们可以看到多了几个标签,我简单介绍一下 bool:这个的意思是过滤查询,下面还有很多和bool组合起来的属性,大家可以自己到es官网查找一波 should:意思是应该,例如我们这个案例在搜搜智能手机的时候should匹配到了keyWords属性上 match: 就是匹配的意思

我想买个麒麟系统的手机,且按照售价或销量来倒序查询

我们这里应该返回3条数据,我这里先将分页设置为一页2条,我们从第二条开始

  • 按照售价倒序查询含有麒麟的商品
post goods/_search
{
	"query": {
		"bool": {
			"should": [{
					"match": {
						"name": "麒麟"
					}
				},
				{
					"match": {
						"keyWords": "麒麟"
					}
				}
			]
		}
	},
	"from": 2,
	"size": 2,
	"sort": {
		"price": "desc"
	}
}
  • 按照销量倒序查询含有麒麟的商品
post goods/_search
{
	"query": {
		"bool": {
			"should": [{
					"match": {
						"name": "麒麟"
					}
				},
				{
					"match": {
						"keyWords": "麒麟"
					}
				}
			]
		}
	},
	"from": 2,
	"size": 2,
	"sort": {
		"sellNum": "desc"
	}
}

结语

本篇文章我们基于ik分词器分析了电商场景下的简单es搜索实现。关于es比较基本的入门文章更的差不多了,下一篇我们探讨一下如何用spring boot实现我们今天的我们今天的需求
下篇文章:基于spring boot 实现电商场景下的ik分词搜索,敬请期待!