【java进阶】初识Elasticsearch(一)

146 阅读7分钟

一. 简介

​ Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。

​ 但是,Lucene只是一个库。想要 使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。

​ Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

​ Elasticsearch 的中文网址:www.elastic.co/cn/products…

1.1 正排索引与倒排索引

​ 正向索引(前向索引)与倒排索引,这是在搜索领域中非常重要的两个名词,正向索引通常用于数据库中,在搜索引擎领域使用的最多的就是倒排索引,我们根据如下两个网页来对这两个概念进行阐述:

html1

我爱我的祖国,我爱编程

html2


我爱编程,我是个快乐的小码农

正向索引: 假设我们使用mysql的全文检索,会对如上两句话分别进行分词处理,那么预计得到的结果如下:


我 爱 爱我 祖国 我的祖国 编程 爱编程 我爱编程

我 我爱 爱 编程 爱编程 我爱编程 快乐 码农 小码农

​ 假设我们现在使用正向索引搜索 编程 这个词,那么会到第一句话中去查找是否包含有 编程 这个关键词,如果有则加入到结果集中;第二句话也是如此。假设现在有成千上百个网页,每个网页非常非常的分词,那么搜索的效率将会非常非常低些。

倒排索引: 倒排索引是按照分词与文档进行映射,我们来看看如果按照倒排索引的效果:

关键词文档名
html1,html2
html1,html2
爱我html1
我爱html2
祖国html1,html3
我的祖国html1
编程html1,html2
我爱编程html1,html2
爱编程html1,html2
快乐html2
码农html2
小码农html2

​ 如果采用倒排索引的方式搜索 编程 这个词,那么会直接找到关键词中查找到 编程 ,然后查找到对应的文档,这就是所谓的倒排索引。

二. ES与Kibana的docker版本安装

2.1 ES与Kibana安装

  1. 需要安装docker和docker-compse,参考docker的文档。
  1. 创建elk文件夹,在文件夹下编写 docker-compose.yml 文件,内容如下:

version: '3.8'
services:
  es:
    image: elasticsearch:7.17.0
    ports:
      - 9200:9200 
      - 9300:9300       
    environment:
      - 'discovery.type=single-node'
  kibana:
    image: kibana:7.17.0
    ports:
      - 5601:5601
    environment:
      # 表示kibana要连接的es的地址
      ELASTICSEARCH_HOSTS: '["http://es:9200"]'
  1. 启动Elasticsearch和kibana,执行命令:docker-compose up
  1. 在浏览器输入:ip:9200 验证elasticsearch是否安装成功;ip:5601 验证kibana是否安装成功

2.2 Logstash的安装

​ Logstash是一个开源的服务器端数据处理管道,可以同时从多个数据源获取数据,并对其进行转换,然后将其发送到你最喜欢的“存储”。创建于2009年,于2013年被elasticsearch收购,下载地址如下:

www.elastic.co/cn/download…

本课程以movielens数据集为例进行讲解,数据集下载地址:files.grouplens.org/datasets/mo…

第一步,下载后解压,将movies.csv文件拷贝到指定的目录下,例如:D:/logstash-datas/ 目录。

第二步,进入到Logstash的解压目录,进入到config目录下,新建logstash.conf,文件内容如下:

input {
  file {
    path => "D:/logstash-datas/movies.csv"
    start_position => "beginning"
    sincedb_path => "D:/logstash-datas/db_path.log"
  }
}
filter {
  csv {
    separator => ","
    columns => ["id","content","genre"]
  }
​
  mutate {
    split => { "genre" => "|" }
    remove_field => ["path", "host","@timestamp","message"]
  }
​
  mutate {
​
    split => ["content", "("]
    add_field => { "title" => "%{[content][0]}"}
    add_field => { "year" => "%{[content][1]}"}
  }
​
  mutate {
    convert => {
      "year" => "integer"
    }
    strip => ["title"]
    remove_field => ["path", "host","@timestamp","message","content"]
  }
​
}
output {
   elasticsearch {
     hosts => "http://192.168.31.173:9200"
     index => "movies"
     document_id => "%{id}"
   }
  stdout {}
}

第三步,在命令行进入到logstash的bin目录下,输入如下命令启动Logstash:


logstash.bat -f D:\logstash-datas\config\logstash.conf

特别强调:路径中绝对不能有中文和空格。

第四步,在Kibana的Dev Tools中执行 GET _cat/indices, 即可查看到movies数据集:

三. Elasticsearch的基本概念

3.1 索引、文档、类型

索引,Elasticsearch中的索引有多层的意思:a. 某一类文档的集合就构成了一个索引,类比到数据库就是一个数据库(或者数据库表);b.它还描述了一个动作,就是将某个文档保存在elasticsearch的过程也叫索引;c. 倒排索引。

文档,具体的一条数据,类比到数据库就是一条记录。

类型,在7.0之前,一个Index可以创建多个类型,从7.0开始,一个索引只能创建一个类型,也就是_doc

DBMSElasticsearch
databaseIndex
tabletype(在7.0之后type为固定值_doc)
RowDocument
ColumnField
SchemaMapping
SQLDSL(Descriptor Structure Language)

四. RestAPI

查看所以的索引


GET _cat/indices

查看movies这个所有的 mapping


GET movies

查看movies这个所有的数据总量


GET movies/_count

查看movies所有的数据


GET movies/_search

删除掉某个索引


DELETE movies

4.1 基本CRUD

不指定id的方式来添加数据,ES会自动的帮我们生成一个随机的id(是一个字符串)


# 没有指定id来添加数据,ES会自动的生成一个字符串的id
POST user/_doc
{
    "firstname": "Jack",
    "lastname": "Ma"
}

指定id的方式来添加一条数据,在uri和数据本身上都需要添加,如果只是在uri后面添加,那么数据本身是没有id的


POST user/_doc/34
{
  "id": 34,
  "firstname": "Rod",
  "lastname": "Jhonson"
}

要给已有的文档添加属性,以上面的数据为例,添加一个 age 属性,方式如下:


POST user/_update/34
{
  "doc": {
    "age": 45
  }
}

要删除掉某个属性,或者修改属性的值,只能通过覆盖的的方式:


POST user/_doc/34
{
  "id" : 34,
  "firstname" : "Bob",
  "lastname" : "Jhonson"
}

删除某一条数据


DELETE user/_doc/34

分页查询


GET movies/_search
{
    "from": 0,
    "size": 10
}

添加数据,如果有就不添加,如果没有就添加


POST user/_create/34
{
   "id": 56,
   "firstname": "Doug",
   "lastname": "Cutting"
}

批量查询,可以查询同一个索引的不同的数据,也可以跨索引查询


GET _mget
{
  "docs": [
    {"_index": "user", "_id": 1},
    {"_index": "movies", "_id": 456}
  ]
}

批量插入数据,可以指定索引,也可以不指定


POST user/_bulk
{"index": {"_id": 1}}
{"firstname": "Jack", "lastname": "Ma", "id": 1}
{"index": {"_id": 2}}
{"firstname": "Pony", "lastname": "Ma", "id": 2}
{"index": {"_id": 3}}
{"firstname": "Robbin", "lastname": "Li", "id": 3}
{"index": {"_id": 4}}
{"firstname": "Doug", "lastname": "Lea", "id": 4}

4.2 Request Body查询

在有些查询中,可能会用到各种特别复杂的查询,那么就需要使用Request Body查询。

以year的倒序排序,查询电影年份在 [2017, 2018]的数据, query只能单条件查询


GET movies/_search
{
  "sort": [
    {
      "year": {
        "order": "desc"
      }
    }
  ],
  # query中只能有一个条件
  "query": {
    "range": {
      "year": {
        "gte": 2017,
        "lte": 2018
      }
    }
  }
}

以year的倒序排序,查询titile中包含有Beautiful或者Mind的数据, query只能单条件查询


GET movies/_search
{
  "sort": [
    {
      "year": {
        "order": "desc"
      }
    }
  ],
  # query中只能有一个条件
  "query": {
    "match": {
      "title": "Beautiful Mind"
    }
  }
}

按照年份的倒序,分页查询


GET movies/_search
{
  "sort": [
    {
      "year": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 20
}

短语匹配,查询title中包含有 “Beautiful Mind” 这个短语的的电影


GET movies/_search
{
  "query": {
    "match_phrase": {
      "title": "Beautiful Mind"
    }
  }
}

E. 只查询部分列


GET movies/_search
{
  "_source": ["title", "year"]
}

多个条件查询,多条件查询必须使用bool


GET movies/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "year": {
              "gte": 2017,
              "lte": 2018
            }
          }
        },
        {
          "match": {
            "title": "Beautiful Mind"
          }
        }
      ]
    }
  }
}

查询电影的title或者genre中包含有 beautiful 或 mind 或 Romance 的所有的电影


GET movies/_search
{
  "query": {
    "multi_match": {
      "query": "beautiful mind Romance",
      "fields": ["title", "genre"],
      "type": "best_fields"
    }
  }
}

查询电影名字中包含有 beautiful 或者 mind,或者电影在 [2017, 2018] 年上映的所有的电影


GET movies/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "Beautiful Mind"
          }
        },
        {
          "range": {
            "year": {
              "gte": 2017,
              "lte": 2018
            }
          }
        }
      ]
    }
  }
}

4.3 自动补全功能

​ 自动补全对于一个搜索引擎或者电商网站来都是使用最为为频繁的一个功能,所以对性能要求是极高,所以ES针对这种情况专门设计了一种数据 completion, 会将用户搜索过所有的数据都存储到内容中,下次再进行前缀匹配的直接从内容中找;它还能做到预先加载;

A. 定义mapping并执行,需要自动补全的属性的 type 必须是 completion


{"mappings" : {
   "properties" : {
      "@version" : {
        "type" : "text",
        "fields" : {
          "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "genre" : {
        "type" : "completion",  
        "fields" : {
          "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "id" : {
        "type" : "text",
        "fields" : {
          "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
     },
     "title" : {
        "type" : "completion",
        "fields" : {
          "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "year" : {
        "type" : "long"
      }
    }
  }
}

执行如下命令,重新定义mapping

put movies

B. 执行推荐


# size是提示的数量、skip_duplicate是忽略掉重复
GET movies/_search
{
  "suggest": {
    "title-suggest": {
      "prefix": "bokx",
      "completion": {
         "field": "title",
         "size": 20,
         "skip_duplicates": true   
      }
    }
  }
}

不足: completion这个推荐的方式只能是前缀查询,但是其查找速度极快。