1、写在前面的话:
在电商公司已待了三年有余,这样运营下去的一个电商品台,多少还是有些东西值得学习,今天对ElasticSearch这一利器来一个学习和总结。更多的,是对ES的相关讯息生成一个自己的笔记,以后自己回顾也好,再次接触ES需要进行开发和运用,也有一个思路可以追寻,也会针对自己的疑惑有再次提点作用。
2、再说ES前,需要了解es的底层架构Lucene:
2.1,Lucene以json格式作为文件存储结构:一条json格式的字符串就是一个文档,与MongoDB一模一样。
{"id" : "23", "name" : "郭冠兰", "sex" : "Male", "age" : 25}
2.2,Lucene建立索引分词器的特性:以倒排序的方式实现文档索引,这里可以展开说明:
倒排序,在lucene接收到一个json字符串文档时,分词器会将接收到的数据按关键字分解,分解后的结构是:[term key] [docid]
{"name" : "郭冠兰", "address" : "广东省广州市天河区"},{"name" : "冠兰", "address" : "广东省广州市天河区"}
分词后的结构是:
[冠兰][124]
[郭冠兰][123]
[广东省][123,124]
[广州市][123,124],
[天河区][123,124],
说明:123,124分别是接收到的字符串文档在lucene文件存储系统中的文档id,我们举例它分配到的id是123,124,id也可以自行设定,也可以lucene生成。但是最好是按顺序有规律的id生成规则,自行指定时。
参考这里有详细的说明,这涉及到lucene底层搜索排序的高级算法:基础介绍及索引原理分析后面提到id生成规则,docid压缩算法等等。
在分词过程中,可以定义字段是否进行分词,全局分词等操作,例如:
RESTful形式:
PUT /my_index
{
"mappings": {
"my_type": {
"properties": {
"status_code": { //字段名
"type": "string",
"index": "not_analyzed" //非查询字段”不建索引index
}
"title": {
"type": "string",
"store": true
},
"content": {
"type": "string"
}
}
}
}
}
Java api形式:
private XContentBuilder getMapping(String type) throws Exception {
XContentBuilder mappingBuilder = XContentFactory.jsonBuilder().startObject().startObject(type)
.startObject("_all").field("enabled", false).endObject()
.startObject("properties")
.startObject("userName").field("type", "keyword").endObject()
.startObject("age").field("type", "integer").endObject()
.startObject("create_time").field("type", "date").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("index", "not_analyzed")
.field("include_in_all", false).endObject(); //不创建索引
.endObject()
.endObject().endObject();
return mappingBuilder;
}
index的参数值:
· no: 不把此字段添加到索引中,也就是不建索引,此字段不可查询
· not_analyzed:将字段的原始值放入索引中,作为一个独立的term,它是除string字段以外的所有字段的默认值。
· analyzed:string字段的默认值,会先进行分析后,再把分析的term结果存入索引中。
3、ES的高效性原理:
es为何如此高效?为了提高查询效率,lucene在分词过程、存储数据,和索引结构中下了很多功夫。我自己不会总结,在网上查看了很多文章,发现很多精妙之处,此处查看这几篇文章了解:
时间序列数据库的秘密 (2)——索引 ,都是底层的算法,如果没有耐心,会看不懂,枯燥无味。
仔细看了之后深有体会,底层竟如是如此的算法,惊奇,巧妙,从此对算法有了很深深的好感,果然应了那句话,大神都是玩算法,渣渣讨论代码。
4、ES的查询:
lucene分词,默认情况下是对所有字段创建倒排索引的(动态mapping解析出来为数字类型、布尔类型的字段除外),某个字段是否生成倒排索引是由字段的index属性控制的,可查看上面的Java代码,在第一次创建索引库的时候进行设置,之后往这加入文档的时便遵照这设置进行建索引。
//根据字段,索引查询
private BoolQueryBuilder convertParam(UserSearchParam param) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
if (StringUtils.hasText(param.getUserName())) {
boolQueryBuilder.must(QueryBuilders.termQuery("name", param.getName()));
}
if (param.getAge() != null) {
boolQueryBuilder.must(QueryBuilders.rangeQuery("age").gt(param.getAge()));
}
if (StringUtils.hasText(param.getDescription())) {
boolQueryBuilder.must(QueryBuilders.matchQuery("description", param.getDescription()));
}
return boolQueryBuilder;
}
//创建索引
public boolean createIndex(String index, String type) {
XContentBuilder mappingBuilder;
try {
mappingBuilder = this.getMapping(type);
} catch (Exception e) {
logger.error("创建Mapping 异常");
return false;
}
Settings settings = Settings.builder().build();
IndicesAdminClient adminClient = client.admin().indices();
CreateIndexRequestBuilder builder = adminClient.prepareCreate(index);
builder.setSettings(settings);
CreateIndexResponse response = builder.addMapping(type, mappingBuilder).get();
return response.isAcknowledged();
}
//创建索引的Mapping信息 注意声明的roles为nested类型
private XContentBuilder getMapping(String type) throws Exception {
XContentBuilder mappingBuilder = XContentFactory.jsonBuilder().startObject().startObject(type)
.startObject("_all").field("enabled", false).endObject()
.startObject("properties")
.startObject("userName").field("type", "keyword").endObject()
.startObject("age").field("type", "integer").endObject()
.startObject("create_time").field("type", "date").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("index", "not_analyzed").field("include_in_all", false).endObject();
.endObject()
.endObject().endObject();
return mappingBuilder;
}
有一点需要注意,es在默认情况下,是针对所有字段进行索引,却也可以针对单个字段进行索引查询:
//例如查询一个商品的货号字段Java代码
//货号
if(!StringUtils.isEmpty(productViewParam.getGoodsNo())){
boolQueryBuilder.must(QueryBuilders.termQuery("goodsNo", productViewParam.getGoodsNo()));
}
//单个匹配,搜索goodsNo为某个值的文档
存储在es上的文档,除了各个字段外,也额外存在一个_all字段存储字段的所有值,并已空格隔开各个字段值:例如
该字段可开启和关闭,ES的各字段,查看更多字段含义。
4、结尾:
文章说的比较凌乱,思路不清晰,就是把网络上很多的知识拼接在一起,算是对自己的一个笔记和温习的思路吧,如果有兴致可以仔细读,对es有个了解。
感谢网络上很多大神,写了很多好文章,在此就引用,作为一个入口去查看。