如果你對ES不了解但是現在想知道他是干什么的,可以先查看我的其他幾篇檔案:
ElasticSearch究竟是個什么東西
通過官方檔案高效學習ElasticSearch的JavaAPI實作!
如何在自己的專案中引入ElasticSearch搜索引擎?
ElasticSearch聚合查詢Restful語法和JavaApi詳解(基于ES7.6)
(一)ES如何存盤物件
ElasticSearch中可以將資料以物件的方式存盤并查詢,但是ES底層的Lucene 沒有內部物件的概念,因此如果通過默認的方式往ES中插入物件,ES會將物件層次結構扁平化為欄位名稱和值的簡單串列,
比如下面這一段資料:
PUT my_index/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
ES內部會將這份資料變成下面這個樣子:
{
"group" : "fans",
"user.first" : [ "alice", "john" ],
"user.last" : [ "smith", "white" ]
}
缺失了first和last之間的關聯性,比如這個時候想查詢一個first為John,last為White的人,理論上是沒有這個人的,但是實際上名為fans的這個組還是被查出來了,
GET my_index/_search
{
"query": {
"bool": {
"must": [
{ "match": { "user.first": "John" }},
{ "match": { "user.last": "White" }}
]
}
}
}
從結果可以看到,兩條資料都被查詢出來了,

(二)Nested型別
這個時候就需要用到nested,nested型別是object資料型別的特殊版本,它允許物件陣列以一種可以相互獨立查詢的方式進行索引,
在Nested內部,每個物件索引其實是一個單獨的隱藏檔案,這意味著每個嵌套物件都可以獨立于其他物件進行查詢,
使用Nested需要先創建索引,依舊通過上邊的這個例子
DELETE my_index
PUT my_index
{
"mappings": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
PUT my_index/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith",
"age" : "23"
},
{
"first" : "Alice",
"last" : "White",
"age":"24"
}
]
}
首先創建my_index索引,設定user的型別為nested,接著在查詢時,需要通過es的nested查詢陳述句查詢,使用同樣的方式查詢first為John,last為White的用戶,這次的結果是不存在,因為通過nested存盤的物件是具有關聯性的,
GET my_index/_search
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "John" }},
{ "match": { "user.last": "White" }}
]
}
}
}
}
}
上邊的DSL陳述句用Java API實作如下:
@Test
public void testNested() throws Exception{
//自己封裝的一個獲取RestHighLevelClient的類
RestHighLevelClient client=ElasticSearchClient.getClient();
SearchRequest request = new SearchRequest("my_index");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("user.first","John"));
boolQueryBuilder.must(QueryBuilders.matchQuery("user.last","White"));
searchSourceBuilder.query(QueryBuilders.nestedQuery("user",boolQueryBuilder, ScoreMode.None));
request.source(searchSourceBuilder);
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
SearchHit[] hits = search.getHits().getHits();
for (int i = 0; i < hits.length; i++) {
SearchHit hit = hits[i];
System.out.println(hit.getSourceAsString());
}
}
(三)使用nested進行聚合查詢
除了使用nested進行普通查詢外,nested也支持聚合查詢,同樣是上面的例子,現在做一個對年齡聚合的操作:
GET my_index/_search
{
"aggs": {
"nestedAgg": {
"nested": {
"path": "user"
},
"aggs": {
"ageAgg": {
"terms": {
"field": "user.age.keyword",
"size": 10
}
}
}
}
}
}
(五)nested中的inner_hits
查詢nested物件時,只要查詢條件符合這個nested物件里的某一個條件,整個nested物件都會被檢索出來,比如上面這個例子中,我只想查詢叫做John Smith的這個人,但是通過普通的query查詢會把整條記錄都查詢出來,效果就是這樣:
"hits" : [
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.3862942,
"_source" : {
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith",
"age" : "23"
},
{
"first" : "Alice",
"last" : "White",
"age" : "24"
}
]
}
}
]
如果只想要nested中里的一個物件,就可以使用inner_hits,使用比較簡單,只需要在查詢陳述句之后加上inner_hits即可,
GET my_index/_search
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "John" }},
{ "match": { "user.last": "Smith" }}
]
}
},
"inner_hits": {}
}
}
}
查詢結果里就會多出來一塊資料,里面就只會展示具體的nested物件:
"inner_hits" : {
"user" : {
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "1",
"_nested" : {
"field" : "user",
"offset" : 0
},
"_score" : 1.3862942,
"_source" : {
"last" : "Smith",
"first" : "John",
"age" : "23"
}
}
]
}
}
}
(六)nested的使用建議
nested可以很好地存盤和查詢物件型別資料,但是也不能濫用nested,每個nested物件都被索引為一個單獨的檔案,簡單來講就是如果一個索引里包含 100 個user物件,那么在實際底層將創建 101 個 Lucene 檔案,是一個很大的消耗,
nested型別只應在特殊情況下使用,一個索引在創建的時候,nested型別的物件默認不能超過50個,可通過index.mapping.nested_fields.limit修改,
一個具體的檔案中,nested型別中包含的嵌套物件的數量默認不能超過10000個,也就是說上面創建的user在一個檔案里不能超過10000個,可通過index.mapping.nested_objects.limit修改,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/381944.html
標籤:其他
上一篇:數數小木塊
