go语言实战es,高亮全文检索关键词等!
Golang实战ES
- 一、ES的安装
-
- 下载elasticSearch7.7.0
- docker-compose启动elasticSearch7.7.0
- 安装中文分词器IK
- 注意事项
- 二、ES的简单的应用
-
- 查询
-
- 简单查询
- 复合条件查询
- 三、ES的在go中实战项目运用
-
- 安装
- 连接
- 查询索引
- 添加
- 查询
- 更新
- 删除
- 返回搜索词高亮处理
一、ES的安装
下载elasticSearch7.7.0
docker pull elasticsearch:7.7.0 //下拉镜像docker images//查看镜像//创建所需文件mkdir -p /mydata/elasticsearch/configmkdir -p /mydata/elasticsearch/dataecho "http.host: 0.0.0.0">>/mydata/elasticsearch/config/elasticsearch.yml//文件夹赋权chmod -R 777 elasticsearch///命令启动docker run --name elasticsearch -p 9200:9200 \ -p 9300:9300 \ -e "discovery.type=single-node" \ -e ES_JAVA_OPTS="-Xms64m -Xmx128m" \ -v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \ -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.7.0 //参数说明 --name elasticsearch:将容器命名为 elasticsearch-p 9200:9200:将容器的9200端口映射到宿主机9200端口-p 9300:9300:将容器的9300端口映射到宿主机9300端口,目的是集群互相通信-e "discovery.type=single-node":单例模式-e ES_JAVA_OPTS="-Xms64m -Xmx128m":配置内存大小-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:将配置文件挂载到宿主机-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data:将数据文件夹挂载到宿主机-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins:将插件目录挂载到宿主机(需重启)-d elasticsearch:7.7.0:后台运行容器,并返回容器ID
docker-compose启动elasticSearch7.7.0
//docker-compose启动version: '2'services: elasticsearch: container_name: elasticsearch image: elasticsearch:7.7.0 ports: - "9200:9200" volumes: - /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml - /mydata/elasticsearch/data:/usr/share/elasticsearch/data - /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins environment: - "ES_JAVA_OPTS=-Xms64m -Xmx128m" - "discovery.type=single-node" - "COMPOSE_PROJECT_NAME=elasticsearch-server" restart: always//查看容器docker ps -a//验证是否成功curl http://192.168.0.50:9200///返回信息{ "name" : "550eca5cf3b2", "cluster_name" : "elasticsearch", "cluster_uuid" : "1mP9IJU3TUy_C2DZMfKGBg", "version" : { "number" : "7.7.0", "build_flavor" : "default", "build_type" : "docker", "build_hash" : "81a1e9eda8e6183f5237786246f6dced26a10eaf", "build_date" : "2020-05-12T02:01:37.602180Z", "build_snapshot" : false, "lucene_version" : "8.5.1", "minimum_wire_compatibility_version" : "6.8.0", "minimum_index_compatibility_version" : "6.0.0-beta1" }, "tagline" : "You Know, for Search"}//Docker启动容器自启docker update elasticsearch --restart=always
安装中文分词器IK
1.在线安装//进入容器docker exec -it elasticsearch /bin/bash//在线下载并安装./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip//进入plugins可以看到IK分词器已经安装成功目录中有analysis-ik安装成功2.离线安装//将IK分词器上传到/tmp目录中elasticsearch-analysis-ik-6.5.4.zip//将压缩包移动到容器里面docker cp /tmp/elasticsearch-analysis-ik-6.5.4.zip elasticsearch:/usr/share/elasticsearch/plugins//进入容器docker exec -it elasticsearch /bin/bash//创建目录mkdir /usr/share/elasticsearch/plugins/ik//将文件压缩包移动到ik中mv /usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-6.5.4.zip /usr/share/elasticsearch/plugins/ik//进入目录cd /usr/share/elasticsearch/plugins/ik//解压unzip elasticsearch-analysis-ik-6.5.4.zip//删除压缩包rm -rf elasticsearch-analysis-ik-6.5.4.zip//退出并重启镜像exitdocker restart elasticsearch
注意事项
* 注意 安装中文分词器版本一定要和ES的版本号对应,不然会报错。
报错信息如下:Exception in thread "main" java.lang.IllegalArgumentException: Plugin [analysis-ik] was built for Elasticsearch version 7.0.0 but version 7.7.0 is runningat org.elasticsearch.plugins.PluginsService.verifyCompatibility(PluginsService.java:346)at org.elasticsearch.plugins.InstallPluginCommand.loadPluginInfo(InstallPluginCommand.java:814)at org.elasticsearch.plugins.InstallPluginCommand.installPlugin(InstallPluginCommand.java:869)at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:254)at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:224)at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86)at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:127)at org.elasticsearch.cli.MultiCommand.execute(MultiCommand.java:91)at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:127)at org.elasticsearch.cli.Command.main(Command.java:90)at org.elasticsearch.plugins.PluginCli.main(PluginCli.java:47)
查看对应IK版本号:https://github.com/medcl/elasticsearch-analysis-ik/releases
安装完成重启ESdocker restart elasticsearchIK分词使用请求地址POST:http://127.0.0.1/_analyze?pretty{"analyzer": "ik_smart","text": "中华人民共和国国歌"}//返回内容{"tokens": [ { "token": "中华人民共和国", "start_offset": 0, "end_offset": 7, "type": "CN_WORD", "position": 0 }, { "token": "国歌", "start_offset": 7, "end_offset": 9, "type": "CN_WORD", "position": 1 }]}ik_max_word:最细粒度的拆分ik_smart: 最粗粒度的拆分
二、ES的简单的应用
查询
简单查询
Query Context 处理判断文档是否满足查询条件以外,ES还会计算一个_score 来标识匹配程度,判断目标文档与查询条件匹配的有多好。 常用查询 针对文本类型数据 字段级别的查询 针对结构化数据,如数字,日期。
Post请求 127.0.0.1/book/_search//模糊查询{ "query":{ "match":{ "author":"瓦力" } }} //准确查询{ "query":{ "match_parase":{ "author":"瓦力" } }} //多字段的匹配查询(查询autor和title包含瓦力的){ "query":{ "multi_match":{ "query":"瓦力", "fields":["author", "title"] } }} //查询瓦力和很好或则是python{ "query":{ "query_string":{ "query":"(瓦力 AND 很好)OR python", } }} //查询瓦力和很好或则是python(在author和title字段中){ "query":{ "query_string":{ "query":"(瓦力 AND 很好)OR python", "fields":["author", "title"] } }}//字段级别的查询,查询字数等于1000的图书{ "query":{ "term":{ "word_count": 1000 } }}//字段级别的查询,查询字数在1000-2000的图书有哪些,//now表示现在时间,gte表示大于等于,gt表示大于{ "query":{ "range":{ "word_count":{ "gte":1000, "lte":2000 } } }}
Filter Context 子条件查询 在查询过程中,只判断该文档是否满足条件,只有Yes或No 进行数据过滤,会加到缓存中,比query要快
//查询字数在一千字的书籍{ "query": { "bool": { "filter": { "term": { "word_count": 1000 } } } }}
复合条件查询
//POST 127.0.0.1/_search//全文查询,但是给出的评分不是一样的{ "query": { "match": { "title": "ElasticSeach" } }}//固定评分查询,结果评分全部是1,不支持match{ "query": { "constant_score": { "filter": { "match": { "title": "ElasticSearch" } }, "boost": 2 //指定分数查询, } }}//must必须查询条件{ "query": { bool": { "should": [ { "match": { "author": "瓦力" } }, { "match":{ "title": "ElasticSearch" } } ], "filter": [ { "term":{ "word_count": 1000 } } ] } } }}//must_not.不满足条件,筛选处作者没有瓦力的条件{ "query": { "must_not": { "term": { "author": "瓦力" } } }}
三、ES的在go中实战项目运用
安装
1.根据版本安装依赖(注意go mod 安装和直接安装方法不一样,下面是go mod 安装)"github.com/olivere/elastic/v7"
连接
//连接客户端client, err := elastic.NewClient(elastic.SetURL(esUrl), elastic.SetSniff(false))if err != nil {// Handle errorfmt.Println("出错了")panic(err)}// Ping the Elasticsearch server to get e.g. the version number// ping通服务端,并获得服务端的es版本,本实例的es版本为version 7.6.1info, code, err := client.Ping(esUrl).Do(ctx)if err != nil {// Handle errorpanic(err)}fmt.Println("开始打印版本号")fmt.Printf("Elasticsearch returned with code>: %d and version %s\n", code, info.Version.Number)// 获取版本号的直接APIesVersion, err := client.ElasticsearchVersion(esUrl)if err != nil {panic(err)}fmt.Printf("es的版本为%s\n", esVersion)
查询索引
//连接客户端client, err := elastic.NewClient(elastic.SetURL(esUrl), elastic.SetSniff(false))if err != nil {// Handle errorfmt.Println("出错了")panic(err)}// Ping the Elasticsearch server to get e.g. the version number// ping通服务端,并获得服务端的es版本,本实例的es版本为version 7.6.1info, code, err := client.Ping(esUrl).Do(ctx)if err != nil {// Handle errorpanic(err)}fmt.Println("开始打印版本号")fmt.Printf("Elasticsearch returned with code>: %d and version %s\n", code, info.Version.Number)// 获取版本号的直接APIesVersion, err := client.ElasticsearchVersion(esUrl)if err != nil {panic(err)}fmt.Printf("es的版本为%s\n", esVersion)
添加
//连接客户端client, err := elastic.NewClient(elastic.SetURL(esUrl), elastic.SetSniff(false))if err != nil {// Handle errorfmt.Println("出错了")panic(err)}// Ping the Elasticsearch server to get e.g. the version number// ping通服务端,并获得服务端的es版本,本实例的es版本为version 7.6.1info, code, err := client.Ping(esUrl).Do(ctx)if err != nil {// Handle errorpanic(err)}fmt.Println("开始打印版本号")fmt.Printf("Elasticsearch returned with code>: %d and version %s\n", code, info.Version.Number)// 获取版本号的直接APIesVersion, err := client.ElasticsearchVersion(esUrl)if err != nil {panic(err)}fmt.Printf("es的版本为%s\n", esVersion)
查询
5.查询get1, err := client.Get().Index("user").Id("1").Do(ctx)if err != nil{panic(err)}if get1.Found{fmt.Printf("Got document %s in version %d from index %s, type %s\n", get1.Id, get1.Version, get1.Index, get1.Type)}//将保存的内容保存到磁盘_, err = client.Flush().Index("user").Do(ctx)if err != nil {panic(err)}// 按"term"搜索Search with a term querytermQuery := elastic.NewTermQuery("name", "mike")searchResult, err := client.Search().Index("user"). // 搜索的索引"user"Query(termQuery). // specify the querySort("age", true). //按字段"age"排序,升序排列From(0).Size(10). // 分页,单页显示10条Pretty(true).// pretty print request and response JSON以json的形式返回信息Do(ctx) // 执行if err != nil {// Handle errorpanic(err)}fmt.Printf("Query took %d milliseconds\n", searchResult.TookInMillis)// 按"term"搜索Search with a term querytermQuery := elastic.NewTermQuery("name", "mike")searchResult, err := client.Search().Index("user"). // 搜索的索引"user"Query(termQuery). // specify the querySort("age", true). //按字段"age"排序,升序排列From(0).Size(10). // 分页,单页显示10条Pretty(true).// pretty print request and response JSON以json的形式返回信息Do(ctx) // 执行if err != nil {// Handle errorpanic(err)}fmt.Printf("Query took %d milliseconds\n", searchResult.TookInMillis)var user User//Each是一个简便函数,此函数忽略了错误输出for _, item1 := range searchResult.Each(reflect.TypeOf(user)) {if u, ok := item1.(User); ok {fmt.Printf("Person by %s,age:%d,married:%t,Sex:%s\n", u.Name, u.Age, u.Married,u.Sex) //Person by bob,age:23,married:false,Sex:male}// 搜索文档方法2// 使用hits,获得更详细的输出结果if searchResult.Hits.TotalHits.Value >0{fmt.Printf("找到的数据总数是 %d \n", searchResult.Hits.TotalHits.Value)for _,hits := range searchResult.Hits.Hits{u :=User{}err := json.Unmarshal([]byte(hits.Source), &u)if err != nil{fmt.Println("反序列化失败",err)}fmt.Printf("User by %s,age:%d,married:%t,Sex:%s\n", u.Name, u.Age, u.Married,u.Sex)}}else {fmt.Println("没有搜到用户")}
更新
// 更新文档 updateupdate, err := client.Update().Index("user").Id("1").Script(elastic.NewScriptInline("ctx._source.age += params.num").Lang("painless").Param("num", 1)).//Upsert(map[string]interface{}{"created": "2020-06-17"}). // 插入未初始化的字段valueDo(ctx)if err != nil {// Handle errorpanic(err)}fmt.Printf("New version of user %q is now %d\n", update.Id, update.Version) // 更新方法2update,err := client.Update().Index("user").Id("1").Script(elastic.NewScriptInline("ctx._source.created=params.date").Lang("painless").Param("date","2020-06-17")).Do(ctx)termQuery := elastic.NewTermQuery("name", "bob")update,err = client.UpdateByQuery("user").Query(termQuery).Script(elastic.NewScriptInline("ctx._source.age += params.num").Lang("painless").Param("num", 1)).Do(ctx)if err != nil{panic(err)}fmt.Printf("New version of user %q is now %d\n", update.Id, update.Version)fmt.Println(update)
删除
//删除文档termQuery := elastic.NewTermQuery("name", "mike")_, err = client.DeleteByQuery().Index("user"). // search in index "user"Query(termQuery). // specify the queryDo(ctx)if err != nil {// Handle errorpanic(err)}
返回搜索词高亮处理
func QueryHeight(search dto.SearchActileDto,client *elastic.Client) (hightmap []bo.SearchActileBo,err error){var data []bo.SearchActileBotextFild:=elastic.NewHighlighterField("content")//idFild:=elastic.NewHighlighterField("id")//fild.PreTags("")//fild.PostTags("")titleFild:=elastic.NewHighlighterField("title")titleFild.FragmentSize(350)higthFild :=elastic.NewHighlight()higthFild.Fields(titleFild)higthFild.Fields(textFild)//higthFild.Fields(idFild)//两个里面必须有//query :=elastic.NewBoolQuery().Must(elastic.NewMatchPhraseQuery("title","css"),elastic.NewMatchPhraseQuery("text","css"))query :=elastic.NewBoolQuery().Should(elastic.NewMatchPhraseQuery("title",search.SearchWord),elastic.NewMatchPhraseQuery("content",search.SearchWord))res, err := client.Search("article").Query(query).Highlight(higthFild).Size(int(search.Size)).From(int((search.Current - 1) * search.Size)).Do(context.Background())if err != nil {return nil,err}//fmt.Println(res.Hits.Hits)for _, hit := range res.Hits.Hits {aa :=hit.Highlightvar search bo.SearchActileBosearch.ContentId = hit.Idvar qq dto.Articlejson.Unmarshal(hit.Source,&qq)search.CreatTime = fmt.Sprintf("%v",qq.CreateTime)search.CreateID = qq.CreateByif aa["title"] == nil{search.Title=qq.Title}else{search.Title=aa["title"][0]}if aa["content"] == nil{search.Content=qq.Content}else{search.Content = aa["content"][0]}data = append(data, search)}return data,nil}
欢迎私信我交流更多ES用法