- 专业全文索引是怎么处理的?
全文搜索引擎目前主流的索引技术就是倒排索引的方式。
传统的保存数据的方式都是: 记录→单词
而倒排索引的保存数据的方式是: 单词→记录
例如
搜索“红海行动”
但是数据库中保存的数据如图:
那么搜索引擎是如何能将两者匹配上的呢?
基于分词技术构建倒排索引:
首先每个记录保存数据时,都不会直接存入数据库。系统先会对数据进行分词,然后以倒排索引结构保存。如下:
然后等到用户搜索的时候,会把搜索的关键词也进行分词,会把“红海行动”分词分成:红海和行动两个词。
这样的话,先用红海进行匹配,得到id=1和id=2的记录编号,再用行动匹配可以迅速定位id为1,3的记录。
那么全文索引通常,还会根据匹配程度进行打分,显然1号记录能匹配的次数更多。所以显示的时候以评分进行排序的话,1号记录会排到最前面。而2、3号记录也可以匹配到。
2. 搜索引擎介绍
lucene是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来的应用。
目前市场上流行的搜索引擎软件有ElaticSearch和Solr,ElaticSearch在目前的市场的占有率更高。
3. ElaticSearch的使用场景
a) 为用户提供按关键字查询的全文搜索功能。
b) 著名的ELK框架(ElasticSearch,Logstash,Kibana),实现企业海量日志的处理分析的解决方案。大数据领域的重要一份子。
4.ElaticSearch的基本概念
cluster | 整个elasticsearch 默认就是集群状态,整个集群是一份完整、互备的数据。 |
---|---|
node | 集群中的一个节点,一般只一个进程就是一个node |
shard | 分片,即使是一个节点中的数据也会通过hash算法,分成多个片存放,默认是5片。 |
index | 相当于rdbms的database, 对于用户来说是一个逻辑数据库,虽然物理上会被分多个shard存放,也可能存放在多个node中。 |
type | 类似于rdbms的table,但是与其说像table,其实更像面向对象中的class , 同一Json的格式的数据集合 |
document | 类似于rdbms的 row、面向对象里的objecfield 相当于字段、属性 |
- ElaticSearch的依赖
SpringBoot中可以自动管理控制elasticsearch的版本
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.5.1</version>
</dependency>
ELASTICSEARCH的使用
1.设计存入ES的数据结构
PUT gmall
{
"mappings": {
"SkuInfo":{
"properties": {
"id":{
"type": "keyword"
, "index": false
},
"price":{
"type": "double"
},
"skuName":{
"type": "text",
"analyzer": "ik_max_word"
},
"skuDesc":{
"type": "text",
"analyzer": "ik_smart"
},
"catalog3Id":{
"type": "keyword"
},
"skuDefaultImg":{
"type": "keyword",
"index": false
},
"skuAttrValueList":{
"properties": {
"valueId":{
"type":"keyword"
}
}
}
}
}
}
}
2.将数据存入ES中
//将skuLsInfo导入到es中
Index index = new Index.Builder(skuLsInfo).index("gmall").type("SkuLsInfo")
id(skuLsInfo.getId()).build();
jestClient.execute(index)
3.ES的数据查询
//ES的数据查询
@Test
public void ESqueryData() throws IOException {
//生成Dsl语句
String dsl = getMyDsl();
//查询
Search search = new Search.Builder(dsl).addIndex("gmall").addType("SkuLsInfo").build();
//执行查询
SearchResult searchResult = jestClient.execute(search);
List<SearchResult.Hit<SkuLsInfo, Void>> hits = searchResult.getHits(SkuLsInfo.class);
ArrayList<SkuLsInfo> SkuLsInfoList = new ArrayList<>();
for (SearchResult.Hit<SkuLsInfo, Void> hit : hits) {
SkuLsInfo source = hit.source;
SkuLsInfoList.add(source);
}
System.out.println(SkuLsInfoList);
}
public String getMyDsl() {
//查询语句封装
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//联合查询
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
// TermQueryBuilder termQueryBuilder = new TermQueryBuilder(null,null);
// boolQueryBuilder.filter(termQueryBuilder);
MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("skuName","苹果");
boolQueryBuilder.must(matchQueryBuilder);
searchSourceBuilder.query(boolQueryBuilder);
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("skuName");
searchSourceBuilder.highlight(highlightBuilder);
return searchSourceBuilder.toString();
}
4.实际代码
`@Service
public class ListServiceImpl implements ListService {
@Autowired
JestClient jestClient;
/**
* 完成ES的查询
* @param skuLsParam 入参属性
* @return List<SkuLsInfo> 出参属性集合
*/
@Override
public List<SkuLsInfo> list(SkuLsParam skuLsParam) {
List<SkuLsInfo> SkuLsInfoList = new ArrayList<>();
//生成Dsl语句
String dsl = getMyDsl(skuLsParam);
//查询
Search search = new Search.Builder(dsl).addIndex("gmall")
.addType("SkuLsInfo").build()
//执行查询
SearchResult searchResult = null;
try {
searchResult = jestClient.execute(search);
} catch (IOException e) {
e.printStackTrace();
}
Long total = searchResult.getTotal();
if (total>0){
List<SearchResult.Hit<SkuLsInfo, Void>> hits = searchResul
.getHits(SkuLsInfo.class)
for (SearchResult.Hit<SkuLsInfo, Void> hit : hits) {
SkuLsInfo source = hit.source;
SkuLsInfoList.add(source);
}
}
return SkuLsInfoList;
}
public String getMyDsl(SkuLsParam skuLsParam) {
//查询语句封装
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//联合查询
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
//三级分类id
String catalog3Id = skuLsParam.getCatalog3Id();
if (StringUtils.isNotBlank(catalog3Id)) {
TermQueryBuilder termQueryBuilder =
new TermQueryBuilder("catalog3Id", catalog3Id);
boolQueryBuilder.filter(termQueryBuilder);
}
//分类属性值
String[] valueId = skuLsParam.getValueId();
if (valueId != null && valueId.length > 0) {
for (String id : valueId) {
TermQueryBuilder termQueryBuilder
new TermQueryBuilder("skuAttrValueList.valueId", id);
boolQueryBuilder.filter(termQueryBuilder);
}
}
//关键字
String keyword = skuLsParam.getKeyword();
if (StringUtils.isNotBlank(keyword)) {
MatchQueryBuilder matchQueryBuilde
= new MatchQueryBuilder("skuName", keyword);
boolQueryBuilder.must(matchQueryBuilder);
}
searchSourceBuilder.query(boolQueryBuilder);
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("skuName");
searchSourceBuilder.highlight(highlightBuilder);
//查询数量
searchSourceBuilder.from(0);
searchSourceBuilder.size(100);
return searchSourceBuilder.toString();
}
}