菜鸡一只,国庆真是玩了好几天,等到快上班的时候才开始又学习,找状态
本文来讲讲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(作者:铭毅天下)
好,方案二,我在下一篇文章里面再写吧,不然文章太长,看的我自己都头疼,更别说各位看官了~
老话,菜鸡一只,如果有任何问题,欢迎留言!