ELK技術棧中的那些查詢語法
ES-基本概念
詞項: 所有文件中出現的不重複個體
分片: 底層工作單元,儲存全部資料中的一部分,能搜尋任意一個節點上的資源(文件會被儲存和索引到分片內)。分為主分片和副本分片,索引內的任意一個文件都歸屬於主分片,副本分片是主分片的拷貝,提供讀服務,副本分片和主分片不會被分到同一個節點
Document: 根物件,能被indexed的基本資訊單元,用JSON表示,ID唯一,不可修改,只能替換
文件元資料
_Index 索引。相似document的集合,標明文件存放的位置
_type 資料子分割槽。同一索引下的資料邏輯分割槽,一個索引可以有多個型別
_id 與 _index,_type一起唯一確定一個文件
_version 保證相互衝突的變更不會導致資料丟失。ES使用樂觀併發控制,預設不阻塞併發請求,如果請求版本號不對,返回錯誤狀態碼
Shards: 索引被切分儲存的片段
Replication: 索引shards的拷貝
_score:正浮點數,表示相關性,評分越高,相關性越高
倒排索引:文件中所有不重複詞的列表構成,其中每個詞有一個包含它的文件列表,只能搜尋到索引中存在的詞條
ES-命令列使用
叢集操作
查詢叢集的名字
⇒ curl -XGET 'http://localhost:9200'
查詢叢集的健康狀況
⇒ curl -XGET 'http://localhost:9200/_cluster/health?format=yaml'
status欄位說明:
1. green 一切正常
2. yellow replicas沒有分配[可能是隻有單個節點],叢集正常
3. red 某些資料取不到
format=yaml指定使用yaml格式輸出,方便檢視
索引操作
獲取叢集的所有索引
⇒ curl -XGET 'http://localhost:9200/_cat/indices'
索引的欄位
⇒ curl -XGET 'http://localhost:9200/mytest/_mapping?format=yaml'
結果
mytest:
mappings:
external:
properties:
addre:
type: "string"
name:
type: "string"
它類似於資料庫的schema,描述文件可能具有的欄位或屬性、每個欄位的資料型別。
欄位對於非string型別,一般只需要設定type。string域兩重要屬性 index analyzer
index
1. analyzed 全文索引這個域。首先分析字串,然後索引
2. not_analyzed 精確索引 ,不分析
3. no 此域不會被搜尋
analyzer
將文字分成四核倒排索引的獨立詞條,後將詞條統一化提高可搜尋性
動態對映: 文件中出現之前從未遇到過的欄位,動態確定資料型別,並自動把新的欄位新增到型別對映
新建索引
⇒ curl -XPUT 'localhost:9200/mytest'
刪除索引
⇒ curl -XDELETE 'localhost:9200/mytest?format=yaml'
資料查詢
插入單條資料
⇒ curl -XPUT 'localhost:9200/mytest/external/1?format=yaml' -d '
quote> { "name":"paxi"}'
查詢單條資料
⇒ curl -XGET 'localhost:9200/mytest/external/1?format=yaml'
刪除單條資料
curl -XDELETE 'localhost:9200/mytest/external/3?format=yaml'
儲存的文字分析
curl -XGET 'localhost:9200/_analyze?format=yaml' -d '
{"papa xixi write"}'
結果為
tokens:
- token: "papa"
start_offset: 3
end_offset: 7
type: "<ALPHANUM>"
position: 1
- token: "xixi"
start_offset: 8
end_offset: 12
type: "<ALPHANUM>"
position: 2
- token: "write"
start_offset: 13
end_offset: 18
type: "<ALPHANUM>"
position: 3
token 表示實際儲存的詞條,position表示詞條在原始文字中的位置。
可以看出完整的文字會被切割儲存成不同的詞條
不返回元資料
curl -XGET 'localhost:9200/mytest/_search?filter_path=hits.hits._source&format=yaml' -d '
{ "query":{"match":{"name":"papa xixi write"}}}'
低版本無法生效
只返回部分欄位
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"match":{"name":"papa xixi write"}},"_source":["name"]}'
低版本無效,可以用萬用字元
match查詢
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"match":{"name":"papa xixi write"}}}'
查詢匹配的結果如下
hits:
- _index: "mytest"
_type: "external"
_id: "11"
_score: 0.6532502
_source:
name: "papa xixi write"
- _index: "mytest"
_type: "external"
_id: "4"
_score: 0.22545706
_source:
name: "papa xixi"
- _index: "mytest"
_type: "external"
_id: "2"
_score: 0.12845722
_source:
name: "papa"
- _index: "mytest"
_type: "external"
_id: "10"
_score: 0.021688733
_source:
name: "xixi"
從查詢結果,它獲取到了所有包含 papa 、 xixi和write 的詞,相當於將原來的詞拆開,然後兩個單詞做了 OR 操作,如果要全部匹配,可以使用AND操作
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"match":{"name":{"query":"papa xixi write","operator":"and"}}}}'
---
hits:
total: 1
max_score: 0.6532502
hits:
- _index: "mytest"
_type: "external"
_id: "11"
_score: 0.6532502
_source:
name: "papa xixi write"
如果只是想提高精度
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"match":{"name":{"query":"papa xixi write","minimum_should_match":"75%"}}}}'
---
hits:
total: 2
max_score: 0.6532502
hits:
- _index: "mytest"
_type: "external"
_id: "11"
_score: 0.6532502
_source:
name: "papa xixi write"
- _index: "mytest"
_type: "external"
_id: "4"
_score: 0.22545706
_source:
name: "papa xixi"
term查詢
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"term":{"name":"papa xixi write"}}}'
它的結果是什麼也沒有查到
total: 0
max_score: null
hits: []
換用查詢語句
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"term":{"name":"papa"}}}'
結果為
hits:
- _index: "mytest"
_type: "external"
_id: "2"
_score: 1.0
_source:
name: "papa"
- _index: "mytest"
_type: "external"
_id: "4"
_score: 0.37158427
_source:
name: "papa xixi"
- _index: "mytest"
_type: "external"
_id: "11"
_score: 0.2972674
_source:
name: "papa xixi write"
match 和 term的區別
match 如果在全文欄位上查詢,會使用正確的分析器分析查詢字串;如果精確值欄位使用,會精確匹配。 term精確匹配,只要包含了對應的文字就可以,不對文字分析(not_analyzed文字會精確匹配,terms 多個值只要有一個匹配就匹配);
從”papa xixi write”的儲存文字分析來看,它本身會被切割成不同的詞條,所以用 term查詢”papa xixi write”,無法獲取到結果,但是match確能夠匹配
filter使用
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"filtered":{"filter":{"range":{"name":{"gt":"w"}}}}}}'
或者
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"constant_score":{"filter":{"range":{"name":{"gt":"w"}}}}}}'
驗證語法是否正確
⇒ curl -XGET 'localhost:9200/_validate/query?explain&format=yaml' -d '{ "query":{{"filter":{"range":{"name":{"gt":"w"}}}}}'
---
valid: false
//原因省略
bool使用
使用term查詢
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ "query":{"term":{"addre":"beijing"}}}'
結果為
hits:
- _index: "mytest"
_type: "external"
_id: "5"
_score: 0.30685282
_source:
addre: "beijing"
- _index: "mytest"
_type: "external"
_id: "6"
_score: 0.30685282
_source:
addre: "beijing"
name: "px"
轉換為bool查詢,結果一樣
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ query:{bool:{must:{match:{addre:"beijing"}}}}}'
如果只想要最後一條
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ query:{bool:{must:{match:{addre:"beijing"}},must:{match:{name:"px"}}}}}'
想要第一條
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ query:{bool:{must:{match:{addre:"beijing"}},must_not:{match:{name:"px"}}}}}'
都想要
curl -XGET 'localhost:9200/mytest/_search?format=yaml' -d '
{ query:{bool:{must:{match:{addre:"beijing"}},should:{match:{name:"px"}}}}}'
must的意思是當前值必須是有的,must_not必須沒有,should表示資料可以有也可以沒有
ES-java查詢
AggregationBuilders.terms:一段時間內,某個欄位取值的數量排名前幾的聚合
/ ** @param startTime 開始的時間
* @param endTime 結束的時間
* @param termAggName term過濾
* @param fieldName 要做count的欄位
* @param top 返回的數量
*/
RangeQueryBuilder actionPeriod = QueryBuilders.rangeQuery("myTimeField").gte(startTime).lte(endTime).format("epoch_second");
TermsBuilder termsBuilder = AggregationBuilders.terms(termAggName).field(fieldName).size(top).order(Terms.Order.count(false));
return client.prepareSearch(INDICE).setQuery(actionPeriod).addAggregation(termsBuilder).setSize(0).execute().actionGet();
order(Terms.Order.count(false)):表示降序
size(top):top表示只要排序的數量
prepareSearch(INDICE):INDICE表示索引的名字
setSize(0):表示只要聚合結果
如果需要去掉某些特殊欄位取值
client為構建的ES客戶端
BoolQueryBuilder actionPeriodMustNot = QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("myTimeField").gte(startTime).lte(endTime).format("epoch_second")).mustNot(QueryBuilders.termQuery(field, value));
如果是單個欄位特定的多個值
//values是個List
BoolQueryBuilder actioPeriodMust = QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("myTimeField").gte(startTime).lte(endTime).format("epoch_second")).must(QueryBuilders.termsQuery(field, values));
使用結果
Terms clickCount= sr.getAggregations().get(termAggName);
for (Terms.Bucket term:clickCount.getBuckets()){
int key = term.getKeyAsNumber().intValue(); //要排序欄位的值
long docCount = term.getDocCount(); //數量
}
date_histogram: 一段時間之內,時間欄位按照時間間隔的聚合
BoolQueryBuilder actioPeriodMust = QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("myTimeField").gte(startTime).lte(endTime).format("epoch_second"));
DateHistogramBuilder actionInterval = AggregationBuilders.dateHistogram(dateNickName).field("myTimeField").timeZone("Asia/Shanghai");
if (timeInterval<MINUTE){
actionTimeInterval.interval(DateHistogramInterval.seconds(timeInterval)).format("HH:mm:ss");
}else if (timeInterval<HOUR){
actionTimeInterval.interval(DateHistogramInterval.minutes(timeInterval / MINUTE)).format("dd HH:mm");
}else if (timeInterval < DAY){
actionTimeInterval.interval(DateHistogramInterval.hours(timeInterval / HOUR)).format("HH:mm");
}else if (timeInterval < THIRTY_DAY){
actionTimeInterval.interval(DateHistogramInterval.days(timeInterval / DAY));
}else{
actionTimeInterval.interval(DateHistogramInterval.MONTH);
}
actionInterval.format("yyyy-MM-dd HH:mm:ssZ");
return client.prepareSearch(INDICE).setQuery(actioPeriodMust).addAggregation(actionInterval).setSize(0).execute().actionGet();
es本身預設設定的時間戳是 UTC形式,在國內要設定TimeZone(“Asia/Shanghai”);
java的SimpleDateFormate會預設獲取虛擬機器所在時區的時間戳,所以存時間的時候,最好存與時區無關的時間,再做本地化顯示
使用結果
Histogram histogram=sr.getAggregations().get(dateNickName);
for(Histogram.Bucket entry:histogram.getBuckets()){
String key = entry.getKeyAsString();//時間間隔
long count = entry.getDocCount();//數量
}
subAggregation:一段時間內,按照一定的時間間隔,每個間隔段內欄位每個取值的數量聚合
相當於合併上述兩個場景
BoolQueryBuilder query = QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("myTimeField").gte(startTime).lte(endTime).format("epoch_second"))
.must(QueryBuilders.termsQuery("action", orderValue));
DateHistogramBuilder actionTimeInterval = AggregationBuilders.dateHistogram(dateNickName).field("myTimeField").timeZone("Asia/Shanghai");
actionTimeInterval.subAggregation(AggregationBuilders.terms(termNickName).field("action").size(size));
return client.prepareSearch(INDICE).setQuery(query).addAggregation(actionTimeInterval).setSize(0).execute().actionGet();
使用結果
Histogram hitogram = sr.getAggregations().get(dateAggName);
for (Histogram.Bucket date : hitogram.getBuckets()) {
String intervalName = date.getKeyAsString();
long timeIntervalCount = date.getDocCount();
if (timeIntervalCount != 0) {
Terms terms = date.getAggregations().get(termAggName);
for (Terms.Bucket entry : terms.getBuckets()) {
int key= entry.getKeyAsNumber().intValue();
long childCount = entry.getDocCount();
}
}
}
分頁獲取資料
BoolQueryBuilder actionPeriodMust = QueryBuilders.boolQuery().must(QueryBuilders.termQuery(key, value)).must(QueryBuilders.rangeQuery("myTimeField").gte(startTime).lte(endTime).format("epoch_second"));
return client.prepareSearch(INDICE).setQuery(actionPeriodMust).addSort(SortBuilders.fieldSort("myTimeField").order(SortOrder.ASC)).setFrom(from).setSize(size).execute().actionGet();
使用
Iterator<SearchHit> iterator = sr.getHits().iterator();
while (iterator.hasNext()) {
SearchHit next = iterator.next();
JSONObject jo = JSONObject.parseObject(next.getSourceAsString());
}
AggregationBuilders.cardinality:獲取某個欄位的唯一取值數量
BoolQueryBuilder query = QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("myTimeField").gte(startTimeInSec*1000).lte(endTimeInSec*1000).format("epoch_millis"));
CardinalityBuilder fieldCardinality = AggregationBuilders.cardinality(cardinalityAggName).field(field);//field 要獲取的欄位
return client.prepareSearch(INDICE).setQuery(query).addAggregation(fieldCardinality).execute().actionGet();
使用結果
Cardinality cardinality = sr.getAggregations().get(cardinalityAggName);
long value = cardinality.getValue();
bool查詢
比如想要addr是beijing的,同時必須滿足條件:name是 paxi,或者,phoneNumber是 1234567890
BoolQueryBuilder searchIdQuery = QueryBuilders.boolQuery();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
while (kvs.hasNext()){
Map.Entry<String, String> fieldValue = kvs.next();
String field=fieldValue.getKey();
String value=fieldValue.getValue();
searchIdQuery.should(QueryBuilders.termQuery(field, value));
}
boolQueryBuilder.must(searchIdQuery);
boolQueryBuilder.must(QueryBuilders.termsQuery(key, values));
return client.prepareSearch(INDICE).setQuery(boolQueryBuilder).execute().actionGet();
LogStash
./bin/logstash -f conf/test.conf
啟動方式
[] 欄位引用,將欄位名放到裡面即可。 例如: 獲取精度 [geoip][location][0]
a => true 資料型別,前面代表資料的欄位名,後面是值,當前為bool
stdin 中 type用來標記事件型別,tags由具體外掛新增、刪除
start_position: logstash讀取檔案的初始位置,預設使用結束位置
grok表示式語法
完整語法: %{PATTERN_NAME:capture_name:data_type}
寫表示式如果沒有完全匹配那麼會匹配失敗
一樣的字串直接寫對應字元即可,希望解析的用%{}包裹
(?\w+) 表示匹配 單詞 一次或多次並將結果儲存在 param1裡面
pattern_dir 指定grok表示式儲存的位置,match直接引用
正則寫到想要匹配的位置即可
附錄
Kibana查詢
kibana框中的查詢可以使用LUCENE查詢語法或者是ES的查詢語句
Field
查詢指定的欄位否則使用預設欄位
比如 index包含兩個欄位 title , text ;text是預設欄位
title:”hello world” AND text:to 和 title:”hello world” AND to 等效
title: hello world 查詢的則是 title為hello的欄位 text為world的欄位
wildcard
te?t 匹配 text test ;表示任意一個字元
test* 匹配 test tests tester;表示0到多個字元
?和 * 不能用在第一個位置
Fuzzy
roam~ 匹配 foam和roams 基於 Levenshtein Distance,波浪線新增在末尾。從1.9版本開始可以追加數字代表相似度,越接近1相似度越高,比如 roam~0.8,預設是0.5
Proximity
“jakarta apache”~10 匹配從jakarta到apache中間隔10個單詞
Range
mode_date:[20020101 TO 20030101] 匹配時間在20020101到20030101之間,包括20020101和20030101
title:{Aida TO Carmen} 匹配Aida 到 Carmen之間,不包括Aida和Carmen
“[”表示包含 “{”表示不包含
AND(+) OR NOT(-)
關鍵字要大寫
(jakarta OR apache) AND website 組合查詢 包含 website 和 jakarta/apache
逃逸字元
\(1\+1\)\:2
使用ES查詢語法
將ES命令中的 -d 後面的引數加入即可;比如curl查詢為
curl -XGET 'localhost:9200/_search?format=yaml' -d '
{ "query":{"term":{"addre":"beijing"}}}'
命令列下輸入為:{ "query":{"term":{"addre":"beijing"}}}