触类旁通Elasticsearch:关联
目錄
一、文檔間關(guān)系概覽
1. 對(duì)象類型
2. 嵌套類型
3. 父子關(guān)系
4. 反規(guī)范化
二、將對(duì)象最為字段值
1. 映射和索引對(duì)象
2. 搜索對(duì)象
三、嵌套類型
1. 映射并索引嵌套文檔
2. 搜索和聚合嵌套文檔
四、父子關(guān)系
1. 子文檔的索引、更新和刪除
2. 在父文檔和子文檔中搜索
五、反規(guī)范化
1. 反規(guī)范化使用案例
2. 索引、更新和刪除反規(guī)范化的數(shù)據(jù)
3. 查詢反規(guī)范化的數(shù)據(jù)
《Elasticsearch In Action》學(xué)習(xí)筆記。
? ? ? ? ES本身不支持SQL數(shù)據(jù)庫(kù)的join操作,在ES中定義關(guān)系的方法有對(duì)象類型、嵌套文檔、父子關(guān)系和反規(guī)范化。
一、文檔間關(guān)系概覽
1. 對(duì)象類型
? ? ? ? 允許將一個(gè)對(duì)象作為文檔字段的值,主要用于處理一對(duì)一關(guān)系。如果用對(duì)象類型表示一對(duì)多關(guān)系,可能出現(xiàn)邏輯上的錯(cuò)誤。例如,使用對(duì)象類型(object type)表示一個(gè)小組多個(gè)活動(dòng)的關(guān)系:
{"name": "Denver technology group""events": [{"date": "2014-12-22","title": "Introduction to Elasticsearch"},{"date": "2014-06-20","title": "Introduction to Hadoop"}] }? ? ? ? 如果希望搜索一個(gè)關(guān)于Elasticsearch的活動(dòng)分組,可以在events.title字段里搜索。在系統(tǒng)內(nèi)部,文檔是像下面這樣進(jìn)行索引的:
{"name": "Denver technology group","events.date": ["2014-12-22", "2014-06-20"],"events.title": ["Introduction to Elasticsearch", "Introduction to Hadoop"] }? ? ? ? 假設(shè)想過(guò)濾2014年12月主辦過(guò)Hadoop會(huì)議的分組,查詢可以是這樣的:
"bool": {"must": [{"term": {"events.title": "Hadoop"}},{"range": {"events.date": {"from": "2014-12-01","to": "2014-12-31"}}}] }? ? ? ? 這將匹配例中的那個(gè)文檔,但顯然錯(cuò)誤的,Hadoop活動(dòng)是在6月而不是12月。造成這種錯(cuò)誤的原因是對(duì)象類型將所有數(shù)據(jù)都存儲(chǔ)在一篇文檔中,ES并不知道內(nèi)部文檔之間的邊界,如圖1所示。
圖1 在存儲(chǔ)的時(shí)候,內(nèi)部對(duì)象的邊界并未考慮在內(nèi),這導(dǎo)致了意外的搜索結(jié)果?
? ? ? ? 如果處理的是一對(duì)一關(guān)系,則不會(huì)出現(xiàn)這樣的邏輯錯(cuò)誤,而且對(duì)象類型是最快、最便捷的關(guān)系處理方法。ES的關(guān)系類型類似Oracle中的嵌套表。
2. 嵌套類型
? ? ? ? 要避免跨對(duì)象匹配的發(fā)生,可以使用嵌套類型(nested type),它將活動(dòng)索引到分隔的Lucene文檔。對(duì)象與嵌套的區(qū)別在于映射,這會(huì)促使ES將嵌套的內(nèi)部對(duì)象索引到鄰近的位置,但是保持獨(dú)立的Lucene文檔,如圖2所示。在搜索時(shí),需要使用nested過(guò)濾器和查詢,這些會(huì)在Lucene文檔中搜索。
圖2 嵌套類型使得ES將多個(gè)對(duì)象索引到多個(gè)分隔的Lucene文檔?
? ? ? ? 在某些用例中,像對(duì)象和嵌套類型那樣,將所有數(shù)據(jù)存儲(chǔ)在同一個(gè)ES文檔中不見得是明智之舉。拿分組和活動(dòng)的例子來(lái)說(shuō):如果一個(gè)分組所有數(shù)據(jù)都放在同一篇文檔中,那么在創(chuàng)建一項(xiàng)新的活動(dòng)時(shí),不得不為這個(gè)活動(dòng)重新索引整篇文檔。這可能會(huì)降低性能和并發(fā)性,取決于文檔有多大,以及操作的頻繁程度。
3. 父子關(guān)系
? ? ? ? 通過(guò)父子關(guān)系,可以使用完全不同的ES文檔,并在映射中定義文檔間的關(guān)系。在索引一個(gè)子文檔時(shí),可以將它指向其父文檔,如圖3所示。在搜索時(shí),可以使用has_parent和has_child查詢和過(guò)濾器處理父子關(guān)系。
圖3 不同ES文檔可以有父子關(guān)系?
4. 反規(guī)范化
? ? ? ? 對(duì)象、嵌套和父子關(guān)系可以用于處理一對(duì)一或一對(duì)多關(guān)系,而反規(guī)范化用于處理多對(duì)多關(guān)系。反規(guī)范化(denormalizing)意味著一篇文檔將包含所有相關(guān)的數(shù)據(jù),即使是同樣的數(shù)據(jù)在其它文檔中有復(fù)本。
? ? ? ? 以分組和會(huì)員為例,一個(gè)分組可以擁有多個(gè)會(huì)員,一個(gè)用戶也可以成為多個(gè)分組的會(huì)員。分組和會(huì)員都有它們自己的一組屬性。為了表示這種關(guān)系,可以讓分組成為會(huì)員的父輩。對(duì)于身為多個(gè)分組會(huì)員的用戶而言,可以反規(guī)范化他們的數(shù)據(jù):每次表示一個(gè)其所屬的分組,如圖4所示。反規(guī)范化實(shí)際上是一種典型的以空間(數(shù)據(jù)冗余)換時(shí)間的處理方式。
圖4 反規(guī)范化技術(shù)將數(shù)據(jù)進(jìn)行復(fù)制,避免了高成本的關(guān)系處理?
二、將對(duì)象最為字段值
? ? ? ? 通過(guò)對(duì)象,ES在內(nèi)部將層級(jí)結(jié)構(gòu)進(jìn)行了扁平化,使用每個(gè)內(nèi)部字段的全路徑,將其放入Lucene內(nèi)的獨(dú)立字段。整個(gè)流程如圖5所示。
圖5 JSON層次結(jié)構(gòu),在Lucene中被存儲(chǔ)為扁平結(jié)構(gòu)?
1. 映射和索引對(duì)象
? ? ? ? 默認(rèn)情況下,內(nèi)部對(duì)象的映射是自動(dòng)識(shí)別的。
# 自動(dòng)創(chuàng)建索引 curl -XPOST '172.16.1.127:9200/event-object/_doc/1?pretty' -H 'Content-Type: application/json' -d' {"title": "Introduction to objects","location":?{"name": "Elasticsearch in Action book","address": "chapter 8"} }'# 查看索引映射 curl '172.16.1.127:9200/event-object/_mapping?pretty'? ? ? ? 結(jié)果返回:
{"event-object" : {"mappings" : {"_doc" : {"properties" : {"location" : {"properties" : { ? ? ? ? ? ? ? ? ? ?# 內(nèi)部對(duì)象及其屬性的映射是自動(dòng)識(shí)別的"address" : {"type" : "text","fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}},"name" : {"type" : "text","fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}}}},"title" : {"type" : "text","fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}}}}}} }? ? ? ? 如果有多個(gè)這樣的對(duì)象所構(gòu)成的數(shù)組,單個(gè)內(nèi)部對(duì)象的映射同樣奏效。例如,如果索引了下面的文檔,映射將會(huì)保持不變。
curl -XPOST '172.16.1.127:9200/event-object/_doc/2?pretty' -H 'Content-Type: application/json' -d' {"title": "Introduction to objects","location": [{"name": "Elasticsearch in Action book","address": "chapter 8"},{"name": "Elasticsearch Guide","address": "elasticsearch/reference/current/mapping-object-type.html"} ] }'2. 搜索對(duì)象
? ? ? ? 默認(rèn)情況下,需要設(shè)置所查找的字段路徑,來(lái)引用內(nèi)部對(duì)象。下面的代碼指定location_event.name的全路徑將其作為搜索的字段,從而搜索在辦公室舉辦的活動(dòng)。
EVENT_PATH="172.16.1.127:9200/get-together/" curl "$EVENT_PATH/_search?q=location_event.name:office&pretty"? ? ? ? 下面的terms聚合返回了location.name字段中最為常用的單詞。
curl "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d' {"aggs": {"location_cloud": {"terms": {"field": "location.name"}}} }'? ? ? ? 再次強(qiáng)調(diào),對(duì)象擅于處理一對(duì)一關(guān)系,而對(duì)于一對(duì)多關(guān)系的查詢,可能出現(xiàn)邏輯錯(cuò)誤。
三、嵌套類型
1. 映射并索引嵌套文檔
? ? ? ? 嵌套映射和對(duì)象映射看上去差不多,不過(guò)期type不是object,而必須是nested。
# 定義索引映射 curl -XPUT "172.16.1.127:9200/group-nested?pretty" -H 'Content-Type: application/json' -d' {"mappings": {"_doc": {"properties": {"name": {"type": "text"},"members": {"type": "nested", ? ? ? ? ? ? ? ? ? ? # 這里告訴ES將會(huì)員對(duì)象索引到同一個(gè)分塊中的不同文檔中"properties": {"first_name": {"type": "text"},"last_name": {"type": "text"}}}}}} }'# 增加一篇文檔 curl -XPUT "172.16.1.127:9200/group-nested/_doc/1?pretty" -H 'Content-Type: application/json' -d' {"name": "Elasticsearch News", ? ? ? ? ? ? ? ? # 這個(gè)屬性將存入主文檔"members": [{"first_name": "Lee", ? ? ? ? ? ? ? ? ? ? ?# 這些對(duì)象存入自己的文檔中,共同組成根文檔中的一個(gè)分塊"last_name": "Hinman"},{"first_name": "Radu","last_name": "Gheorghe"}] }'? ? ? ? 與對(duì)象不同,嵌套查詢和過(guò)濾器可以在文檔的邊界之內(nèi)搜索。例如,可以搜索名為“Lee”且姓為“Hinman”的分組會(huì)員。缺省時(shí),嵌套的查詢不會(huì)進(jìn)行跨多個(gè)對(duì)象的匹配,因此避免了名為“Lee”而姓為“Gheorghe”這樣的意外匹配。
2. 搜索和聚合嵌套文檔
? ? ? ? 使用nested在嵌套文檔上運(yùn)行搜索和聚合,使ES連接在同一個(gè)分塊中的多個(gè)Lucene文檔,并將連接后的結(jié)果數(shù)據(jù)看作普通的ES文檔。
(1)Nested查詢和過(guò)濾器
? ? ? ? 運(yùn)行nested查詢或過(guò)濾器時(shí),需要指定path參數(shù),告訴ES這些嵌套對(duì)象位于哪里的Lucene分塊中。除夕之外,nested查詢或者過(guò)濾器將會(huì)分別封裝一個(gè)常規(guī)的查詢或過(guò)濾器。下面的代碼搜索名為“Lee”、姓為“Gheorghe”的會(huì)員。查詢不會(huì)返回匹配的文檔,因?yàn)闆]有會(huì)員的名字是Lee Gheorghe。
(2)在多個(gè)嵌套層級(jí)上搜索
? ? ? ? ES支持多級(jí)嵌套。下面的代碼創(chuàng)建兩級(jí)嵌套的索引:會(huì)員(members)和他們的評(píng)論(comments)。
? ? ? ? 添加一個(gè)嵌套文檔:
curl -XPUT "172.16.1.127:9200/group-multinested/_doc/1?pretty" -H 'Content-Type: application/json' -d' {"name": "Elasticsearch News","members": {"first_name": "Radu","last_name": "Gheorghe","comments": { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # 多個(gè)會(huì)員對(duì)象嵌套于分組中,而多個(gè)評(píng)論對(duì)象又嵌套在會(huì)員對(duì)象中"date": "2013-12-22","comment": "hello world"}} }'? ? ? ? 為了在內(nèi)嵌的評(píng)論文檔中搜索,需要指定members.comments的路徑:
curl '172.16.1.127:9200/group-multinested/_search?pretty' -H 'Content-Type: application/json' -d' {"query": {"nested": {"path": "members.comments", ? ? ? ? ? ? ? # 查找位于members之中的comments字段"query": {"term": {"members.comments.comment": "hello" ? # 查詢?nèi)匀惶峁┝俗侄蔚娜柯窂接糜诓檎襺}}} }'(3)整合嵌套對(duì)象的得分
? ? ? ? 一個(gè)nested查詢會(huì)計(jì)算得分。例如,根據(jù)查詢條件的匹配程度,每個(gè)內(nèi)部會(huì)員文檔會(huì)得到自己的得分。但是來(lái)自應(yīng)用的查詢是為了查找分組文檔,所以ES需要為整個(gè)分組文檔給出一個(gè)得分。在這點(diǎn)上一共有4中選項(xiàng),通過(guò)score_mode設(shè)置。
- avg:這是默認(rèn)選項(xiàng),系統(tǒng)獲取所有匹配的內(nèi)部文檔之分?jǐn)?shù),并返回其平均分。
- total:系統(tǒng)獲取所有匹配的內(nèi)部文檔之分?jǐn)?shù),將其求和并返回。
- max:返回匹配的內(nèi)部文檔之最大得分。
- none:考慮總文檔得分的計(jì)算時(shí),不保留、不統(tǒng)計(jì)嵌套文檔的得分。
(4)獲知哪些內(nèi)部文檔匹配上了
? ? ? ? 可以在嵌套查詢或過(guò)濾器中添加一個(gè)inner_hits對(duì)象,來(lái)展示匹配上的嵌套文檔。
curl '172.16.1.127:9200/group-nested/_search?pretty' -H 'Content-Type: application/json' -d' {"query": {"nested": {"path": "members","query": {"term": {"members.first_name": "lee"}},"inner_hits": {"from": 0,"size": 1}}} }'? ? ? ? 結(jié)果返回:
..."inner_hits" : {"members" : {"hits" : {"total" : 1,"max_score" : 0.6931472,"hits" : [{"_index" : "group-nested","_type" : "_doc","_id" : "1","_nested" : {"field" : "members","offset" : 0},"_score" : 0.6931472,"_source" : {"first_name" : "Lee","last_name" : "Hinman"}}]}}}? ? ? ? 要識(shí)別子文檔,可以查看_nested對(duì)象。其中field字段是嵌套對(duì)象的路徑,而offset顯示了嵌套文檔在數(shù)組中的位置。上例中,Lee是查詢結(jié)果中的第一個(gè)member。
(5)嵌套和逆向嵌套聚合
? ? ? ? 為了在嵌套類型的對(duì)象上進(jìn)行聚合,需要使用nested聚合。這是一個(gè)單桶聚合,在其中可以指定包含所需字段的嵌套對(duì)象之路徑。如圖6所示,nested聚合促使ES進(jìn)行了必要的連接,以確保其它聚合在指定的路徑上能正常運(yùn)行。
?
? ? ? ? 例如,為了獲得參與分組最多的活躍用戶,通常會(huì)在會(huì)員名字字段上運(yùn)行一個(gè)terms聚合。如果這個(gè)name字段存儲(chǔ)在嵌套類型的members對(duì)象中,那么需要將terms聚合封裝在nested聚合中,并將聚合的路徑path設(shè)置為會(huì)員members:
curl '172.16.1.127:9200/get-together/_search?pretty' -H 'Content-Type: application/json' -d' {"aggs": {"members": {"nested": {"path": "members"},"aggs": {"frequent_members": {"terms": {"field": "members.name"}}}}} }'? ? ? ? 有些情況下,需要反向訪問(wèn)父輩或者根文檔。例如,希望針對(duì)活躍會(huì)員,展示他們參加最多的分組之tags。為了實(shí)現(xiàn)這一點(diǎn),使用reverse_nested聚合,它會(huì)告訴ES在嵌套層級(jí)中向上返回查找:
curl -X PUT "172.16.1.127:9200/get-together/_mapping/_doc?pretty" -H 'Content-Type: application/json' -d' {"properties": {"tags": {?"type": ? ? "text","fielddata": true}} }'curl '172.16.1.127:9200/get-together/_search?pretty' -H 'Content-Type: application/json' -d' {"aggs": {"members": {"nested": {"path": "members"},"aggs": {"frequent_members": {"terms": {"field": "member.name"},"aggs": {"back_to_group": {"reverse_nested": {},"aggs": {"tags_per_member": {"terms": {"field": "tags"}}}}}}}}} }'? ? ? ? Nested和reverse_nested聚合可以快速告訴ES,在哪些Lucene文檔中查找下一項(xiàng)聚合的字段。
四、父子關(guān)系
? ? ? ? 在嵌套的文檔中,實(shí)際情況是所有內(nèi)部的對(duì)象集中在同一個(gè)分塊中的Lucene文檔,這對(duì)于對(duì)象便捷地連接根文檔而言,是非常有好處的。父子文檔則是完全不同的ES文檔,所以只能分別搜索它們,效率更低。
? ? ? ? 對(duì)于文檔的索引、更新和刪除而言,父子的方式就顯得出類拔萃了。這是因?yàn)楦篙吅妥虞呂臋n都是獨(dú)立的ES文檔,各自管理。舉例來(lái)說(shuō),如果一個(gè)分組有很多活動(dòng),要增加一個(gè)新活動(dòng),那么就是增加一篇新的活動(dòng)文檔。如果使用嵌套類型的方式,ES不得不重新索引分組文檔,來(lái)囊括新的活動(dòng)和全部已有活動(dòng),這個(gè)過(guò)程就會(huì)更慢。
1. 子文檔的索引、更新和刪除
(1)映射
? ? ? ? 在示例索引get-together的映射中定義了一對(duì)父子關(guān)系屬性如下;
(2)索引和檢索
? ? ? ? 索引子文檔時(shí),需要在URI中放置routing值作為參數(shù)。routing字段向ES提供了散列的ID,即路由值,這使得ES將父子文檔路由到相同的分片,搜索的時(shí)候能從中獲益。ES會(huì)自動(dòng)使用這個(gè)路由值來(lái)查詢父輩的分片并獲得其子輩,或者是查詢子輩的分片來(lái)獲得其父輩。
? ? ? ? routing參數(shù)是強(qiáng)制的,如果不加該參數(shù),報(bào)錯(cuò)如下:
{"error" : {"root_cause" : [{"type" : "mapper_parsing_exception","reason" : "failed to parse"}],"type" : "mapper_parsing_exception","reason" : "failed to parse","caused_by" : {"type" : "illegal_argument_exception","reason" : "[routing] is missing for join field [relationship_type]"}},"status" : 400 }? ? ? ? 當(dāng)索引子文檔時(shí),其父輩文檔可能已經(jīng)被索引,也可能尚未索引。這類似于關(guān)系數(shù)據(jù)庫(kù)中的主子表之間沒有強(qiáng)制的外鍵約束。在上例中,當(dāng)索引event子文檔1103時(shí),其對(duì)應(yīng)的group父文檔2可以并不存在。
? ? ? ? _routing字段是被存儲(chǔ)的,因此可以檢索其內(nèi)容。同時(shí),這個(gè)字段也是被索引的,這樣可以通過(guò)條件來(lái)搜索其值。為了檢索一篇活動(dòng)文檔,這里運(yùn)行了一個(gè)普通的索引請(qǐng)求:
curl '172.16.1.127:9200/get-together/_doc/1103?routing=2&pretty'? ? ? ? 結(jié)果返回:
{"_index" : "get-together","_type" : "_doc","_id" : "1103","_version" : 1,"_routing" : "2","found" : true,"_source" : {"host" : "Radu","title" : "Yet another Elasticsearch intro in Denver","relationship_type" : {"name" : "event","parent" : "2"}} }? ? ? ? 如果請(qǐng)求中不加routing=2,查詢會(huì)路由到1103的散列分片上去,而不是2的散列分片,最終導(dǎo)致查詢不到相應(yīng)的文檔。再者,子文檔ID,如1103在索引中并不唯一,只有parent ID和_id的組合才是唯一的。
(3)更新與刪除
? ? ? ? 類似地,更新與刪除子文檔同樣需要指定routing參數(shù)。
? ? ? ? 通過(guò)查詢來(lái)進(jìn)行的刪除,不需要指定routing參數(shù):
curl -X POST "172.16.1.127:9200/get-together/_delete_by_query?pretty" -H 'Content-Type: application/json' -d' {"query": {"query_string": {"fields": ["host"],"query": "radu"}} }'2. 在父文檔和子文檔中搜索
(1)has_child查詢和過(guò)濾器
? ? ? ? 使用子輩的條件來(lái)搜索父輩的時(shí)候,如搜索Elasticsearch活動(dòng)的分組,可以使用has_child查詢或過(guò)濾器。
? ? ? ? has_child查詢和這個(gè)過(guò)濾器的運(yùn)行方式差不多,不過(guò)它可以通過(guò)聚合子文檔的得分,對(duì)每個(gè)父輩進(jìn)行評(píng)分。可以將score_mode設(shè)置為max、sum、avg或none,和嵌套查詢是一樣的。例如,如下查詢?cè)诜祷胤纸M時(shí),按照舉辦的Elasticsearch活動(dòng)之最高相關(guān)性排序:
curl -X GET "172.16.1.127:9200/get-together/_doc/_search?pretty" -H 'Content-Type: application/json' -d' {"query": {"has_child": {"type": "event","score_mode": "max","query": {"term": {"title": "elasticsearch"}}}} }'(2)在結(jié)果中獲得子文檔
? ? ? ? 默認(rèn)情況下,has_child查詢只會(huì)返回父文檔,不會(huì)返回子文檔。通過(guò)添加inner_hits選項(xiàng)可以獲得子文檔:
(3)has_parent查詢和過(guò)濾器
? ? ? ? 使用父輩的條件來(lái)搜索子輩的時(shí)候使用has_parent查詢或過(guò)濾器。下面的代碼展示了如何搜索關(guān)于Elasticsearch的活動(dòng),而且它們只在Denver舉辦。
(4)子輩聚合
? ? ? ? ES允許在子文檔上嵌入聚合。假設(shè)已經(jīng)通過(guò)詞條聚合,獲得了get-together分組中最流行的標(biāo)簽。對(duì)于這些標(biāo)簽,需要知道每個(gè)標(biāo)簽的分組中,誰(shuí)是最積極的活動(dòng)參與者。下面代碼在標(biāo)簽的terms聚合下嵌套了children聚合,以此來(lái)發(fā)現(xiàn)這類會(huì)員。在children聚合中,又嵌套了另一個(gè)terms聚合來(lái)統(tǒng)計(jì)每個(gè)標(biāo)簽所對(duì)應(yīng)的活動(dòng)參與者。
五、反規(guī)范化
1. 反規(guī)范化使用案例
? ? ? ? 反規(guī)范化利用數(shù)據(jù)冗余,以空間換時(shí)間,查詢時(shí)沒有必要連接不同的文檔。在分布式系統(tǒng)中這一點(diǎn)尤為重要,因?yàn)榭邕^(guò)網(wǎng)絡(luò)來(lái)連接多個(gè)文檔引入了很大的延時(shí)。ES中的反規(guī)范化主要用于處理多對(duì)多關(guān)系。與嵌套、父子的一對(duì)多實(shí)現(xiàn)不同,ES無(wú)法承諾讓多對(duì)多關(guān)系保持在一個(gè)節(jié)點(diǎn)內(nèi)。如圖7所示,一個(gè)單獨(dú)的關(guān)系可能會(huì)延伸到整個(gè)數(shù)據(jù)集。這種操作可能會(huì)非常昂貴,跨網(wǎng)絡(luò)的連接無(wú)法避免。
圖7 多對(duì)多關(guān)系會(huì)包含大量的數(shù)據(jù),使得本地連接成為不可能?
? ? ? ? 圖8展示了反規(guī)范化后,分組與會(huì)員之間的多對(duì)多關(guān)系。它將多對(duì)多關(guān)系的一端反規(guī)范化為許多一對(duì)多關(guān)系。
圖8 多對(duì)多關(guān)系反規(guī)范化為多個(gè)一對(duì)多關(guān)系,讓本地連接成為可能?
2. 索引、更新和刪除反規(guī)范化的數(shù)據(jù)
(1)反規(guī)范化哪個(gè)方向
? ? ? ? 是將會(huì)員復(fù)制為分組的子文檔呢。還是反過(guò)來(lái)將分組復(fù)制為會(huì)員的子文檔?必須要理解數(shù)據(jù)是如何索引、更新、刪除和查詢的,才能做出選擇。被反規(guī)范化的部分(也就是子文檔)從各方面看都是難以管理的。
- 會(huì)多次索引這些文檔,某文檔在父輩中每出現(xiàn)一次,就會(huì)被索引一次。
- 更新時(shí),必須更新這篇文檔的所有實(shí)例。
- 刪除時(shí),必須刪除所有實(shí)例。
- 當(dāng)單獨(dú)查詢這些子文檔時(shí),將獲得多個(gè)同樣的內(nèi)容,所以需要在應(yīng)用端移除重復(fù)項(xiàng)。
? ? ? ? 基于這些假設(shè),看上去讓會(huì)員成為分組的子文檔更合理一些。會(huì)員文檔的規(guī)模更小,變動(dòng)沒那么頻繁,查詢頻率也不像分組活動(dòng)那么高。因此,管理復(fù)制后的會(huì)員文檔要容易一些。同理也可應(yīng)用于SQL數(shù)據(jù)庫(kù)的反規(guī)范化。
(2)如何表示一對(duì)多關(guān)系
? ? ? ? 是選擇父子關(guān)系還是嵌套文檔呢?這里,最好按照分組和會(huì)員一起搜索并獲取的頻率來(lái)選擇。嵌套查詢比has_parent或has_child查詢性能更佳。但如果會(huì)員更新頻繁,父子結(jié)構(gòu)性能更好,因?yàn)樗鼈兛梢愿髯詥为?dú)更新。
? ? ? ? 對(duì)于本例,假設(shè)一并搜索并獲取分組和會(huì)員是很罕見的行為,而會(huì)員經(jīng)常會(huì)加入或者退出分組,因此選擇父子關(guān)系。
(3)索引
? ? ? ? 下面代碼首先定義了一個(gè)包含分組-會(huì)員父子關(guān)系的新索引,然后添加了兩個(gè)父文檔,并在兩個(gè)分組中分別添加了同一個(gè)子文檔。
(4)更新
? ? ? ? 下面代碼將搜索_id為3的全部文檔,并將其更名為L(zhǎng)ee。為同一會(huì)員使用同樣的_id,對(duì)于會(huì)員所屬的分組每組使用一次。這樣通過(guò)會(huì)員的ID,快速并可靠地檢索某位會(huì)員的全部實(shí)例。
(5)刪除
curl -X DELETE '172.16.1.127:9200/my_index/_doc/3?routing=1&pretty' curl -X DELETE '172.16.1.127:9200/my_index/_doc/3?routing=2&pretty'3. 查詢反規(guī)范化的數(shù)據(jù)
? ? ? ? 下面的代碼首先索引兩個(gè)會(huì)員,然后在搜索的時(shí)候,將同時(shí)獲得兩者。
curl -X PUT "172.16.1.127:9200/my_index/_doc/4?routing=1&refresh&pretty" -H 'Content-Type: application/json' -d' {"first_name": "Radu","last_name": "Gheorghe","my_join_field": {"name": "member","parent": "1"} }'curl -X PUT "172.16.1.127:9200/my_index/_doc/4?routing=2&refresh&pretty" -H 'Content-Type: application/json' -d' {"first_name": "Radu","last_name": "Gheorghe","my_join_field": {"name": "member","parent": "2"} }'curl -X POST "172.16.1.127:9200/my_index/_refresh?pretty"curl '172.16.1.127:9200/my_index/_doc/_search?pretty' -H 'Content-Type: application/json' -d' {"query": {"term": {"first_name": "radu"}} }'? ? ? ? 對(duì)于多數(shù)索引和聚合,一種變通的方式是在獨(dú)立的索引中維護(hù)所有會(huì)員的副本。
總結(jié)
以上是生活随笔為你收集整理的触类旁通Elasticsearch:关联的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: layui 前端计算
- 下一篇: Exchange报错:452 4.3.1