第一节 ElasticSearch了解及初步使用

一、ElasticSearch简介
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口并且提供java api。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。
1.1了解下Lucene
apache Lucene是一套用于全文搜索的开源程序库,具有成熟、高性能、可扩展、轻量级及强大的功能。区别于搜索引擎,Lucene是一整套工具,如果直接使用则需要开发的东西比较多。
1.1.1文档(document):索引与搜索的主要数据载体,包含一个或多个字段。类比于数据库的一行数据
1.1.2字段(field):文档的一个片段,包括字段名称和内容(类比于表字段)
1.1.3词项(term):搜索时的一个单位,代表文本中的某个词。
1.1.4词条(token):词项在字段中的一次出现,包括词项的文本、开始和结束的位移以及类型。
1.1.5倒排索引:倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排 索引文件。
1.1.6 fieldData/docValue:创建索引文件的所有文档的某个字段会被单独存储起来。 对于这块,Lucene 经历了两阶段的发展。第一阶段是fieldData ,查询时从倒排索引反向构成doc-term。
另外,还有段文件、分析器、过滤器、分词器、迭代器等概念就不在这里一一描述了。

1.2ElasticSearch的基本概念
1.2.1索引:存储数据的地方,类比于一个数据库实例。可以向索引写入文档或读取文档。ElasticSearch中的索引可能由一个或多个lucene索引构成,具体细节由ElasticSearch的索引分片(shard)、复制(replica)机制及其配置决定。
1.2.2类型(type):类比于数据库中的一张表(表名)。每一个文档都有与之对应的类型定义,所以在一个索引中可以存储多种文档类型,并为不同的文档类型提供不同的映射。
1.2.3文档(document):类比于表中的一行数据。elasticsearch世界中的主要实体,文档由一个或多个字段构成,从客户端的角度看,文档是一个json串。
1.2.4映射(mapping):所有文档在写入索引前都需要先进行分析。用户可以设置一些参数,来决定如何将输入文本分割为词条,哪些词条应该被过滤掉,哪些附加处理是必要的(比如移除HTML标签等).此外,elasticsearch也提供了各种特性,如排序时所需的字段内容信息。这就是映射扮演的角色,存储所有这种元信息。
1.2.5节点(node):单个的ElasticSearch服务实例称为节点,一般部署一个ElasticSearch节点就足以应付大多数简单应用,但考虑到容错性或在数据膨胀到单机无法应付这些状况的时候,更倾向于使用多节点的集群。
1.2.6集群(cluster):当数据量或查询压力超过单机负载时,需要多个节点来协同处理,所有这些节点组成的系统称为集群。集群同时也是无间断提供服务的一种解决方案。
1.2.7分片(shard):集群允许系统存储的数据总量超过单机容量。为了满足这个需求,ElasticSearch将数据散布到多个物理lucene索引上。这些lucene索引称为分片(shard),而散布这些分片的过程称为分片处理(sharding)。ElasticSearch会自动完成分片处理,并且让这些分片呈现一个大索引的样子。除了ElasticSearch本身自动进行分片处理外,用户为具体的应用进行参数调优也至关重要,因为分片的数量在索引创建时就已经配置好,而且之后无法改变。
1.2.8副本(replica):副本则解决了访问压力过大时单机无法处理所有请求的问题。即为每个分片创建冗余的副本,处理查询时可以把这些副本用作最初的主分片(primary shard).即使某个分片所在的节点空压机,ElasticSearch也可以使用鞭副本,从而不会造成数据丢失,而且支持在任意时间点添加或移除副本。
1.2.9网关(gateway):在ElasticSearch的工作过程中,关于集群状态,索引设置的各种信息都会被收集起来,并在网关中被持久化。

1.3ElasticSearch的架构
1.3.1合理的默认配置:用户在简单安装以后能直接使用。
1.3.2默认分布式工作模式:每个节点总是假定自己是某个集群的一部分或将是某个集群的一部分,一旦工作启动节点便会加入某个集群。
1.3.3对等架:可以避免单点故障。节点会自动连接到集群中的其他节点,进行相互的数据交换和监控操作。包括自动复制。
1.3.4易于扩充新节点
1.3.5没有对索引中的数据结构强加任何限制,从而可以随时调整数据模型。
1.3.6准实时搜索和版本同步。考虑到ElasticSearch的分布式特性,查询延迟和节点之间临时的数据不同步是难避免的。
1.3.7当ElasticSearch节点启动时,它使用广播技术(也可配置为单播)来发现同一个集群中的其他节点并与它们连接。集群中会有一个节点被选为管理节点。该节点负责集群的状态管理以及在集群拓扑变化时做出反应,分发索引分片至集群的相应节点上。
1.3.8故障检测:管理节点会监控所有的可用节点,检查他们是否正在工作(发送ping请求,某未响应则节点 从集群中移除)
1.3.9与ElasticSearch通信:REST API或java api
1.3.10索引数据:ElasticSearch允许使用REST API向服务器发送文档(HTTP协议)。或使用bulk API或UDP bulk API来一次性发送多个文档(UDP协议)。再者是使用插件发送数据称为河流(river),河流运行在ElasticSearch节点上,能够从外部系统获取数据。建索引只会发生在主分片上。
1.3.11查询数据:可以使用查询语言DSL(基于JSON的可用于构建复杂查询的语言),查询类型包括:词项查询、短语查询、范围查询、模糊查询、通配符查询等。通过组合简单查询构建复杂查询。查询分为两个阶段:分散阶段和合并阶段。分散阶段将查询分发到包含相关文档的多个分片中去执行查询,合并阶段则从众多分片中收集返回结果,然后进行合并,排序及后续处理等,最后返回给客户端。
1.3.12索引配置:ElasticSearch提供了一些功能使得用户能手动配置,例如用户可以通过映射来配置自定义的文档结构,设置索引的分片和副本数,定制文本分析过程等等。
1.3.13系统管理和监控:ElasticSearch提供了系统管理与监控相关的API。


二、部署测试
2.1下载
可以通过: https://www.elastic.co/cn/downloads/elasticsearch 地址下载各版本。
2.2安装
1.首先要保障具有java运行环境,最好是java 1.8及以上版本
2.解压ElasticSearch到指定目录即可:比如(E:\software\elasticsearch-5.3.0)
3.直接进入bin目录,window下执行elasticsearch.bat即可启动
4.启动完成后,在浏览器中输入: http://localhost:9200/ 有一段json返回值,说明启动成功
5.接下来就可以使用http request工具(比如谷歌的postman或火狐的poaster)向服务器添加或查询文档。
添加: http://localhost:9200/es/t_user/2 -d {JSON数据}
查询:http://localhost:9200/es/_search?pretty

三、JAVA API的使用
3.1新建maven工程并引入 elasticsearch的jar包
测试示例代码:
public class ElsUtil {  
private static Logger logger = LoggerFactory.getLogger(ElsUtil.class);  
private static TransportClient client;  
public static TransportClient getClient(){  
Settings settings = Settings.builder()  
.put("cluster.name", "myApplication").build();  
try {
TransportClient client = new PreBuiltTransportClient(settings)
.addTransportAddress(new InetSocketTransportAddress( InetAddress.getByName("localhost"), 9300)); ElsUtil.client = client; return client; } catch (UnknownHostException e) { e.printStackTrace(); } return null; } public static void closeClient(){ if(client != null) { client.close(); } } public static void index(){ Map<String,Object> map = Maps.newHashMap(); map.put("id","1"); map.put("name","java customer"); map.put("desc","这个是使用java api添加的7人"); IndexResponse response = client.prepareIndex("es", "t_user") .setVersion(7l).setVersionType(VersionType.EXTERNAL)//可以指定外部版本号,作为此数据的版本号 .setSource(map).setId(String.valueOf(map.get("id"))) .execute().actionGet(); if(response != null) { System.out.println(response.getVersion()); } } public static void updateRequest(){ List<Map<String,Object>> list = getDataList(); for(Map<String,Object> map:list) { UpdateRequest updateRequest = new UpdateRequest(); updateRequest.index("es"); updateRequest.type("t_user"); updateRequest.id("5"); Map<String,Object> updMap = new HashMap<>(); updMap.put("name","只更新name"); updateRequest.doc(updMap); client.update(updateRequest).actionGet(); } } public static void updatePrepare(){ List<Map<String,Object>> list = getDataList(); Map<String,Object> updMap = new HashMap<>(); updMap.put("name","只更新11nam测试lll9"); updMap.put("desc","更新11name7788"); for(Map<String,Object> map:list) { //client.prepareUpdate("es", "t_user", "5") // .setDoc(updMap).get();//可以同时更新多个字段 //.setScript(new Script("ctx._source.name = \"prepare更新\""))//只能更新一个字段 //.setScript(new Script("ctx._source.desc = \"这个是特殊更新的\"")) //.get();//加上才会真正的更新 client.prepareUpdate("es", "t_user", "7") .setVersion(5l).setVersionType(VersionType.INTERNAL) .setScript(new Script("ctx._source.name = \"只更新11nam测试l7667377ll9\"")) .get(); } } public static void updatePrepareAdd(){ List<Map<String,Object>> list = getDataList(); Map<String,Object> updMap = new HashMap<>(); updMap.put("name","只更新11nam测试更新不了就新加"); updMap.put("desc","更新11name7788新加"); updMap.put("version",6); for(Map<String,Object> map:list) { IndexRequest indexRequest = new IndexRequest("es", "t_user", "6") .source(updMap); UpdateRequest updateRequest = new UpdateRequest("es", "t_user", "6") .script(new Script("ctx._source.id = \"6\""))//更新原不包含的字段时 会新添加 .upsert(indexRequest); UpdateResponse response = client.update(updateRequest).actionGet(); System.out.println(response.getVersion()); } } public static void delete(){ //版本号(插入,删除): //VersionType.INTERNAL 内部版本号,只有等于当前版本号才可以进行操作 //VersionType.EXTERNAL 外部版本号,只有大于当前版本号才可以进行操作,且update不支持此类型 DeleteResponse response = client.prepareDelete("es", "t_user", "7") .setVersion(7l) .setVersionType(VersionType.EXTERNAL)//外部版本号,或内部版本号 .get(); } public static void deleteByQuery(){ DeleteByQueryRequestBuilder deleteByQueryRequestBuilder = DeleteByQueryAction.INSTANCE.newRequestBuilder(client); deleteByQueryRequestBuilder.source().setIndices("es").setTypes("t_user"); //为什么提供了.source(“es”)设置setIndices,而没有提供setTypes?? //同步删除 //BulkByScrollResponse response = // deleteByQueryRequestBuilder.filter(QueryBuilders.matchQuery("name", "java")) // .get(); //long deleted = response.getDeleted(); //logger.info("delete:{}",deleted); //异步删除 deleteByQueryRequestBuilder.filter(QueryBuilders.matchQuery("name", "java")) .execute(new ActionListener<BulkByScrollResponse>() { @Override public void onResponse(BulkByScrollResponse bulkByScrollResponse) { long deleted = bulkByScrollResponse.getDeleted(); logger.info("异步delete:{}",deleted); } @Override public void onFailure(Exception e) { logger.info("异步delete"); logger.error("异步删除错误:{}",e); } }); } public static void getAsQuery(){ GetResponse response = client.prepareGet("es","t_user","2").get(); if(response.isExists()) { logger.info("getAsQueryResponseMap:{}",response.getSourceAsMap()); logger.info("getAsQueryResponse:{}",response.getSourceAsString()); } //多个ID查询 MultiGetResponse multiGetResponse = client.prepareMultiGet() .add("es","t_user","2") .add(new MultiGetRequest.Item("es","t_user","5")).get(); for (MultiGetItemResponse itemResponse : multiGetResponse) { GetResponse getResponse = itemResponse.getResponse(); if (getResponse.isExists()) { logger.info("MultiGetResponse:{}",getResponse.getSourceAsString()); } } } public static void query(){ SearchResponse searchResponse = client.prepareSearch().setIndices("es").setTypes("t_user") .setQuery(QueryBuilders.disMaxQuery() .add(QueryBuilders.termQuery("name","更新")) .add(QueryBuilders.matchQuery("name","更新")) .add(QueryBuilders.prefixQuery("name","java"))) .setFrom(0).setSize(2)//分页 .execute().actionGet(); logger.info("查询的结果:{}",searchResponse); } /** * 批处理查询 */ public static void multiQuery(){ MultiSearchResponse multiSearchResponse = client.prepareMultiSearch() .add(client.prepareSearch("es").setQuery(QueryBuilders.disMaxQuery() .add(QueryBuilders.termQuery("name","更新")) .add(QueryBuilders.matchQuery("name","更新")) .add(QueryBuilders.prefixQuery("name","李"))).request()) .add(client.prepareSearch("es") .setQuery(QueryBuilders.queryStringQuery("四")).request()) .execute().actionGet(); logger.info("查询的结果:{}",multiSearchResponse); } /** * 批量处理 */ public static void bulkHandler(){ BulkResponse bulkResponse = client.prepareBulk() .add(client.prepareIndex("es","t_user","9").setSource(getDataList().get(0)).request()) .add(client.prepareDelete("es","t_customer","9").request()) .execute().actionGet(); if(bulkResponse.hasFailures()){ logger.info("批处理的错误结果:{}",bulkResponse); } } public static void highlightQuery(){ HighlightBuilder highlightBuilder = new HighlightBuilder() .field("name").requireFieldMatch(false) .highlightQuery(QueryBuilders.matchQuery("name","1add")) .preTags(Const.HIGHLIGHT_PRE_TAGS) .postTags(Const.HIGHLIGHT_POST_TAGS); SearchResponse searchResponse = client.prepareSearch().setIndices("es").setTypes("t_user") .setQuery(QueryBuilders.disMaxQuery() .add(QueryBuilders.termQuery("name","更新")) .add(QueryBuilders.matchQuery("name","更新")) .add(QueryBuilders.prefixQuery("name","java"))) .highlighter(highlightBuilder) .setFrom(0).setSize(5)//分页 .execute().actionGet(); logger.info("查询的结果:{}",searchResponse); } public static void countQuery(){ SearchResponse searchResponse = client.prepareSearch().setIndices("es").setTypes("t_user") .setQuery(QueryBuilders.disMaxQuery() .add(QueryBuilders.termQuery("name","更新")) .add(QueryBuilders.matchQuery("name","更新")) .add(QueryBuilders.prefixQuery("name","java"))) .setSize(0)//不要数据 .execute().actionGet(); logger.info("查询的结果:{}",searchResponse.getHits().getTotalHits()); logger.info("查询的结果:{}",searchResponse); } public static List<Map<String,Object>> getDataList(){ List<Map<String,Object>> list = new ArrayList<>(); Map<String,Object> map = Maps.newHashMap(); map.put("id","1"); map.put("name","java 1add是的"); map.put("desc","1这个是使用java api添加的人修改了一次"); list.add(map); return list; }}

猜你喜欢

转载自blog.csdn.net/xpsallwell/article/details/77101746