天天看点

ElasticSearch基础及用java操作实现crud

  1. ES即为了解决原生Lucene使用的不足,优化Lucene的调用方式,并实现了高可用的分布式集群的搜索方案,其第一个版本于2010年2月出现在GitHub上并迅速成为最受欢迎的项目之一。
  2. 首先,ES的索引库管理支持依然是基于Apache Lucene™的开源搜索引擎。
  3. ES也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful
  4. API来隐藏Lucene的复杂性,从而让全文搜索变得简单。 Lucene直接通过java
  5. API调用,而ES把这些API调用过程进行了的封装为简单RESTful请求,让我们调用起来更加简单.
  6. 不过,ES的核心不在于Lucene,其特点更多的体现为: 分布式的实时文件存储,每个字段都被索引并可被搜索 分布式的实时分析搜索引擎
  7. 可以扩展到上百台服务器,处理PB级结构化或非结构化数据 高度集成化的服务,你的应用可以通过简单的 RESTful
  8. API、各种语言的客户端甚至命令行与之交互。
  9. 上手Elasticsearch非常容易。它提供了许多合理的缺省值,并对初学者隐藏了复杂的搜索引擎理论。它拥有开瓶即饮的效果(安装即可使用),只需很少的学习既可在生产环境中使用。

安装es

官方下载地址:https://www.elastic.co/downloads/elasticsearch

安装之后 运行 bin/elasticsearch.bat

访问:http://localhost:9200/

安装辅助管理工具Kibana5

① Kibana5.2.2下载地址:https://www.elastic.co/downloads/kibana

② 解压并编辑config/kibana.yml,设置elasticsearch.url的值为已启动的ES

③ 启动Kibana5 : bin\kibana.bat

④ 默认访问地址:http://localhost:5601

Discover:可视化查询分析器

Visualize:统计分析图表

Dashboard:自定义主面板(添加图表)

Timelion:Timelion是一个kibana时间序列展示组件(暂时不用)

Dev Tools :Console(同CURL/POSTER,操作ES代码工具,代码提示,很方便)

Management:管理索引库(index)、已保存的搜索和可视化结果(save objects)、设置 kibana 服务器属性。

ES数据管理

ES是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在ES中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。

ES使用Javascript对象符号(JavaScript Object Notation),也就是JSON,作为文档序列化格式。

在ES中将对象转化为JSON并做索引要比在表结构中做相同的事情简单的多。

一个文档(document)不只有数据。它还包含元数据(metadata)—关于文档的信息。三个必须的元数据节点是:

ElasticSearch基础及用java操作实现crud

_index:索引库,类似于关系型数据库里的“数据库”—它是我们存储和索引关联数据的地方。

_type:类型,类似于关系型数据库中表

_id: 与 _index 和 _type 组合时,就可以在ELasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成。

另外还包括:_uid文档唯一标识(_type#_id)

_source:文档原始数据

_all:所有字段的连接字符串

ES数据的CRUD

传统数据库和ES的对应关系:

关系数据库(MYSQL) -> 数据库DB-> 表TABLE-> 行ROW-> 列Column

Elasticsearch -> 索引库Indices -> 类型Types -> 文档Documents -> 字段Fields

创建索引文档
①使用自己的ID创建:
PUT {index}/{type}/{id}
{
  "field": "value",//json格式,K-V键值对
  ...
}
②ES内置ID创建:
POST {index}/{type}/
{
  "field": "value",
  ...
}
①②ES响应内容:
{
"_index": "test",
"_type": "employee",
"_id": xxxxxx,
"_version": 1, //文档版本号
"created": true //是否新增
}
           

查询

③ 获取指定ID的文档
GET test/employee/123
③返回的内容:
{
"_index" : "test",
"_type" : "employee",
"_id" : "123",
"_version" : 1,
"found" : true,
"_source" : {
   "email": "[email protected]",
   "fullName": "皮蛋",
   ...
   "joine_date": "2016-06-01"
}
}
返回文档的部分字段:
//GET默认返回整个文档,通过
GET /itsource/employee/123?_source=fullName,email
           

修改

更新整个文档
同PUT {index}/{type}/{id}
在响应中,我们可以看到Elasticsearch把 _version  增加了。
{
...
"_version" : 2,
"created": false
}
           

删除

DELETE {index}/{type}/{id}
存在文档的返回:
{
"found" : true,
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 3
}
不存在的返回:
{
"found" : false,
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 4
}
           

搜索全部文档

GET _search
           

分页搜索

Elasticsearch接受 from 和 size 参数:

size : 每页条数,默认 10。

from : 跳过开始的结果数,默认 0

GET test/qqq/_search?size=5&from=10 #查询11-15
           

等等方法详细的可以查看官方的API

这里就不多介绍了,

【ES的一个核心 分词器】

在全文检索理论中,文档的查询是通过关键字查询文档索引来进行匹配,因此将文本拆分为有意义的单词,对于搜索结果的准确性至关重要,因此,在建立索引的过程中和分析搜索语句的过程中都需要对文本串分词。

ES中分词需要对具体字段指定分词器等细节,因此需要在文档的映射中明确指出。

对于字段要想使用分词器,需要做以下两件事情:

  1. 集成合适分词器-

    在服务端通过插件集成分词器

  2. 要设置字段的分词器

    通过客户端告诉es某个字段要使用某个分词

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

ES的IK分词器插件源码地址:https://github.com/medcl/elasticsearch-analysis-ik

并将其内容放置于ES根目录/plugins/ik

ES的文档映射(mapping)机制用于进行字段类型确认(字段是什么类型,那个分词器),将每个字段匹配为一种确定的数据类型。

文档映射用来确认字段的类型和分词器等信息.映射分三种:默认映射,全局映射,自定义映射.

当添加一个文档字段,首先是否有自定义映射规则,再判断全局映射规则,如果最后都没有的化就使用默认规则.

默认映射<全局映射<自定义映射

ES的文档映射【根据实际开发需求再确定映射规则】

下面才是正题,使用java操作ES 进行一个简单的CRUD

public class EsTest {
	private String index = "test";
	private String type = "emps";

	/**
	 * 1:测试创建客户端: 使用传输客户端:
	 * 
	 * @throws Exception
	 */
	@Test
	public void testGetClient() throws Exception {
		// org.elasticsearch.common.settings.Settings
		/*你可以设置client.transport.sniff为true来使客户端去嗅探整个集群的状态,
		把集群中其它机器的ip地址加到客户端中,
		这样做的好处是一般你不用手动设置集群里所有集群的ip到连接客户端,
		它会自动帮你添加,并且自动发现新加入集群的机器*/
		Settings setting = Settings.builder().put("client.transport.sniff", true).build();
		// 1:获取TransportClient 客户端:
		TransportClient client = new PreBuiltTransportClient(setting);
		TransportAddress transportAddress = new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300);
		client.addTransportAddress(transportAddress);
		// [email protected]95d
		System.out.println(client);
		// 关闭
		client.close();
	}

	public TransportClient getClient() throws Exception {
		// org.elasticsearch.common.settings.Settings
		Settings setting = Settings.builder().put("client.transport.sniff", true).build();
		// 1:获取TransportClient 客户端:
		TransportClient client = new PreBuiltTransportClient(setting);
		TransportAddress transportAddress = new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300);
		client.addTransportAddress(transportAddress);
		return client;
	}

	/**
	 * 添加数据
	 */
	@Test
	public void testIndex() throws Exception {
		for (int i = 1; i <= 10; i++) {
			// 1:获取客户端
			TransportClient client = getClient();
			// 2:添加列相当于使用管理工具的 PUT {index}/{type}/{id}
			IndexRequestBuilder prepareIndex = client.prepareIndex(index, type, "1" + i);
			// 添加数据
			Map<String, Object> map = new HashMap<>();
			map.put("id", 1 + i);
			map.put("name", "皮蛋" + i);
			//设置到Source中
			prepareIndex.setSource(map);
			// 发送请求
			prepareIndex.get();
			// 3:关闭
			client.close();
		}

	}

	/**
	 * 获取数据
	 * 
	 * @throws Exception
	 */
	@Test
	public void testGet() throws Exception {
		// 1:获取客户端
		TransportClient client = getClient();
		// 2:做获取
		GetResponse getResponse = client.prepareGet(index, type, "1").get();
		Map<String, Object> source = getResponse.getSource();
		System.out.println(source);
		// 3:关闭
		client.close();
	}

	/**
	 * 删除
	 * 
	 * @throws Exception
	 */
	@Test
	public void testDelete() throws Exception {
		// 1:获取客户端
		TransportClient client = getClient();
		// 2:删除
		client.prepareDelete(index, type, "1").get();
		// 3:关闭
		client.close();
	}

	/**
	 * 更新
	 * 
	 * @throws Exception
	 */
	@Test
	public void testUpdate() throws Exception {
		// 1:获取客户端
		TransportClient client = getClient();
		// 2:更新那一条
		UpdateRequestBuilder prepareUpdate = client.prepareUpdate(index, type, "12");
		// 设置更新的值
		Map<String, Object> map = new HashMap<>();
		map.put("id", 33);
		map.put("name", "xxx3");
		prepareUpdate.setDoc(map);
		// 发送请求
		prepareUpdate.get();

		// 3:关闭
		client.close();
	}

	/**
    *ES的查询是通过执行json格式的查询条件,在java中就是构造QueryBuilder对象,
    ES完全支持queryDSL风格的查询方式,QueryBuilder的构建类是QueryBuilders,
    filter的构建类是FilterBuilders。
					    "query": {
					    "bool": {
					              "must": [
					    {"match": {"description": "search" }}
					    ],
					               "filter": {
					                   "term": {"age": "12"}
					               }
					        }
					    },
					     "_source": ["id","name"],
					    "from": 20, 
					    "size": 10,
					    "sort": [{"join_date": "desc"},{"age": "asc"}]
					    }
	 * 	搜索
	 * @throws Exception
	 */
	@Test
	public void testSearcher() throws Exception {
		//获取客户端
	TransportClient client = getClient();
	SearchRequestBuilder builder = client.prepareSearch(INDEX);//设置查询索引库
	builder.setTypes(TYPE);//设置搜索类型
	//1)dsl=查询+过滤
	BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
	boolQuery.must(QueryBuilders.matchAllQuery());
	//过滤
	RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
	rangeQuery.lte(40);//小于等于
	rangeQuery.gte(20);//大于等于
	boolQuery.filter(rangeQuery); //年龄来过滤
	builder.setQuery(boolQuery);//设置过滤得到的数据
	//2)排序
	builder.addSort("age", SortOrder.ASC);
	//3)分页
	builder.setFrom(10).setSize(10);
	//4)截取字段
	builder.setFetchSource(new String[]{"name","age"}, null);
	//5)查询并打印结果
	SearchResponse response = builder.get();
	SearchHits hits = response.getHits();//总数+命中了哪些
	System.out.println("总命中数:"+hits.totalHits());
	SearchHit[] hits2 = hits.getHits();//命中了哪些
	System.out.println("=============命中内容===================");
	for (SearchHit searchHit : hits2) {
		Map<String, Object> source = searchHit.getSource();
		System.out.println(source);
	}
	System.out.println("=============命中内容===================");
	//关闭
	client.close();
	}


}
           

如有错误的地方还请各位大佬指点

继续阅读