Elassticsearch介绍、安装及使用

Elassticsearch

什么是全文搜索

我想找简历:只要出现java 、 go 原本的需求就是只要简历中出现了 go 分布式 elasticsearch 都是我要的
我们生活中的数据总体分为两种:结构化数据和非结构化数据。

  • **结构化数据:**指具有固定格式或有限长度的数据,如数据库,元数据等。
  • **非结构化数据:**指不定长或无固定格式的数据,如邮件,word文档等。
非结构化数据另一种叫法叫全文数据。
按照数据的分类,搜索也分为两种:
  • 对结构化数据的搜索:如对数据库的搜索,用SQL语句。再如对元数据的搜索,如利用windows搜索对文件名,类型,修改时间进行搜索等。
  • 对非结构化数据的搜索:如利用windows的搜索也可以搜索文件内容,Linux下的grep命令,再如用Google和百度可以搜索大量内容数据。
    对非结构化数据也即对全文数据的搜索主要有两种方法:
    一种是顺序扫描法(Serial Scanning):所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。如利用windows的搜索也可以搜索文件内容,只是相当的慢。假如有一个80G硬盘,如果想在上面找到一个内容包含某字符串的文件,可能需要几个小时的时间。Linux下的grep命令也是这一种方式。这是一种比较原始的方法,但对于小数据量的文件,这种方法还是最直接,最方便的。但是对于大量的文件,这种方法的速度就很慢。
    另一种是全文检索(Full-text Search):即先建立索引,再对索引进行搜索。索引是从非结构化数据中提取出之后重新组织的信息。

什么是elasticsearch

Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene™ 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 Lucene 那么简单,它不仅包括了全文搜索功能,还可以进行以下工作:

  • 分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索。
  • 实时分析的分布式搜索引擎。
  • 可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。

ES的适用场景

  • 维基百科
  • The Guardian、新闻
  • Stack Overflow
  • Github
  • 电商网站、检索商品
  • 日志数据分析、logstash采集日志、ES进行复杂的数据分析(ELK)
  • 商品价格监控网站、用户设定价格阈值
  • BI系统、商业智能、ES执行数据分析和挖掘

ES特点

  1. 可以作为一个大型的分布式集群(数百台服务器)技术,处理PB级数据,服务大公司,可以运行在单机上,服务小公司。
  2. ES不是什么新技术,主要是将全文检索、数据分析以及分布式技术合并在一起,才形成了独一无二的ES.lucene(全文检索)、商用的数据分析软件、分布式数据库 (mycat)
  3. 对用户而言,是开箱即用,非常简单,作为中小型的应用,直接3分钟部署ES,就可以作为生产环境的系统使用,数据量不大,操作不是很复杂。
  4. 数据库的功能面对很多领域是不够用的(事务,还有各种联机事务的操作):特殊的功能,比如全文检索、同义词处理、相关度排名、复杂数据分析、海量数据近实时处理;ES作为传统数据库的一个补充,提供了数据库所不能提供的很多功能。

elasticsearch安装

1. 关闭并禁用防火墙
systemctl stop firewalld.service
systemctl disable firewalld.service
systemctl status firewalld.service
2. 通过docker安装elasticsearch
#新建es的config配置文件夹
mkdir -p /data/elasticsearch/config
#新建es的data目录
mkdir -p /data/elasticsearch/data
#新建es的plugins目录
mkdir -p /data/elasticsearch/plugins
#给目录设置权限
chmod 777 -R /data/elasticsearch

#写入配置到elasticsearch.yml中, 下面的 > 表示覆盖的方式写入, >>表示追加的方式写入,但是要确保http.host: 0.0.0.0不能被写入多次
echo "http.host: 0.0.0.0" >> /data/elasticsearch/config/elasticsearch.yml
#安装es
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
    -e "discovery.type=single-node" \
  -e ES_JAVA_OPTS="-Xms128m -Xmx256m" \
  -v /data/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
  -v /data/elasticsearch/data:/usr/share/elasticsearch/data \
  -v /data/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
  -d elasticsearch:7.10.1
3. 通过docker安装 kibana
docker run -d --name kibana -e ELASTICSEARCH_HOSTS="http://192.168.0.104:9200" -p 5601:5601 kibana:7.10.1

新建数据

查看索引

GET _cat/indices
通过put+id新建数据
在customer下保存id为1的数据, 这里id是必须的
PUT /account/_doc/1
{
  "name":"bobby",
  "age":18,
  "company":{
      "name":"xiaoxi",
    "address":"beijing"
  }
}
同一个请求发送多次,下面的信息会产生变化
"_version" : 11,
"result" : "updated", #这里第一次是created,后续都是updated
"_seq_no" : 10, #版本号
 #### 发送post不带id新建数据
POST user/_doc/
{
  "name":"likezhu"
}


POST user/_doc/2
{
  "name":"likezhu"
}
如果post带id就和put一样的操作了, put是不允许不带id的
post + _create

没有就创建,有就报错

POST user/_create/1
{
  "name":"likezhu"
}
查看index
GET _cat/indices  //查看所有索引
GET /account //查看index的基本信息

获取数据

获取数据
GET user/_doc/1

{
  "_index" : "user", //所属 
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 11, #版本号
  "_seq_no" : 10, #并发控制,用于乐观锁
  "_primary_term" : 1, #同_seq_no, 主分片重新分配, 如重启,就会变化
  "found" : true,
  "_source" : {
    "name" : "bobby"
  }
}

只返回source的值

GET user/_source/1
搜索数据

Elasticsearch有两种查询方式

11. URI带有查询条件(轻量查询)
    查询能力有限,不是所有的查询都可以使用此方式
12. 请求体中带有查询条件(复杂查询) 
    查询条件以JSON格式表现,作为查询请求的请求体,适合复杂的查询 
通过url查询数据

请求参数位于_search端点之后,参数之间使用&分割,例如:

GET /_search?pretty&q=title:azure&explain=true&from=1&size=10&sort=title:asc&fields:user,title,content

搜索API的最基础的形式是没有指定任何查询的空搜索,它简单地返回集群中所有索引下的所有文档。

通过request body查询数据
GET account/_search
{
  "query": {
    "match_all": {}
  }
}
更新数据
想要给已有的数据新增字段
POST users/_doc/1
{
  "age":18
}
此时会发现,已有的数据的name字段没有了,只有age字段
此时我们需要使用
POST user/_update/1
{
  "doc": {
    "age":18
  }
}

删除数据

删除数据和索引
DELETE users/_doc/1


DELETE users

批量插入和批量查询

批量操作(bulk)

用法

POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }

字段说明:

{
  "account_number" : 6, //银行账户
  "balance" : 5686, // 余额
  "firstname" : "Hattie", 
  "lastname" : "Bond",
  "age" : 36,
  "gender" : "M",
  "address" : "671 Bristol Street",
  "employer" : "Netagy", //所在公司
  "email" : "hattiebond@netagy.com",
  "city" : "Dante", //城市
  "state" : "TN" //国家
}
批量获取(mget)
GET /_mget
{
  "docs": [
    {
      "_index": "my-index-000001",
      "_id": "1"
    },
    {
      "_index": "my-index-000001",
      "_id": "2"
    }
  ]
}

全文查询 - 分词

match查询(匹配查询)

match:模糊匹配,需要指定字段名,但是输入会进行分词,比如”hello world”会进行拆分为hello和world,然后匹配,如果字段中包含hello或者world,或者都包含的结果都会被查询出来,也就是说match是一个部分匹配的模糊查询。查询条件相对来说比较宽松。

GET user/_search
{
  "query": {
    "match": {
      "address": "street"
    }
  }
}

match_phrase查询 短语查询

match_phase:会对输入做分词,但是需要结果中也包含所有的分词,而且顺序要求一样。以”hello world”为例,要求结果中必须包含hello和world,而且还要求他们是连着的,顺序也是固定的,hello that word不满足,world hello也不满足条件。

GET user/_search
{
  "query": {
    "match_phrase": {
      "address": "Madison street"
    }
  }
}

multi_match查询

multi_match查询提供了一个简便的方法用来对多个字段执行相同的查询,即对指定的多个字段进行match查询

POST resume/_doc/12
{
  "title": "后端工程师",
  "desc": "多年go语言开发经验, 熟悉go的基本语法, 熟悉常用的go语言库",
  "want_learn":"python语言"
}

POST resume/_doc/13
{
  "title": "go工程师",
  "desc": "多年开发经验",
  "want_learn":"java语言"
}


POST resume/_doc/14
{
  "title": "后端工程师",
  "desc": "多年开发经验",
  "want_learn":"rust语言"
}


GET account/_search
{
  "query": {
    "multi_match": {
      "query": "go",
      "fields": ["title", "desc"]
    }
  }
}

query_string查询

query_string:和match类似,但是match需要指定字段名,query_string是在所有字段中搜索,范围更广泛。

  GET user/_search 
  { 
      "query":{
      "query_string": { 
      "default_field": "address", 
      "query": "Madison AND street"
  }}}
2. match all查询
GET user/_search
{
  "query": {
    "match_all": {}
  }
}

term 级别查询

term查询

term: 这种查询和match在有些时候是等价的,比如我们查询单个的词hello,那么会和match查询结果一样,但是如果查询”hello world”,结果就相差很大,因为这个输入不会进行分词,就是说查询的时候,是查询字段分词结果中是否有”hello world”的字样,而不是查询字段中包含”hello world”的字样,elasticsearch会对字段内容进行分词,“hello world”会被分成hello和world,不存在”hello world”,因此这里的查询结果会为空。这也是term查询和match的区别。

GET user/_search
{
  "query": {
    "term": {
      "address": "madison street"
    }
  }
}
range查询 - 范围查询
GET user/_search
{
  "query":{
    "range": {
      "age": {
        "gte": 20,
        "lte": 30
      }
    }
  }
}
exists查询
GET user/_search
{
  "query": {
    "exists": {
      "field": "school"
    }
  }
}
fuzzy模糊查询
GET user/_search
{
  "query": {
    "match": {
      "address": {
        "query": "Midison streat",
        "fuzziness": 1
      }
    }
  }
}


GET /_search
{
  "query": {
    "fuzzy": {
      "user.id": {
        "value": "ki"
      }
    }
  }
}
复合查询

Elasticsearch bool查询对应Lucene BooleanQuery, 格式如下

{
    "query":{
        "bool":{
            "must":[
            ],
            "should":[
            ],
            "must_not":[
            ],
            "filter":[
            ],
        }
    }
}

must: 必须匹配,查询上下文,加分
should: 应该匹配,查询上下文,加分
must_not: 必须不匹配,过滤上下文,过滤
filter: 必须匹配,过滤上下文,过滤
bool查询采用了一种匹配越多越好的方法,因此每个匹配的must或should子句的分数将被加在一起,以提供每个文档的最终得分

GET user/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "state": "tn"
          }
        },
        {
          "range": {
            "age": {
              "gte": 20,
              "lte": 30
            }
          }
        }
      ],
      "must_not": [
        {
          "term": {
            "gender": "m"
          }
        }
      ],
      "should": [
        {
          "match": {
            "firstname": "Decker"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "age": {
              "gte": 25,
              "lte": 30
            }
          }
        }
      ]
    }
  }
}

go使用gin框架代码实现es(示例):

package main

import (
        "context"
        "fmt"
        "strconv"
        "encoding/json"
        "github.com/olivere/elastic/v7" // 请确保导入适合你的Elasticsearch版本
        "your_project/model" // 请根据实际项目路径修改
)

var (
        esClient *elastic.Client // Elasticsearch客户端
        err      error           // 错误处理
        ctx      context.Context // 上下文
)

// 初始化函数,用于单例模式的Elasticsearch客户端实例化
func init() {
        esClient, err = elastic.NewClient(
                elastic.SetURL("http://localhost:9200"), // 设置Elasticsearch的URL
                elastic.SetSniff(false)) // 关闭节点嗅探
        if err != nil {
                // 处理错误
                panic(err) // 如果创建客户端失败,则程序崩溃
        }
        // 初始化全局变量ctx为背景上下文
        ctx = context.Background()
}

// 创建索引函数
func CreateIndex(indexName string) error {
        // 判断索引是否存在
        exists, err := esClient.IndexExists(indexName).Do(ctx)
        if err != nil {
                // 处理错误
                fmt.Println("Error checking index existence:", err)
                return err
        }

        if !exists {
                // 如果索引不存在,创建一个新的索引
                createIndex, err := esClient.CreateIndex(indexName).BodyString(mapping).Do(ctx)
                if err != nil {
                        // 处理错误
                        panic(err) // 创建索引失败则程序崩溃
                }
                fmt.Println("Index created:", createIndex)
        }

        return nil
}

// 从MySQL到Elasticsearch的批量导入函数
func MySqlToEsBulk(indexName string) {
        videos := []model.Video{} // 定义一个切片来存储视频数据
        model.DB.Model(&model.Video{}).Offset(0).Limit(100).Find(&videos) // 从数据库查询视频数据

        // 创建一个bulk请求
        bulkRequest := esClient.Bulk()

        for _, v := range videos {
                // 为每个视频创建一个bulk请求动作
                req := elastic.NewBulkIndexRequest().Index(indexName).Id(strconv.Itoa(int(v.Id))).Doc(v)
                bulkRequest = bulkRequest.Add(req) // 添加请求到bulk请求中
        }

        // 执行bulk请求
        _, err := bulkRequest.Do(ctx)
        if err != nil {
                fmt.Println("Failed to execute bulk request:", err)
        }

        fmt.Println("Successfully executed bulk request")
}

// 添加文档到Elasticsearch的函数
func AddDoc() {
        // 创建一个视频实例
        tweet1 := model.Video{
                Id:                 8888888,
                Title:              "间谍过家家",
                SubTitle:           "安尼亚",
                ChannelId:          1,
                Status:             0,
                EpisodesCount:      32,
                IsEnd:              0,
                IsHot:              0,
                RegionId:           0,
                TypeId:             0,
                EpisodesUpdateTime: 0,
                Comment:            0,
                UserId:             0,
                IsRecommend:        0,
        }
        // 将视频实例索引到Elasticsearch
        put1, err := esClient.Index().
                Index("videos"). // 指定索引名
                Id("8888888").   // 指定文档ID
                BodyJson(tweet1). // 使用JSON格式的文档体
                Do(ctx)
        if err != nil {
                // 处理错误
                panic(err) // 索引文档失败则程序崩溃
        }
        fmt.Printf("Indexed video %s to index %s\n", put1.Id, put1.Index)
}

// 在Elasticsearch中执行搜索的函数
func EsSearch(indexName string, termQuery *elastic.TermQuery, offset, limit int) (int64, []model.Video, error) {
        // 执行搜索请求
        searchResult, err := esClient.Search().
                Index(indexName). // 指定索引名
                Query(termQuery). // 添加查询条件
                From(offset).Size(limit). // 设置分页
                Pretty(true). // 使输出更美观
                Highlight(elastic.NewHighlight().Field("title").PreTags("<span style='color:red;'>").PostTags("</span>")). // 高亮标题
                Do(ctx) // 执行搜索请求
        if err != nil {
                return 0, nil, err // 返回错误
        }

        // 处理搜索结果
        var results []model.Video
        if searchResult.Hits.TotalHits.Value > 0 {
                for _, hit := range searchResult.Hits.Hits {
                        var searchData model.Video

                        // 高亮标红展示
                        if hit.Highlight != nil && hit.Highlight["title"] != nil {
                                searchData.HighTitle = hit.Highlight["title"][0]
                        }

                        err := json.Unmarshal(hit.Source, &searchData) // 反序列化搜索结果
                        if err != nil {
                                return 0, nil, err // 返回错误
                        }
                        results = append(results, searchData) // 添加到结果集中
                }
        } else {
                return 0, nil, nil // 如果没有结果,返回
        }

        return searchResult.Hits.TotalHits.Value, results, nil // 返回总命中数和结果集
}
入口函数
package main

import (
    "gin_video/router"
    "gin_video/server"
)

func main() {
    //检测索引和创建索引
    //server.CreateIndex("videos")
    //server.MySqlToEsBulk("videos")
    //server.AddDoc()

    r := router.InitRouter()

    r.Run(":8888") // 监听并在 0.0.0.0:8080 上启动服务
}
控制器
func SearchVideo(c *gin.Context) {
    title := c.Query("title")
    offset, limit := Page(c)

    termQuery := elastic.NewTermQuery("title", title)

    nums, videos, _ := server.EsSearch("videos", termQuery, offset, limit)

    c.JSON(http.StatusOK, gin.H{
       "code": 0,
       "data": map[string]interface{}{
          "total_count": nums,
          "video":       videos,
       },
       "msg": "success",
    })
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值