【ES】ES中的join方案一(Nested類型,基於6.3版本的java實現)

菜雞一隻,國慶真是玩了好幾天,等到快上班的時候纔開始又學習,找狀態

本文來講講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(作者:銘毅天下)

好,方案二,我在下一篇文章裏面再寫吧,不然文章太長,看的我自己都頭疼,更別說各位看官了~

老話,菜雞一隻,如果有任何問題,歡迎留言!

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章