Elasticsearch初体验-第一次使用就感觉他很勇,搜索界的扛把子!

4,089 阅读9分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情

引言

What is Elasticsearch?

You know, for search (and analysis)

Elasticsearch is the distributed search and analytics engine at the heart of the Elastic Stack.

Elasticsearch provides near real-time search and analytics for all types of data.

从官网介绍可以看出几个关键的字眼,Elasticsearch是分布式的搜索、存储和数据分析引擎。Elasticsearch为所有类型的数据提供近乎实时的搜索和分析。

它很强很好用。后续会输出一些 Elasticsearch 的相关文章,本文只简单体验体验它的搜索功能。

准备工作

安装、启动Elasticsearch,kibana

版本:7.9

下载 Elasticsearch 和 kibana ,解压之后进入各自文件夹下的bin目录下,双击.bat文件启动即可(Windows OS),此处略过。

本文主要目的是看Elasticsearch 的查询功能有多爽,原理性问题后面再说。

验证Elasticsearch启动:

image.png

Kibana启动成功:

image.png

另外,建议安装一个elasticsearch-head,它能帮助我们很直观的查看ES节点状态。

elasticsearch-head提供可视化的操作页面,对ElasticSearch搜索引擎进行各种设置和数据检索功能。

可以很直观的查看集群的健康状况,索引分配情况,还可以管理索引和集群以及提供方便快捷的搜索功能等等。

安装、启动 elasticsearch-head

1. 安装node,略
2. 解压 elasticsearch-head-master ,并进入该文件夹,修改 Gruntfile.js ,在connect.server.options添加hostname: '*'connect: {
	server: {
		options: {
			hostname: '*',
			port: 9100,
			base: '.',
			keepalive: true
		}
	}
}
3. npm安装:D:\Java\elasticsearch-head-master> npm install
4. 启动:D:\Java\elasticsearch-head-master> npm run start

D:\Java\elasticsearch-head-master> npm run start                                                
> elasticsearch-head@0.0.0 start D:\Java\elasticsearch-head-master
> grunt server

Running "connect:server" (connect) task
Waiting forever...
Started connect web server on http://localhost:9100

ES创建索引

启动Kibana后,直接在 Kibana Dev Tools 控制台执行。

image.png

  1. 建立索引
# 建立city索引
PUT /city

# 创建的每个索引都可以具有与之关联的特定设置,这些设置在主体中定义
# number_of_shards的默认值为1
# number_of_replicas的默认值为1(即每个主分片一个副本)
PUT /test
{
  "settings": {
    "index": {
      "number_of_shards": 3,
      "number_of_replicas": 2
    }
  }
}

image.png

我们可以看到,建立的 city 索引,有1个分片,1个副本,这也是默认值;而 test 索引,通过 settings 配置了分片数为3,副本数为2。由图中也可以看出 test 有3个分片,每个分片有2个副本。

为什么副本都是unassigned的呢?这是因为 ES不允许Primary和它的Replica放在同一个节点中,并且同一个节点不接受完全相同的两个Replica ,而我本地只启动了一个ES节点。

  1. 删除索引

上图中,test 和 city 中间有一个 ilm-history-2-000001 ,我也不知道它是啥,要不把它删掉吧?

# 删除索引
DELETE /ilm-history-2-000001

# 执行结果
{
  "acknowledged" : true
}

可以通过查询索引验证一下:

# 查询索引
GET /ilm-history-2-000001

{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index [ilm-history-2-000001]",
        "resource.type" : "index_or_alias",
        "resource.id" : "ilm-history-2-000001",
        "index_uuid" : "_na_",
        "index" : "ilm-history-2-000001"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index [ilm-history-2-000001]",
    "resource.type" : "index_or_alias",
    "resource.id" : "ilm-history-2-000001",
    "index_uuid" : "_na_",
    "index" : "ilm-history-2-000001"
  },
  "status" : 404
}

也可以通过 elasticsearch-head 查看:

image.png

没有 ilm-history-2-000001 索引了。

  1. 插入数据

这个语法很简单,可参考如下:

POST /city/_doc/1
{
    "city" : "En shi",
    "province" :  "Hubei province",
    "acreage" :  24111
}

POST /city/_doc/2
{
    "city" : "E zhou",
    "province" :  "Hu bei province",
    "acreage" :  1594
}

POST /city/_doc/3
{
    "city" : "Zheng zhou, China",
    "province" :  "Henan province, capital",
    "acreage" :  7446
}

POST /city/_doc/4
{
    "city" : "Bei jing",
    "province" :  "Beijing China, capital",
    "acreage" :  16410
}

POST /city/_doc/5
{
    "city" : "Nan jing",
    "province" :  "Jiangsu province, capital",
    "acreage" :  6587
}

POST /city/_doc/6
{
    "city" : "Shen zhen",
    "province" :  "Guangdong province",
    "acreage" :  1997
}

POST /city/_doc/7
{
    "city" : "Guang zhou",
    "province" :  "Guangdong province, capital",
    "acreage" :  7434
}

以上面的数据为基础,开始ES常用查询。

ES查询

Elasticsearch 查询语法有很多,下面针对常用查询做一个总结。

Query_string

  1. 查询所有

GET /索引/_search

GET /city/_search

image.png

查询出所有的7条记录,并且 relation 类型为 eqequal ),max_score 为1.0(相关度分数)

  1. 带参数的查询

GET /索引/_search?q=xx:xx

GET /city/_search?q=city: Bei Jing

"hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 2.8371272,
    "hits" : [
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "4",
        "_score" : 2.8371272,
        "_source" : {
          "city" : "Bei jing",
          "province" : "Beijing China, capital",
          "acreage" : 16410
        }
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "5",
        "_score" : 1.1631508,
        "_source" : {
          "city" : "Nan jing",
          "province" : "Jiangsu province, capital",
          "acreage" : 6587
        }
      }
    ]
  }

这个查询把 city 中带 jing 的都查出来了,但相关度分数不一样。

  1. 分页,排序查询

GET /索引/_search?from=x&size=x&sort=xx:[asc|desc]

GET /city/_search?from=0&size=3&sort=acreage:asc

"hits" : {
    "total" : {
      "value" : 7,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "2",
        "_score" : null,
        "_source" : {
          "city" : "E zhou",
          "province" : "Hu bei province",
          "acreage" : 1594
        },
        "sort" : [
          1594
        ]
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "6",
        "_score" : null,
        "_source" : {
          "city" : "Shen zhen",
          "province" : "Guangdong province",
          "acreage" : 1997
        },
        "sort" : [
          1997
        ]
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "5",
        "_score" : null,
        "_source" : {
          "city" : "Nan jing",
          "province" : "Jiangsu province, capital",
          "acreage" : 6587
        },
        "sort" : [
          6587
        ]
      }
    ]
  }

查出来按面积 acreage 排序的前3条记录,总记录是7。还可以看出,相关度分数为 null

Query DSL

  1. match_all :匹配所有
GET /city/_search
{
  "query": {
    "match_all": {}
  }
}

GET /city/_search 不带 {} 查询结果一致。

  1. match:xx字段包含xx

查询 city 字段中带 zhou 的:

GET /city/_search
{
  "query": {
    "match": {
      "city": "zhou"
    }
  }
}

# 结果截取
"hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 0.8266786,
    "hits" : [
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "2",
        "_score" : 0.8266786,
        "_source" : {
          "city" : "E zhou",
          "province" : "Hu bei province",
          "acreage" : 1594
        }
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "3",
        "_score" : 0.8266786,
        "_source" : {
          "city" : "Zheng zhou",
          "province" : "Henan province, capital",
          "acreage" : 7446
        }
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "7",
        "_score" : 0.8266786,
        "_source" : {
          "city" : "Guang zhou",
          "province" : "Guangdong province, capital",
          "acreage" : 7434
        }
      }
    ]
  }

查出来3条结果,都有相关度分数。

  1. sort :排序,正序,倒序

查询 province 中包含 capital 的并且按照面积倒序排序:

GET /city/_search
{
  "query": {
    "match": {
      "province": "capital"
    }
  },
  "sort": [
    {
      "acreage": {
        "order": "desc"
      }
    }
  ]
}

image.png

  1. multi_match :根据多个字段查询一个关键词

查询 cityprovince 字段中包含 China 的:

GET /city/_search
{
  "query": {
    "multi_match": {
      "query": "China",
      "fields": ["city", "province"]
    }
  }
}

image.png

  1. _source 元数据:可以指定显示的字段

设置查询结果只显示acreage字段:

GET /city/_search
{
  "query": {
    "multi_match": {
      "query": "China",
      "fields": ["city", "province"]
    }
  },
  "_source": ["acreage"]
}

image.png

只显示了 acreage 字段。

  1. 分页(deep-paging)

按照 acreage 倒序排序,并分页,每页3条记录,查询第一页:

GET /city/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "acreage": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 3
}

全文检索 Full-text queries

  1. query-term & query-match
GET /city/_search
{
  "query": {
    "term": {
      "city": {
        "value": "Guang zhou"
      }
    }
  }
}

GET /city/_search
{
  "query": {
    "match": {
      "city": "Guang zhou"
    }
  }
}

query-term查询结果:

image.png

没有任何记录!!!再来看一下query-match查询结果:

image.png

有3条记录!!!

  1. match和term区别

为什么会出现1那样的结果呢?因为 query-term 查询的term不会 分词 ,会将 Guang zhou 当做一个整体进行操作,而match会进行 分词,分成 Guangzhou ,所以查询结果里面city包含 zhou 的都出来了!

  1. 全文检索

我们来看下面这个查询:

GET /city/_search
{
  "query": {
    "match": {
      "province": "Guangdong province, capital"
    }
  }
}

image.png

查出来7条记录,每条记录都有相关度分数,并且按照相关度分数由高到低排好序了!

验证一下分词:

GET /_analyze
{
  "analyzer": "standard",
  "text": "Guangdong province, capital"
}

结果:

{
  "tokens" : [
    {
      "token" : "guangdong",
      "start_offset" : 0,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "province",
      "start_offset" : 10,
      "end_offset" : 18,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "capital",
      "start_offset" : 20,
      "end_offset" : 27,
      "type" : "<ALPHANUM>",
      "position" : 2
    }
  ]
}

由此可见, Guangdong province, capital 被分成了 guangdongprovincecapital ,ES会全文检索这些词,所以查出了所有包含 guangdongprovincecapitaldocument

Phrase search 短语搜索

GET /city/_search
{
  "query": {
    "match_phrase": {
      "province": "Guangdong province, capital"
    }
  }
}

和全文检索相反,GuangdongGuangdong province, capital 会作为一个短语去检索,应该会只查出一条记录:

image.png

结果确实如此。

Query and filter 查询和过滤

bool

可以组合多个查询条件,bool查询也是采用 more_matches_is_better 的机制,因此满足must和should子句的 文档 (可理解为数据行)将会合并起来计算分值(相关度)。

  • must 必须满足

子句(查询)必须出现在匹配的文档中,并将有助于得分。

  • filte 过滤器 不计算相关度分数,cache

子句(查询)必须出现在匹配的文档中。但是不像must,查询的相关度分数将被忽略。

Filter子句在filter上下文中执行,这意味着相关度得分被忽略,并且子句被考虑用于缓存。查询性能很高。

  • should 可能满足(SQL中的or)

子句(查询)应出现在匹配的文档中。也可以不在文档中。

  • must_not:必须不满足 不计算相关度分数

子句(查询)不得出现在匹配的文档中。子句在过滤器上下文中执行,这意味着相关度得分被忽略,并且子句被视为用于缓存。由于忽略得分,得分将会返回数字0。

  • minimum_should_match

案例

city 这个index增加几条记录:

POST /city/_doc/8
{
    "city" : "Fo shan",
    "province" :  "Guangdong province",
    "acreage" :  3797
}
POST /city/_doc/9
{
    "city" : "Dong guan",
    "province" :  "Guangdong province",
    "acreage" :  2460
}
POST /city/_doc/10
{
    "city" : "Hui zhou",
    "province" :  "Guangdong province",
    "acreage" :  11347
}
POST /city/_doc/11
{
    "city" : "Mei zhou",
    "province" :  "Guangdong province",
    "acreage" :  15864
}
  • 查询province包含 Guangdong province ,面积大于10000,并且city字段包含 hui 的document:
GET /city/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "city": "hui"
          }
        }
      ],
      "filter": [
        {
          "match_phrase": {
            "province": "Guangdong province"
          }
        },
        {
          "range": {
            "acreage": {
              "gt": 10000
            }
          }
        }
      ]
    }
  }
}

这个执行过程是:

  1. 先对province中包含Guangdong province并且面积大于10000的进行筛选,符合条件的有2条记录:

image.png

  1. 再对city中包含hui的进行过滤,最终结果为:

image.png

  • bool多条件

查询city包含zhou不包含hui,province里包不包含guangdong都可以,面积要小于6000:

GET /city/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "city": "zhou"
        }}
      ],
      "must_not": [
        {"match": {
          "city": "hui"
        }}
      ],
      "should": [
        {"match": {
          "province": "guangdong"
        }}
      ],
      "filter": [
        {"range": {
          "acreage": {
            "lte": 6000
          }
        }}
      ]
    }
  }
}

image.png

  • 嵌套查询

minimum_should_match :参数指定should返回的文档必须匹配的子句的数量或百分比。

如果bool查询包含至少一个should子句,而没有must或 filter子句,则默认值为1。否则,默认值为0。

例如:

GET /city/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "city": "zhou"
        }}
      ],
      "should": [
        {"range": {
          "acreage": {
            "gt": 1500
          }
        }},
        {
          "range": {
            "acreage": {
              "gt": 5000
            }
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

minimum_should_match 的意思是在 should 的子句中必须至少满足一个条件。

本例查询结果为:

"hits" : {
    "total" : {
      "value" : 5,
      "relation" : "eq"
    },
    "max_score" : 2.7046783,
    "hits" : [
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "7",
        "_score" : 2.7046783,
        "_source" : {
          "city" : "Guang zhou",
          "province" : "Guangdong province, capital",
          "acreage" : 7434
        }
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "10",
        "_score" : 2.7046783,
        "_source" : {
          "city" : "Hui zhou",
          "province" : "Guangdong province",
          "acreage" : 11347
        }
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "11",
        "_score" : 2.7046783,
        "_source" : {
          "city" : "Mei zhou",
          "province" : "Guangdong province",
          "acreage" : 15864
        }
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "3",
        "_score" : 2.5874128,
        "_source" : {
          "city" : "Zheng zhou, China",
          "province" : "Henan province, capital",
          "acreage" : 7446
        }
      },
      {
        "_index" : "city",
        "_type" : "1",
        "_id" : "2",
        "_score" : 1.7046783,
        "_source" : {
          "city" : "E zhou",
          "province" : "Hu bei province",
          "acreage" : 1594
        }
      }
    ]
  }

查询出来的地市满足acreage大于1500或大于5000。

点个赞再走吧~