最近项目有个需求,很棘手。有个统计功能,须要作到实时出结果。这个功能通过好几我的的处理都没有解决客户的问题。到了我这里,我就来填坑了。苦逼的程序猿,苦逼的命。谁叫我是这个项目现任负责人。经一番折腾大体了解到客户对这个统计分析的需求以后,就进入技术选项。老套路,确定先百度、谷歌、大神群各类咨询、了解。最后决定使用elasticseaarch。至于为什么选择它,相关同窗本身百度去。而我选择的缘由就是简单、易于维护、成本低、性能也是gan gan 的。html
接下来咱们看看elasticsearch index api是如何使用java 来进行相关操做。强烈建议在阅读如下所说的内容前必须先对elasticsearch有必定的了解java
参考官网APIgit
low level [https://www.elastic.co/guide/...]()
high level [https://www.elastic.co/guide/...]()github
如下源码下载链接:ESRestClient.java http://download.csdn.net/down...算法
pom.xmlapi
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>5.6.4</version>
</dependency>数组
建立链接app
private static Logger log = LoggerFactory.getLogger(ESRestClient.class);
private static RestClient lowLevelRestClient = null;
private static RestHighLevelClient highLevelRestClient = null;elasticsearch
/** * @Description: 初始化 * @return void * @throws * @author JornTang * @date 2017年12月23日 */ public void init(){ RestClientBuilder builder = RestClient.builder(new HttpHost("127.0.0.1", 9200)); builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() { @Override public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) { requestConfigBuilder.setConnectTimeout(10000); requestConfigBuilder.setSocketTimeout(30000); requestConfigBuilder.setConnectionRequestTimeout(10000); return requestConfigBuilder; } }); builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { @Override public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { return httpClientBuilder.setDefaultIOReactorConfig( IOReactorConfig.custom() .setIoThreadCount(100)//线程数配置 .setConnectTimeout(10000) .setSoTimeout(10000) .build()); } }); //设置超时 builder.setMaxRetryTimeoutMillis(10000); //构建low level client lowLevelRestClient = builder.build(); //构建high level client highLevelRestClient = new RestHighLevelClient(lowLevelRestClient); log.info("ESRestClient 初始化完成。。。。。。。。。"); }
经过init方法构造RestClient 、RestHighLevelClientide
那么如何操做index
单条处理
/** * @Description: low level put index * @throws IOException * @return void * @throws * @author JornTang * @date 2017年12月22日 */ public static void lowLevelPutIndex(String idnex, String type, ArchiveMapperVo mapperVo) throws IOException{ Map<String, String> params = Collections.emptyMap(); ArchiveMapper mapper = new ArchiveMapper(); BeanUtils.copyProperties(mapperVo, mapper); HttpEntity entity = new NStringEntity(JSON.toJSONString(mapper), ContentType.APPLICATION_JSON); Response response = lowLevelRestClient.performRequest("PUT", "/"+idnex+"/"+type+"/" + mapperVo.getDocumentId(), params, entity); StatusLine statusLine = response.getStatusLine(); log.info("索引执行put:【" + statusLine.getStatusCode() + "】" + statusLine.toString()); } /** * @Description: low level delete index * @throws IOException * @return void * @throws * @author JornTang * @date 2017年12月22日 */ public static void lowLevelDeleteIndex(String idnex, String type, String documentId) throws IOException{ Map<String, String> params = Collections.emptyMap(); Response response = lowLevelRestClient.performRequest("DELETE", "/"+idnex+"/"+type+"/" + documentId, params); StatusLine statusLine = response.getStatusLine(); log.info("索引执行delete:【" + statusLine.getStatusCode() + "】" + statusLine.toString()); }
批量处理
/**
*/
public static void bulkPutIndex(String idnex, String type, List<ArchiveMapper> mappers) throws Exception{
BulkRequest request = new BulkRequest();
if(mappers== null || mappers.size()< 1){
throw new ESIndexException("mappers can not be empty");
}
for (int i = 0; i < mappers.size(); i++) {
ArchiveMapper mapper = mappers.get(i);
request.add(new IndexRequest(idnex, type, mapper.getId())
.opType("create")
.source(JSON.toJSONString(mapper),XContentType.JSON));
}
BulkResponse bulkResponse = highLevelRestClient.bulk(request);
RestStatus stat = doSuccessful(bulkResponse);
//log.info("索引执行bulk put:【" + stat.getStatus() + "】" + stat.toString());
}
是否是以为很是简单呢?上面的代码不能直接使用要结合本身的实际状况。
好比:数据建模、model定义
数据建模参考:
PUT archives { "mappings": { "archive": { "_all": { "enabled": false }, "properties": { "id": { "type": "keyword" }, "arch_type": { "type": "integer" }, "archive_id":{ "type":"integer" }, "room_id":{ "type":"integer" }, "if_store":{ "type":"integer" }, "handover_state":{ "type":"integer" }, "crt_date":{ "type":"long" }, "if_hasfile":{ "type":"integer" }, “doc_date”:{ "type":"long" }, "doc_year":{ "type":"integer" }, "fond_code":{ "type":"keyword" }, "cate_code":{ "type":"keyword" }, "keep_time":{ "type":"integer" }, "file_click":{ "type":"integer" }, "storage_date":{ "type":"long" }, "storage_year":{ "type":"integer" }, "into_archive_date":{ "type":"long" }, "into_archive_year":{ "type":"integer" }, "vol_in_num":{ "type":"integer" }, "pro_vol":{ "type":"integer" }, "pro_doc":{ "type":"integer" }, "file_num":{ "type":"integer" }, "file_size":{ "type":"long" }, "page_nmbr":{ "type":"integer" } } } }, "settings": { "number_of_shards":3, "number_of_replicas":0 } }
属性类型定义参考:
经常使用的数据类型
Ø Boolean 布尔类型
布尔字段接受JSON true和false值,但也能够接受被解释为true或false的字符串
示例以下:
PUT my_index { "mappings": { "my_type": { "properties": { "is_published": { "type": "boolean" } } } } } POST my_index/my_type/1 { "is_published": "true" } GET my_index/_search { "query": { "term": { "is_published": true } } }
如下参数被boolean字段接受:
boost
映射字段级查询时间提高。接受一个浮点数,默认为1.0。
doc_values
该字段是否应该以列步方式存储在磁盘上,以便稍后用于排序,聚合或脚本?接受true (默认)或false。
index
该领域应该搜索?接受true(默认)和false。
null_value
接受上面列出的任何真值或假值。该值被替换为任何显式null值。默认为null,这意味着该字段被视为丢失。
store
字段值是否应该与_source字段分开存储和检索。接受true或false (默认)。
Ø Date 日期类型
JSON没有日期数据类型,因此Elasticsearch中的日期能够是:
· 包含格式的日期,如字符串"2015-01-01"或"2015/01/01 12:10:30"。
· 一个表明毫秒数的长数字。
· 自始至终秒的整数
在内部,日期转换为UTC(若是指定时区)并存储为表示毫秒之后的长数字。
日期格式能够自定义,但若是没有format指定,则使用默认值:
"strict_date_optional_time || epoch_millis"
示例以下:
PUT my_index { "mappings": { "my_type": { "properties": { "date": { "type": "date" } } } } } PUT my_index/my_type/1 { "date": "2015-01-01" } PUT my_index/my_type/2 { "date": "2015-01-01T12:10:30Z" } PUT my_index/my_type/3 { "date": 1420070400001 } GET my_index/_search { "sort": { "date": "asc"} }
定义多个日期格式
多个格式能够经过分隔||做为分隔符来指定。每一个格式将依次尝试,直到找到匹配的格式。第一种格式将被用于将毫秒自时代的值转换回字符串
PUT my_index { "mappings":{ "my_type":{ "properties":{ "date":{ "type":"date", "format":"yyyy-MM-dd HH:mm:ss || yyyy- MM-dd || epoch_millis" } } } } }
如下参数被date字段接受:
boost
映射字段级查询时间提高。接受一个浮点数,默认为1.0。
doc_values
该字段是否应该以列步方式存储在磁盘上,以便稍后用于排序,聚合或脚本?接受true (默认)或false。
format
能够解析的日期格式。默认为 strict_date_optional_time||epoch_millis。
locale
自从月份以来,解析日期时使用的语言环境在全部语言中都没有相同的名称和/或缩写。默认是 ROOT语言环境,
ignore_malformed
若是true,格式不正确的号码被忽略。若是false(默认),格式不正确的数字会抛出异常并拒绝整个文档。
index
该领域应该搜索?接受true(默认)和false。
null_value
接受其中一个配置的日期值format做为代替任何显式null值的字段。默认为null,这意味着该字段被视为丢失。
store
字段值是否应该与_source字段分开存储和检索。接受true或false (默认)。
Ø Keyword 关键字类型
用于索引结构化内容的字段,如电子邮件地址,主机名,状态码,邮政编码或标签。
它们一般用于过滤(找到个人全部博客文章,其中 status为published),排序,和聚合。关键字字段只能按其确切值进行搜索。
若是您须要索引全文内容(如电子邮件正文或产品说明),则可能须要使用text字段。
示例以下:
PUT my_index { "mappings": { "my_type": { "properties": { "tags": { "type": "keyword" } } } } }
如下参数被keyword字段接受:
boost
映射字段级查询时间提高。接受一个浮点数,默认为1.0。
doc_values
该字段是否应该以列步方式存储在磁盘上,以便稍后用于排序,聚合或脚本?接受true (默认)或false。
eager_global_ordinals
全球序言是否应该在刷新的时候急切地加载?接受true或false (默认)。对于常常用于术语聚合的字段,启用此功能是一个不错的主意。
fields
多字段容许为了避免同的目的以多种方式对相同的字符串值进行索引,例如用于搜索的字段和用于排序和聚合的多字段。
ignore_above
不要索引任何比这个值长的字符串。默认为 2147483647全部值都将被接受。
index
该领域应该搜索?接受true(默认)或false。
index_options
为了评分目的,应该在索引中存储哪些信息。默认为,docs但也能够设置为freqs在计算分数时考虑术语频率。
norms
评分查询时是否应考虑字段长度。接受true或false(默认)。
null_value
接受一个替代任何显式null 值的字符串值。默认为null,这意味着该字段被视为丢失。
store
字段值是否应该与_source字段分开存储和检索。接受true或false (默认)。
similarity
应使用 哪一种评分算法或类似性。默认为BM25。
normalizer
如何在索引以前预先处理关键字。默认为null,意味着关键字保持原样。
Ø Number 数字类型
如下数字类型受支持:
long
一个带符号的64位整数,其最小值为-263,最大值为。 263-1
integer
一个带符号的32位整数,其最小值为-231,最大值为。 231-1
short
一个带符号的16位整数,其最小值为-32,768,最大值为32,767。
byte
一个有符号的8位整数,其最小值为-128,最大值为127。
double
双精度64位IEEE 754浮点。
float
单精度32位IEEE 754浮点。
half_float
一个半精度的16位IEEE 754浮点。
scaled_float
一个由long一个固定比例因子支持的浮点。
示例以下:
PUT my_index { "mappings": { "my_type": { "properties": { "number_of_bytes": { "type": "integer" }, "time_in_seconds": { "type": "float" }, "price": { "type": "scaled_float", "scaling_factor": 100 } } } } }
对于double,float和half_float类型须要考虑-0.0和 +0.0不一样的值存储。作一个term查询 -0.0将不匹配+0.0,反之亦然。一样适用于范围查询真:若是上限是-0.0那么+0.0将不匹配,若是下界+0.0那么-0.0将不匹配
那么咱们应该如何使用数字类型。应遵循如下规则
l 至于整数类型(byte,short,integer和long)而言,你应该选择这是足以让你的用例最小的类型。这将有助于索引和搜索更高效。但请注意,考虑到根据存储的实际值对存储进行优化,选择一种类型将不会影响存储要求
l 对于浮点类型,使用缩放因子将浮点数据存储到整数中一般会更高效,这正是scaled_float 类型所作的。例如,一个price字段能够存储在一个 scaled_floatwith scaling_factor中100。全部的API均可以工做,就好像字段被存储为double同样,可是在elasticsearch下面,将会使用分数price*100,这是一个整数。这对于节省磁盘空间很是有帮助,由于整数比浮点更容易压缩。scaled_float交易磁盘空间的准确性也很好用。例如,想象一下,您正在跟踪CPU利用率,并将其做为介于0和之间的数字1。它一般没有多大的CPU使用率是12.7%或13%,因此你可使用一个scaled_float 带有scaling_factor的100,以节省空间,以全面cpu利用率到最近的百分比
若是scaled_float是不适合,那么你应该选择一个适合的浮点类型中的用例是足够最小的类型:double,float和half_float。这里是一个比较这些类型的表格,以帮助作出决定
类型
最小值
最大值
重要的位/数字
double
2-1074
(2-2-52)·21023
53 / 15.95
float
2-149
(2-2-23)·2127
24 / 7.22
half_float
2-24
65504
11 / 3.31
如下参数被数字类型接受:
coerce
尝试将字符串转换为数字并截断整数的分数。接受true(默认)和false。
boost
映射字段级查询时间提高。接受一个浮点数,默认为1.0。
doc_values
该字段是否应该以列步方式存储在磁盘上,以便稍后用于排序,聚合或脚本?接受true (默认)或false。
ignore_malformed
若是true,格式不正确的号码被忽略。若是false(默认),格式不正确的数字会抛出异常并拒绝整个文档。
index
该领域应该搜索?接受true(默认)和false。
null_value
接受与type替换任何显式null值的字段相同的数字值。默认为null,这意味着该字段被视为丢失。
store
字段值是否应该与_source字段分开存储和检索。接受true或false (默认)。
注:参数scaled_float
scaled_float 接受一个额外的参数:
scaling_factor
编码值时使用的缩放因子。在索引时间,数值将乘以该因子,并四舍五入到最接近的长整数值。例如,scaled_float用scaling_factor的10将内部存储2.34的23全部搜索时操做(查询,汇总,排序)的行为就好像该文件是一个值2.3。scaling_factor提升精确度的值较高,但也增长了空间要求。该参数是必需的。
Ø Text 文本数据类型
用于索引全文值的字段,例如电子邮件的正文或产品的说明。这些字段是analyzed,即它们经过 分析器来转换字符串成索引以前的单个条目列表。分析过程容许Elasticsearch 在 每一个全文字段中搜索单个单词。文本字段不用于排序,也不多用于聚合(尽管 重要的文本聚合 是一个明显的例外)。
若是您须要为电子邮件地址,主机名,状态码或标签等结构化内容编制索引,则可能须要使用keyword字段。
示例以下:
PUT my_index { "mappings": { "my_type": { "properties": { "full_name": { "type": "text" } } } } }
有时同一字段的全文本(text)和关键字(keyword)版本都是有用的:一个用于全文搜索,另外一个用于聚合和排序。这能够经过多领域来实现 。
如下参数被text字段接受:
analyzer
的分析器,其应该被用于 analyzed既在索引时间和搜索时间(除非被重写字符串字段 search_analyzer)。默认为默认的索引分析器或 standard分析器。
boost
映射字段级查询时间提高。接受一个浮点数,默认为1.0。
eager_global_ordinals
全球序言是否应该在刷新的时候急切地加载?接受true或false (默认)。在常常用于(重要)术语聚合的字段上启用此功能是一个好主意。
fielddata
该字段可使用内存中的字段数据进行排序,聚合或脚本吗?接受true或false(默认)。
fielddata_frequency_filter
专家设置容许决定fielddata 启用时在内存中加载哪些值。默认状况下全部的值都被加载。
fields
多字段容许为了避免同的目的以多种方式对相同的字符串值进行索引,例如一个字段用于搜索,一个多字段用于排序和聚合,或者由不一样的分析器分析相同的字符串值。
index
该领域应该搜索?接受true(默认)或false。
index_options
索引中应该存储哪些信息,用于搜索和突出显示目的。默认为positions。
norms
评分查询时是否应考虑字段长度。接受true(默认)或false。
position_increment_gap
应该插入到一个字符串数组的每一个元素之间的假词位置的数量。默认为position_increment_gap 默认分析仪上的配置100。100被选中是由于它能够经过字段值匹配术语来防止具备至关大的短语(小于100)的短语查询。
store
字段值是否应该与_source字段分开存储和检索。接受true或false (默认)。
search_analyzer
本analyzer应该在搜索时使用 analyzed的字段。默认为analyzer设置。
search_quote_analyzer
本analyzer应在搜索时遇到一个短语时使用。默认为search_analyzer设置。
similarity
应使用 哪一种评分算法或类似性。默认为BM25。
term_vector
术语向量是否应该存储在一个analyzed 字段中。默认为no。
Ø Feilds 多领域参数使用
为了避免同的目的,以不一样的方式对相同的字段进行索引一般是有用的。这是多领域的目的。例如,一个string 字段能够映射为text全文搜索的字段,也能够映射keyword为排序或聚合的字段:
示例以下:
PUT my_index { "mappings": { "my_type": { "properties": { "city": { "type": "text", "fields": { "raw": { "type": "keyword" } } } } } } }
PUT my_index/my_type/1 { "city": "New York" } PUT my_index/my_type/2 { "city": "York" } GET my_index/_search { "query": { "match": { "city": "york" } }, "sort": { "city.raw": "asc" }, "aggs": { "Cities": { "terms": { "field": "city.raw" } } } }
该city.raw字段是该字段的一个keyword版本city。
该city字段可用于全文搜索。
该city.raw字段可用于排序和聚合
使用多个分析器进行多字段编辑
另外一个多字段的用例是以不一样的方式分析相同的字段以得到更好的相关性。例如,咱们能够用standard分析器对字段进行索引,该 分析器将文本分解成文字,再用english分析器 将文字分解为词根:
PUT my_index { "mappings": { "my_type": { "properties": { "text": { "type": "text", "fields": { "english": { "type": "text", "analyzer": "english" } } } } } } } PUT my_index/my_type/1 { "text": "quick brown fox" } PUT my_index/my_type/2 { "text": "quick brown foxes" } GET my_index/_search { "query": { "multi_match": { "query": "quick brown foxes", "fields": [ "text", "text.english" ], "type": "most_fields" } } }
该text字段使用的standard分析仪。
该text.english字段使用的english分析仪。
索引两个文件,一个与fox另外一个foxes。
查询text和text.english字段并结合分数。
该text字段包含fox第一个文档和foxes第二个文档中的术语。该text.english字段包含fox这两个文档,由于foxes被阻止fox。
查询字符串也由standard分析器分析该text 字段,并由english分析器分析text.english字段。词干字段容许查询foxes也匹配包含正义的文档fox。这使咱们能尽量多地匹配文档。经过查询未定text域,咱们提升了foxes准确匹配的文档的相关性得分。
Ø 元数据字段
_all 字段:一个把其它字段值((("metadata, document", "_all field")))((("_all field", sortas="all field")))看成一个大字符串来索引的特殊字段。query_string
查询子句(搜索?q=john
)在没有指定字段时默认使用_all
字段
禁用示例
PUT /my_index/_mapping/my_type
{
"my_type": {
"_all": { "enabled": false }
}
}
若是_all字段被禁用,则URI搜索请求与 query_string和simple_query_string查询将没法用它来查询,可是你能够设置一个指定字段。以下示例
PUT my_index
{
"mappings": {
"my_type": {
//禁用_all字段
"_all": {
"enabled": false
},
"properties": {
"content": {
"type": "text"
}
}
}
},
"settings": {
//设置query_string 查询指定的字段
"index.query.default_field": "content"
}
}
_source字段:该_source字段包含在索引时间传递的原始JSON文档正文。这个 _source字段自己没有索引(所以是不可搜索的),可是它被存储以便在执行获取请求(好比get或者search)的时候返回 。
_source段会在索引内产生存储开销。出于这个缘由,它能够被禁用。以下:
PUT my_index { "mappings":{ "my_type":{ " _ source":{ "enabled":false } } } }
若是_source字段被禁用,如下功能将不在被支持
· update,update_by_query和reindexAPI。
· 高亮显示
· 将索引从一个Elasticsearch索引从新索引到另外一个索引的能力,能够更改映射或分析,也能够将索引升级到新的主要版本。
· 经过查看索引时使用的原始文档来调试查询或聚合的能力。
· 在未来有可能自动修复索引损坏。
若是磁盘空间是一个问题,而是增长 压缩级别而不是禁用_source。
在Elasticsearch中,对文档的个别字段设置存储的作法一般不是最优的。整个文档已经被存储为_source
字段。使用_source
参数提取你须要的字段老是更好的。
这个时候你可能会不想保存全部字段信息,那么你讲能够经过如下方式指定你要存储的字段。以下
PUT my_index { "mappings": { "my_type": { "_source": { //包含,支持通配符定义 "includes": [ "*.count", "meta.*" ], //不包含,支持通配符定义 "excludes": [ "meta.description", "meta.other.*" ] } } } } PUT my_index/my_type/1 { "requests": { "count": 10, "foo": "bar" }, "meta": { "name": "Some metric", "description": "Some metric description", "other": { "foo": "one", "baz": "two" } } }
注:以上方式只是过滤存储的字段,但每一个字段依旧能够被搜索
写在最后,针对具体相关api请参考elasticsearch官网。
建议:在实际项目中请结合线程池能够在建立索引时大大的提高性能
【其余连接】档案管理系统
本篇文章由一文多发平台ArtiPub自动发布