轻松把玩ElasticSearch

一、简单介绍ElasticSearch

ES基于Lucene,它解决了原生Lucene的不足,优化了Lucene的调用方式,并实现了高可用的分布式集群的搜索方案
ES也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得更简单
ES的特点更多是分布式的实时文件存储,每个字段都被索引并可被搜索,分布式的实时分析搜索引擎可以扩展到上百台服务器,处理PB级结构化或非结构化数据,高度集成化的服务,应用可以通过简单的RESTful API、各种语言的客户端甚至命令行与之交互。

二、ElasticSearch安装

1.ElasticSearch官方下载地址:https://www.elastic.co/cn/downloads/elasticsearch
2.解压下载的zip文件;然后打开目录下 /bin/elasticsearch.bat
如果出现闪退的情况有可能是内存不足的问题;使用记事本打开解压目录下 /conf/jvm.options 修改一下就行
在这里插入图片描述
启动后用浏览器服务 访问localhost:9200 如果出现以下界面ES集群就已经启动并正常运行了
在这里插入图片描述

三、ES辅助管理工具Kibana

1.Kibana官方下载地址:https://www.elastic.co/downloads/kibana
2.解压文件 打开解压目录下的 /bin/kibana.bat
3.使用访问localhost:5601
在这里插入图片描述
Discover:可视化查询分析器
Visualize:统计分析图表
Dashboard:自定义主面板
Timelion:Kibana时间序列展示组件
Dev Tools:操作ES的代码工具
Management:管理索引库

四、RESTful风格

RESTful是一种面向资源的架构风格:就是使用URL定位资源,用HTTP动词GET,POST,DELETE,PUT对其来操作。
例如:
GET /customer/delete?id=23 这是不符合RESTful规范的
规范写法: DELETE /customer/23 (删除方法) GET /customer/23(查询方法) 等
RESTful的优点
无状态:再调用一个接口的时候,可以不用考虑上下文,不用考虑当前状态,极大降低了复杂度。
透明性:直接暴露资源存在

五、使用Kibana管理ES

ES是面向文档的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在ES中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。
ES使用Javascript对象符号(JavaScript Object Notation),也就是JSON,作为文档序列化格式。JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。

_index:索引库,类似于关系型数据库里的“数据库”—它是我们存储和索引关联数据的地方。
_type:在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。可以是大写或小写,不能包含下划线或逗号。我们将使用 employee 做为类型名。
_id:与 _index 和 _type 组合时,就可以在ELasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成;另外还包括:_uid文档唯一标识(_type#_id)
_source:文档原始数据
_all:所有字段的连接字符串

文档的增删改
1.创建索引库:

在这里插入图片描述

2.创建索引数据:

在这里插入图片描述

POST testdb/user/1
{
  "id":1,
  "name":"admin",
  "age":18,
  "gender":"男"
}

运行结果

{
  "_index": "testdb",
  "_type": "user",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}
3.获取指定id的文档
GET testdb/user/1

运行结果

{
  "_index": "testdb",
  "_type": "user",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "id": 1,
    "name": "admin",
    "age": 18,
    "gender": "男"
  }
}

GET默认返回整个文档,可以使用_source来返回你想要的文档内容

GET testdb/user/1?_source=name,gender

运行结果

{
  "_index": "testdb",
  "_type": "user",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "gender": "男",
    "name": "admin"
  }
}

只返回文档类容不返回元数据

GET testdb/user/1/_source

运行结果

{
  "id": 1,
  "name": "admin",
  "age": 18,
  "gender": "男"
}
4.修改文档
POST testdb/user/1
{
  "id":1,
  "name":"admin",
  "age":18,
  "gender":"女"
}

运行结果

{
  "_index": "testdb",
  "_type": "user",
  "_id": "1",
  "_version": 2,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": false
}

在响应中发现ES把_version增加了,而且created 标识为 false
因为同索引、同类型下已经存在同ID的文档。
在这里插入图片描述
在内部,Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。

5.局部跟新文档

接受一个局部文档参数 doc,它会合并到现有文档中,对象合并在一起,存在的标量字段被覆盖,新字段被添加。

POST testdb/user/1/_update
{
  "doc":{
    "name":"admin2",
    "age":20
  }
}

运行结果

{
  "_index": "testdb",
  "_type": "user",
  "_id": "1",
  "_version": 4,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}
6.脚本跟新
POST testdb/user/1/_update
{
  "script":"ctx._source.age += 2"
}

运行后查询结果
在这里插入图片描述
在上面的例子中, ctx._source指向当前被更新的文档。
注意:目前的更新操作只能一次应用在一个文档上。

7.删除文档
DELETE testdb/user/1

存在文档的时候返回

{
  "found": true,
  "_index": "testdb",
  "_type": "user",
  "_id": "1",
  "_version": 6,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

文档不存在时返回

{
  "found": false,
  "_index": "testdb",
  "_type": "user",
  "_id": "1",
  "_version": 7,
  "result": "not_found",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

注意:尽管文档不存在,但_version依旧增加了。这是内部记录的一部分,它确保在多节点间不同操作可以有正确的顺序。

8.批量操作
POST _bulk
{"create":{"_index":"testdb2","_type":"user","_id":1}}
{"title":"批量添加一"}
{"index":{"_index":"testdb2","_type":"user","_id":2}}
{"title":"批量添加二"}

运行结果

{
  "took": 392,
  "errors": false,
  "items": [
    {
      "create": {
        "_index": "testdb2",
        "_type": "user",
        "_id": "1",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "created": true,
        "status": 201
      }
    },
    {
      "index": {
        "_index": "testdb2",
        "_type": "user",
        "_id": "2",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "created": true,
        "status": 201
      }
    }
  ]
}

文档简单查询
1.查询所有
GET testdb2/user/_search

查询结果

{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "testdb2",
        "_type": "user",
        "_id": "2",
        "_score": 1,
        "_source": {
          "title": "批量添加二"
        }
      },
      {
        "_index": "testdb2",
        "_type": "user",
        "_id": "1",
        "_score": 1,
        "_source": {
          "title": "批量添加一"
        }
      }
    ]
  }
}
2.批量获取

方式一:

GET _mget
{
  "docs":[
      {"_index":"testdb2",
        "_type":"user",
        "_id":1
      },
      {"_index":"testdb2",
        "_type":"user",
        "_id":2
      }
    ]
}

查询结果

在这里插入图片描述
方式二:同一个索引库的同一个类型下

GET testdb2/user/_mget
{
  "ids":["1","2"]
}

查询结果
在这里插入图片描述

空搜索

没有指定任何的查询条件,只返回集群索引中的所有文档

GET _search

查询结果
在这里插入图片描述

分页所搜

ES的分页和SQL使用 LIMIT 关键字返回只有一页的结果一样,ES接受 from 和 size 参数:
size : 每页条数,默认 10
from : 跳过开始的结果数,默认 0

GET _search?size=5&from=0
查询字符串搜索

一个搜索可以用纯粹的uri来执行查询。在这种模式下使用搜索,并不是所有的选项都是暴露的。它可以方便快速进行 curl 测试。

GET  testdb3/user/_search?q=age:19

六、DSL查询

ES提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。
DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。

查询字符串模式

GET  testdb3/user/_search?q=name:admin9

DSL模式

GET  testdb3/user/_search
{
  "query": {
    "match": {
      "name": "admin9"
    }
  }
}

注意:使用DSL查询,必须要传递query参数给ES
比较全的一个DSL查询

GET  testdb3/user/_search
{
  "query": {
    "match": {
      "gender": "男"
    }
  },
  "from":0,
  "size":3,
  "_source": ["name","age","id"],
  "sort": [
    {"age":"desc"}
  ]
}

sort:排序 desc/asc

七、DSL过滤

GET  testdb3/user/_search
{
  "query": {
    "bool": {
        "must": [{"match": {"age": "16" }}],
        "filter": {
           "term": {"name": "admin1"}
      }
    }
  }
}

八、DSL查询与过滤进阶

全匹配
GET  testdb3/user/_search
{
  "query" : {
  "match_all" : {}
  }
}
标准查询

match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用match查询一个全文本字段,它会在真正查询之前用分析器先分析查询字符。

GET  testdb3/user/_search
{
  "query": {
    "match": {
     "gender": "女"
    }
  }
}

multi_match 查询允许你做 match查询的基础上同时搜索多个字段。
注意:match一般只用于全文字段的匹配与查询,一般不用于过滤。

单词搜索与过滤

Term:

GET  testdb3/user/_search
{
  "query": {
    "bool": {
      "must": { 
        "match_all": {} 
      }, 
  "filter": { 
  "term": { 
    "age": "21" 
    } 
  } 
} 
}
}

Terms
匹配多个

GET  testdb3/user/_search
{
  "query": {
    "terms": {
      "name": ["admin1", "admin2", "admin3"]
    }
  }
}
组合条件搜索与过滤

组合搜索bool可以组合多个查询条件为一个查询对象,查询条件包括must、should和must_not

GET  testdb3/user/_search
{
  "query": {
    "bool": {
      "must": [{"term":{"hobby":"篮球"}}], 
      "should": [{"term":{"hobby":"学习"}}], 
      "must_not": [{"term":{"hobby":"PlayGame"}}], 
    "filter": {"term": {"age": "21"}} 
    } 
  }
}

注意:如果 bool 查询下没有must子句,那至少应该有一个should子句。但是 如果有 must子句,那么没有 should子句也可以进行查询。

范围查询与过滤

range过滤允许我们按照指定范围查找一批数据
gt:> gte:>= lt:< lte:<=
查询年龄在20至25(包括20不包括25)之间的用户

GET  testdb3/user/_search
{
  "query":{
    "range": {
      "age": {
        "gte": 20,
        "lt": 25
      }
    }
  }
}
存在和缺失过滤器
GET  testdb3/user/_search
{
  "query": {
    "bool": {
      "must": [{
        "match_all": {}
      }],
    "filter": {
      "exists": { "field": "name" }
      }
    }
  }
}

注意:exists和missing只能用于过滤结果。

通配符搜索

使用*代表0~N个,使用?代表1个

GET  testdb3/user/_search
{
  "query": {
    "wildcard": {
      "name": "a*n?"
    }
  }
}
前匹配搜索与过滤

和term查询相似,前匹配搜索不是精确匹配,而是类似于SQL中的like ‘key%’

GET  testdb3/user/_search
{
  "query": {
    "prefix": {
      "name": "admin"
    }
  }
}

九、分词器

ES默认对英文文本的分词器支持较好,但和lucene一样,如果需要对中文进行全文检索,那么需要使用中文分词器,同lucene一样,在使用中文全文检索前,需要集成IK分词器。

安装使用

1.ES的IK分词器插件源码地址:https://github.com/medcl/elasticsearch-analysis-ik
2.将下载的文件解压并将其内容放置于ES根目录/plugins/ik
3.插件配置:plugin-descriptor.properties
4.词典配置:config/IKAnalyzer.cfg.xml
5.重启ES并测试以下代码

POST _analyze
{
  "analyzer":"ik_smart",
  "text":"东方头条网 东方网 旗下《东方头条》是一款会自动学习的资讯软件,它会分析你的兴趣爱好,为你推荐喜欢的内容,并且越用越懂你.就要你好看,东方头条新闻网!"
}
文档映射

ES的文档映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型。
1.基本字段类型
字符串:text,keyword( text默认为全文文本,keyword默认为非全文文本)
数字:long,integer,short,double,float
日期:date
逻辑:boolean
2.复杂数据类型
对象类型:object
数组类型:array
地理位置:geo_point,geo_shape

简单类型映射
PUT testdb5
{
  "mappings": {
    "user": {
      "properties": {
        "id": {"type": "integer"},
        "info": {
          "type": "text",
          "analyzer": "ik_smart"
        }
      }
    },
    "dept": {"properties": {"id": { "type": "integer"}}}
  }
}

注意:你可以在第一次创建索引的时候指定映射的类型。此外,你也可以晚些时候为新字段添加映射(或者为已有的类型更新映射)

对象的映射与索引
PUT testdb6
{
  "mappings": {
    "user": {
      "properties": {
        "id": {"type": "long"},
        "girl": {
          "properties":{
            "name": {"type": "keyword"},
            "age": {"type": "long"}
          }
        }
      }
    }
  }
}

注意:Lucene不理解内置对象,一个lucene文档包含键值对的一个扁平化列表,以便于ES索引内置对象,它把文档转换为类似这样:

{
    "id": 1,
    "girl.name":"admin",
    "girl.age":26
}
对象数组的映射
PUT testdb7
{
  "mappings": {
    "user": {
      "properties": {
        "id": {
            "type": "long"
          },
        "girl": {
          "properties": {
            "age": { "type": "long" },
            "name": { "type": "text" }
           }
        }
      }
    }
  }
}

注意:扁平化后,对象属性的相关性已经丢失,因为每个多值字段只是一个数值集,不是排序的数组。

全局映射

全局映射可以通过动态模板和默认设置两种方式实现。
默认方式:default
索引下所有的类型映射配置会继承_default_的配置

PUT testdb8
{
  "mappings": {
    "_default_": { 
      "_all": {
        "enabled": false
      }
    },
    "user": {}, 
    "dept": { 
      "_all": {
        "enabled": true
      }
    }
  }
}
动态模板

在实际应用场景中,一个对象的属性中,需要全文检索的字段较少,大部分字符串不需要分词,因此,需要利用全局模板覆盖自带的默认模板

PUT _template/global_template
{
  "template":   "*",
  "settings": { "number_of_shards": 1 },
  "mappings": {
    "_default_": {
      "_all": { 
        "enabled": false
      },
      "dynamic_templates": [
        {
          "string_as_text": {
            "match_mapping_type": "string",
            "match":   "*_txt",
            "mapping": {
              "type": "text",
              "analyzer": "ik_max_word",
              "search_analyzer": "ik_max_word",
              "fields": {
                "raw": {
                  "type":  "keyword",
                  "ignore_above": 256
                }
              }
            }
          }
        },
        {
          "string_as_keyword": { 
            "match_mapping_type": "string",
            "mapping": {
              "type": "keyword"
             }
          }
        }
      ]
    }
  }
}

注意:ES会默认把string类型的字段映射为text类型(默认使用标准分词器)和对应的keyword类型

在实际的类型字段映射时,会依次匹配:
(1)字段自定义配置
(2)全局dynamic_templates[string_as_textstring_as_keyword]
(3)索引dynamic_templates[…]
(4)ES自带的类型映射。 以最先匹配上的为准。

注意:索引库在创建的时候会继承当前最新的dynamic_templates,索引库创建后,修改动态模板,无法应用到已存在的索引库。

十、Java API

ES对Java提供一套操作索引库的工具包,即Java API。所有的ES操作都使用Client对象执行。

Maven的引入

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>esdemo</groupId>
  <artifactId>esdemo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <dependencies>
  	<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>5.2.2</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.7</version>
</dependency>
       <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
  </dependencies>
</project>

相关操作

public class ES_APITest {

	public TransportClient getClient() throws Exception{
		TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
		.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"),9300));
		return client;
	}
	//测试连接
	@Test
	public void testConn() throws Exception {
		TransportClient client = getClient();
		System.out.println(client);
		client.close();
	}
	//增加
	@Test
	public void testCreateIndex() throws Exception {
		//获取ES客户端
		TransportClient client = getClient();
		//创建source数据
		Map map = new HashMap();
		map.put("id", 1);
		map.put("name","admin");
		map.put("age", 18);
		map.put("gender","男");
		map.put("hobby","跑步");
		//新增数据
		IndexResponse indexResponse = client.prepareIndex("testdb4", "user", "1").setSource(map).get();
		//打印结果IndexResponse[index=testdb4,type=user,id=1,version=1,result=created,shards={"_shards":{"total":2,"successful":1,"failed":0}}]
		System.out.println(indexResponse);
		//关闭资源
		client.close();	
	}
	//删除
	@Test
	public void testDelete() throws Exception {
		//获取ES客户端
		TransportClient client = getClient();
		//删除 testdb4/user/1的索引
		DeleteResponse deleteResponse = client.prepareDelete("testdb4", "user", "1").get();
		//DeleteResponse[index=testdb4,type=user,id=1,version=2,result=deleted,shards=ShardInfo{total=2, successful=1, failures=[]}]
		System.out.println(deleteResponse);
		//关闭资源
		client.close();	
	}
	//修改   
	@Test
	public void testUpdate() throws Exception {
		//获取ES客户端
		TransportClient client = getClient();
		//创建修改的doc数据
		Map map = new HashMap();
		map.put("id", 1);
		map.put("name","admin_修改");
		map.put("age", 20);
		map.put("gender","女");
		map.put("hobby","弹钢琴");
		//跟新数据
		UpdateResponse updateResponse = client.prepareUpdate("testdb4", "user", "1").setDoc(map).get();
		//UpdateResponse[index=testdb4,type=user,id=1,version=2,result=updated,shards=ShardInfo{total=2, successful=1, failures=[]}]
		System.out.println(updateResponse);
		//关闭资源
		client.close();	
	}
	//批量操作   
	@Test
	public void testBulk() throws Exception {
		//获取ES客户端
		TransportClient client = getClient();
		//得到bulk请求对象
		BulkRequestBuilder bulk = client.prepareBulk();
		for(int i=1;i<30;i++){
			Map map = new HashMap();
			map.put("id", i);
			map.put("name","root"+i);
			map.put("age", 10+i);
			if(i%2!=0){
				map.put("country","China");
				map.put("gender","女");
			}else {
				map.put("country","USA");
				map.put("gender","男");
			}
			if(i%3==0){
				map.put("hobby","跑步");
			}else if(i%3==1){
				map.put("hobby","弹钢琴");
			}else{
				map.put("hobby","玩游戏");
			}
			//把数据放到批量请求里面去
			bulk.add(client.prepareIndex("testdb4", "user", ""+i).setSource(map));
		}
		//执行批量处理
		BulkResponse bulkResponse = bulk.get();
		System.out.println(bulkResponse);
		//关闭资源
		client.close();	
	}
	//搜索   
	@Test
	public void testSearch() throws Exception {
		//获取ES客户端
		TransportClient client = getClient();
		BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 匹配
        List<QueryBuilder> must = boolQueryBuilder.must();
        // 过滤
        List<QueryBuilder> filter = boolQueryBuilder.filter();
        filter.add(QueryBuilders.rangeQuery("age").gte(20).lte(30));
        // 设置分页 排序
        SearchResponse searchResponse = client.prepareSearch("testdb4").setFrom(0).setSize(10).setQuery(boolQueryBuilder).addSort("id", SortOrder.DESC).get();
        System.out.println("总条数:"+searchResponse.getHits().getTotalHits());
        SearchHit[] hits = searchResponse.getHits().getHits();
        // 循环数据结构
        for (SearchHit hit : hits) {
            System.out.println(hit.getSource());
        }
		//关闭资源
		client.close();	
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值