菜雞一隻,國慶真是玩了好幾天,等到快上班的時候纔開始又學習,找狀態
本文來講講ES中的Join方案!
在數據庫中,join是非常常見的操作!
其實就是將兩張表的數據合併到一起,然後查詢出結果數據,當然最後可能還需要一些過濾,這是數據庫中的概念
在ES中也有join的方案,ES提供了兩種api:
1、使用Nested結構存儲(查詢)數據
2、通過設置字段的type爲join,然後使用hasChild和hasParent查詢數據
官網相關鏈接:
https://www.elastic.co/guide/en/elasticsearch/reference/6.3/parent-join.html
https://www.elastic.co/guide/en/elasticsearch/reference/6.3/query-dsl-nested-query.html
https://www.elastic.co/guide/en/elasticsearch/reference/6.3/query-dsl-has-child-query.html
https://www.elastic.co/guide/en/elasticsearch/reference/6.3/query-dsl-has-parent-query.html
https://www.elastic.co/guide/en/elasticsearch/reference/6.3/query-dsl-parent-id-query.html
https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.3/java-joining-queries.html
舉個很現實的例子:
表A:blog_new(博客)
表B:comments(評論)
他們之間的關係是一個問題會有多個答案,將這部分數據錄入到ES中提供搜索
blog_new
|
|
comments
方案一:
通過Nested解決問題(把問題和答案當做一個document文檔插入ES中):
##設置mapping
PUT blog_new
{
"mappings": {
"blog": {
"properties": {
"title": {
"type": "text"
},
"body": {
"type": "text"
},
"tags": {
"type": "keyword"
},
"comments": {
"type": "nested",
"properties": {
"name": {
"type": "text"
},
"comment": {
"type": "text"
},
"age": {
"type": "short"
},
"stars": {
"type": "short"
},
"date": {
"type": "date"
}
}
}
}
}
}
}
##放進兩條測試數據
PUT blog_new/blog/1
{
"title": "Nest eggs",
"body": "Making your money work...",
"tags": [ "cash", "shares" ],
"comments": [
{
"name": "John Smith",
"comment": "Great article",
"age": 28,
"stars": 4,
"date": "2014-09-01"
},
{
"name": "Alice White",
"comment": "More like this please",
"age": 31,
"stars": 5,
"date": "2014-10-22"
}
]
}
POST blog_new/blog/2
{
"title": "Hero",
"body": "Hero test body...",
"tags": ["Heros", "happy"],
"comments": [
{
"name": "steve",
"age": 24,
"stars": 5,
"comment": "Nice article..",
"date": "2014-10-22"
}
]
}
當我們對於這樣的數據結構進行增刪改查的時候,需要通過nested關鍵字來處理
1、查詢數據:
##查詢查詢評論的人的名稱匹配john和他/她的年齡是28歲
GET blog_new/blog/_search
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{
"match": {
"comments.name": "john"
}
},
{
"match": {
"comments.age": 28
}
}
]
}
}
}
}
]
}
}
}
2、新增數據:
##可以把整條數據全部讀出來,然後整條update,
POST blog_new/blog/2
{
"title": "Hero",
"body": "Hero test body...",
"tags": ["Heros", "happy"],
"comments": [
{
"name": "steve",
"age": 24,
"stars": 5,
"comment": "Nice article..",
"date": "2014-10-22"
}
]
}
##也可以通過script來更新部分(某個字段),還是比較麻煩的
POST blog_new/blog/2/_update
{
"script" : {
"source": "ctx._source.tags.add(params.tag)",
"lang": "painless",
"params" : {
"tag" : "blue"
}
}
}
POST blog_new/blog/2/_update
{
"script": {
"source": "ctx._source.comments.add(params.commentuser)",
"lang": "painless",
"params": {
"commentuser": {
"name": "steve",
"age": 70,
"stars": 5,
"comment": "very very good article...",
"date": "2014-10-22"
}
}
}
}
3、刪除數據:
##刪除文檔id=1的所有名字叫做John Smith的comments(評論)
POST blog_new/blog/1/_update
{
"script": {
"lang": "painless",
"source": "ctx._source.comments.removeIf(it -> it.name == 'John Smith');"
}
}
4、修改數據:
##修改文檔id=2,名字叫做steve的comments(評論),年齡修改爲75,評論內容修改爲very very good article...
POST blog_new/blog/2/_update
{
"script": {
"source": "for(e in ctx._source.comments){if (e.name == 'steve') {e.age = 75; e.comment= 'very very good article...';}}"
}
}
5、與之對應的JavaAPI:
public static void main(String[] args) throws Exception {
ElasticConfigration es = new ElasticConfigration();
//自己寫的es的TransportClient創建
es.initialize();
//獲得client對象
TransportClient client = es.client();
testNestedQuery(client);
}
public static void testNestedQuery(TransportClient client) {
NestedQueryBuilder nestedQueryBuilder =
new NestedQueryBuilder("comments", QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("comments.name", "john"))
.must(QueryBuilders.matchQuery("comments.age",28)),ScoreMode.None);
SearchResponse searchResponse = client.prepareSearch("blog_new")
.setTypes("blog")
.addSort("_score", SortOrder.DESC)
.setQuery(nestedQueryBuilder)
.setFrom(0).setSize(50).execute().actionGet();
printSearchResponse(searchResponse);
}
public static void testNestedUpdateQuery(TransportClient client) throws IOException {
// UpdateResponse updateResponse = client.prepareUpdate("blog_new","blog","2").setScript(
// new Script("for(e in ctx._source.comments){if (e.name == 'steve') {e.age = 25; e.comment= 'very very good article...';}}")).execute().actionGet();
// System.out.println(updateResponse.status());
/**
* 如果要插入一個新的comment,需要在params的Map中,插入JSONObject對象
* 直接使用json格式的字符串,會報錯:tried to parse field [null] as object, but found a concrete value
*/
JSONObject jsonObject = new JSONObject();
jsonObject.put("name","ll");
jsonObject.put("age",75);
jsonObject.put("stars",5);
jsonObject.put("comment","very very good article...");
jsonObject.put("date","2014-10-22");
jsonObject.put("name","ll");
HashMap hashMap = new HashMap<String,Object>();
hashMap.put("commentuser",jsonObject);
UpdateResponse updateResponse = client
.prepareUpdate("blog_new","blog","2")
.setScript(new Script(ScriptType.INLINE,"painless",
"ctx._source.comments.add(params.commentuser)", hashMap))
.execute().actionGet();
System.out.println(updateResponse.status());
}
public static void printSearchResponse( SearchResponse searchResponse) {
// 命中的記錄數
long totalHits = searchResponse.getHits().getTotalHits();
System.out.println("totalHits:" + totalHits);
for (SearchHit searchHit : searchResponse.getHits()) {
// 打分
float score = searchHit.getScore();
System.out.println("score:" + score);
// 文章id
int id = Integer.parseInt(searchHit.getId());
System.out.println("doc_id:" + id);
String sourceAsString = searchHit.getSourceAsString();
System.out.println(sourceAsString);
}
}
本文參考了一些其他人的博客和官網,官網列在最前面了,建議大家都去看下,有助於整合思路,有一篇文章我覺得寫的超好的,大家也可以看看,如下:
《乾貨 | Elasticsearch Nested類型深入詳解》:https://blog.csdn.net/laoyang360/article/details/82950393(作者:銘毅天下)
好,方案二,我在下一篇文章裏面再寫吧,不然文章太長,看的我自己都頭疼,更別說各位看官了~
老話,菜雞一隻,如果有任何問題,歡迎留言!