搭建Elasticsearch集群
可以复制,但复制的时候注意把包里面的data删掉,否则几个里面的索引都一样,都有主分片,那么互相不承认对方,集群就不会生效了。
配置文件elasticsearch.yml需要修改,把下列配置放开:
cluster.name: dianping-app
node.name: node-1
network.host: 127.0.0.1
#对外端口
http.port: 9200
#集群间通信
transport.tcp.port: 9300
#允许跨域
http.cors.enabled: true
http.cors.allow-origin: "*"
#用来发现集群的节点
discovery.seed_hosts: ["127.0.0.1:9300", "127.0.0.1:9301", "127.0.0.1:9302"]
#哪些节点有资格竞选master
cluster.initial_master_nodes: ["127.0.0.1:9300", "127.0.0.1:9301", "127.0.0.1:9302"]
其他节点的配置,修改name和两个端口号即可。分别运行后,看一下节点:
创建一个索引:
PUT /test
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 2
}
}
这里我运行了3个,创建后是green
如果把主分片变成10呢?
那么把从分片变成3呢?
由此可以看出,es在主分片的创建上,可以在一个节点创建多个,但是从分片必须和主分片与其他从分片的节点分开。
基础语法
新建索引
分为结构化和非结构化
非结构化新建索引及删改查:非结构化是指在创建索引的时候不定义索引的mapping格式,类比数据库就是不定义表结构,在创建的时候会自动分析索引的结构,自动生成mapping结构。
写法:
PUT /employee/_doc/1
{
"name":"耗子",
"age":18
}
运行后会提示:
就创建好了。
还可以先setting,再创建值:
PUT /employee
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 2
}
}
PUT /employee/_doc/1
{
"name":"耗子",
"age":18
}
还可以做修改操作,把name改一下
运行结果:
可以看到,created变成了updated。所以PUT在所以没有id=1的时候,完成创建操作,如果已经存在这个id,就变成了更新操作了,并且version会+1。
查询的话用GET,例如:GET /employee/_doc/1即可。
需要注意的是上面的PUT是全量修改的,也就是说覆盖,在put的时候不管需不需要修改的字段都需要写上面。那么有全量修改,当然也会对应着有指定字段的修改。往下看
//指定字段修改
POST /employee/_update/1
{
"doc":{
"name":"耗子3"
}
}
GET看一下结果:
那么对于上面的put而言,是如果没有就创建,如果有就覆盖,那么如果想实现如果有就创建失败怎么办?
//强制指定创建,若已存在,则失败
POST /employee/_create/1
{
"name":"123",
"age":19
}
如果有,运行结果:
如果要删除某个文档,DELETE
DELETE /employee/_doc/1
查询全部文档呢?当然用search如果不给条件的话,es不会查询全部文档,会分页,具体详情后面讲复杂查询的时候再说。
GET /employee/_search
再在kibana上看下索引的结构:
结构化新建索引:
因为是非结构化创建,所以es会跟去情况创建mapping,有时候es创建的mapping跟我们想要的并不一样,所以绝大多数情况下,还是要结构化创建的。
//使用结构化的方式创建索引
PUT /employee
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "integer"
}
}
}
}
看一下kibana
接下来看看分页查询
//分页查询
GET /employee/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 1
}
这个分页查询的from和size很好懂,不解释了。
看一下带条件的查询
//带关键字条件查询
GET /employee/_search
{
"query": {
"match": {
"name": "耗子1"
}
}
}
查询结果:
改一下关键字,name改成小子,结果还是能查出来:
这就是es相较于传统数据库like查询的不同了。原理是在es没有设置分词器的时候,es默认会把中文每个字都分开,所以“子”就匹配上了。
带排序的查询:
//带排序
GET /employee/_search
{
"query": {
"match": {
"name": "小子"
}
},
"sort":[
{
"age":{"order":"desc"}
}
]
}
过滤查询
//带过滤
GET /employee/_search
{
"query": {
"bool": {
"filter": [
{
"term":{"age":18}
}
]
}
}
}
其中term相当于词的精确匹配,而match相当于分词匹配。
带聚合的查询:
//带聚合
GET /employee/_search
{
"query": {
"match": {
"name": "小子"
}
},
"sort":[
{
"age":{"order":"desc"}
}
],
"aggs": {
"group_by_age": {
"terms": {
"field": "age"
}
}
}
}
运行结果最后会加记录,通过年龄进行了统计:
高级语法
相对于基本语法,高级语法更注重分词的应用。
写个小查询demo
PUT /movie/_doc/1
{
"name":"Eating an apple a day & keeps the doctor away"
}
GET /movie/_search
{
"query": {
"match": {
"name": "eat"
}
}
}
先新建一个索引,然后查询eat,查询结果是空的
按说在英文中,eating和eat应该是一样的,但是这里是空的,因为这是分词器自己分的,我们想知道分词器怎么分的,那就要看下分词器了:
GET /movie/_analyze
{
"field": "name",
"text": "Eating an apple a day & keeps the doctor away"
}
可以看到:
对于人脑来说,eating和eat可能是同样的,但是对于及其来说,肯定是不一样的。
接下来说下analyze分词处理的过程:
首先文字过来以后,先经过字符过滤器,把标点符号之类的过滤掉,然后进入分词器,如果没有指定分词器的话,es会使用标准的字符处理器,这种处理器会使用空格和标点符号做分割,分割后会变成若干个单词,再进行分词过滤,在标准处理器中的过滤就是变小写。
接下来解决这样一个问题,那么就是在创建索引的时候指定分词器:
//使用结构化的方式创建索引
PUT /movie
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"name":{
"type": "text",
"analyzer": "english"
}
}
}
}
PUT /movie/_doc/1
{
"name":"Eating an apple a day & keeps the doctor away"
}
再查询eat,结果:
接下来还是用analyze查询一下分词情况
可以看到有很大的不同。
这里首先会把a/an/the这样的词去掉,因为英文分词器会认为这些词是没有意义的,对于英文分词器来说,他的分词过程:
字符过滤器(这里不仅过滤字符,还会过滤a/an/the这种词)->字符处理(跟之前一样,分开单词)->分词过滤(重点在这里,英文分词器会把单复数考虑进去,然后从中提炼出词干,例如day的词干就变成了dai,后面也都是一样的)。这样,无论是搜索eat还是apples,都能搜索到。
Elasticsearch字段类型
Text:可被分析索引的字符串类型
Keyword:不可被分析、只能被精确匹配的字符串类型
Date:日期类型,可以配合dateformat一起使用
数字类型:long、integer、short、double等
boolean:true、false
Array:["one","two"]
Object:可用json嵌套来描述
ip类型
Geo_point:地理位置,也就是经纬度