最近项目有个需求,很棘手。有个统计功能,需要做到实时出结果。这个功能经过好几个人的处理都没有解决客户的问题。到了我这里,我就来填坑了。苦逼的程序猿,苦逼的命。谁叫我是这个项目现任负责人。经一番折腾大致了解到客户对这个统计分析的需求之后,就进入技术选项。老套路,肯定先百度、谷歌、大神群各种咨询、了解。最后决定使用elasticseaarch。至于为何选择它,相关同学自己百度去。而我选择的原因就是简单、易于维护、成本低、性能也是gan gan 的。
接下来我们看看elasticsearch index api是如何使用java 来进行相关操作。强烈建议在阅读以下所说的内容前必须先对elasticsearch有一定的了解
参考官网API
low level https://www.elastic.co/guide/en/elasticsearch/client/java-rest/5.6/java-rest-low.html
high level https://www.elastic.co/guide/en/elasticsearch/client/java-rest/5.6/java-rest-high.html
以下源码下载连接:ESRestClient.java http://download.csdn.net/download/u013294278/10174249
pom.xml
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>5.6.4</version>
</dependency>
创建连接
private static Logger log = LoggerFactory.getLogger(ESRestClient.class);
private static RestClient lowLevelRestClient = null;
private static RestHighLevelClient highLevelRestClient = null;
/**
* @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 、RestHighLevelClient
那么如何操作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());
}
批量处理
/**
- @Description: high level bulk put index ps:need jdk1.8
- @param idnex 索引
- @param type 类型
- @param mappers 索引数据集合
- @throws Exception
- @return void
- @throws
- @author JornTang
- @date 2017年12月22日
*/
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自动发布