Skip to content

Commit

Permalink
ok
Browse files Browse the repository at this point in the history
  • Loading branch information
xpc committed Mar 20, 2024
1 parent 1d55796 commit 1c39a88
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 81 deletions.
186 changes: 106 additions & 80 deletions docs/10.v2.x文档/030.拓展功能/050.Join父子类型.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ ES本身更适合"大宽表"模式,不要带着传统关系型数据库那种思
关于父子类型和嵌套类型的选择:如果对文档的写多于读,那么建议你选择父子类型,如果文档读多于写, 那么请选择嵌套类型.
:::

> 推荐您在使用前先通过互联网了解一下ES原生的父子类型或RestHighLevelClient中父子类型相关的索引创建及CRUD,有一定理论基础后可以更方便理解本框架的API设计及使用. ES的父子类型及嵌套类型本身就是极度复杂的东西,使用成本非常高,在看完原生语法后相信您会给我们Star!
## 父子类型创建索引

- 步骤一 添加注解,指定父子关系:
Expand Down Expand Up @@ -44,13 +46,37 @@ documentMapper.createIndex();
```

创建完成后,父子类型索引结构如图:

![index.png](https://iknow.hs.net/fa806956-3a52-44d3-a724-5ec9c778ca73.png)


## 父子类型 CRUD

注意父子类型由于都是独立的文档,独立的实体类,所以各自都需要有各自的mapper

API:

```java
// 根据父id查询 (返回满足条件的所有子文档)
parentId(Object parentId, String type);
parentId(boolean condition, Object parentId, String type);
parentId(Object parentId, String type, Float boost);
parentId(boolean condition, Object parentId, String type, Float boost);

// 根据子查父匹配 (返回满足条件的所有子文档)
hasParent(Consumer<Param> consumer);
hasParent(String parentType, Consumer<Param> consumer);
Children hasParent(boolean condition, String parentType, Consumer<Param> consumer);
hasParent(boolean condition, String parentType, Consumer<Param> consumer, boolean score);

// 根据父查子 (返回满足条件的父文档)
hasChild(String type, Consumer<Param> consumer);
hasChild(String type, Consumer<Param> consumer, ScoreMode scoreMode);
hasChild(boolean condition, String type, Consumer<Param> consumer);
hasChild(boolean condition, String type, Consumer<Param> consumer, ScoreMode scoreMode);

```

CRUD示例:
```java
@Test
Expand Down Expand Up @@ -111,88 +137,88 @@ CRUD示例:

// es写入数据有延迟 适当休眠 保证后续查询结果正确
Thread.sleep(2000);
}
}

@Test
public void testSelect() {
// 温馨提示,下面wrapper中的type实际上就是索引JoinField中指定的父子名称,与原生语法是一致的
// case1: hasChild查询,返回的是相关的父文档 所以查询用父文档实体及其mapper
LambdaEsQueryWrapper<Document> documentWrapper = new LambdaEsQueryWrapper<>();
documentWrapper.hasChild("comment", w -> w.eq(FieldUtils.val(Comment::getCommentContent), "test1"));
List<Document> documents = documentMapper.selectList(documentWrapper);
System.out.println(documents);

LambdaEsQueryWrapper<Author> authorWrapper = new LambdaEsQueryWrapper<>();
authorWrapper.hasChild("contact", w -> w.match(FieldUtils.val(Contact::getAddress), "city"));
List<Author> authors = authorMapper.selectList(authorWrapper);
System.out.println(authors);

// case2: hasParent查询,返回的是相关的子文档 所以查询用子文档实体及其mapper
LambdaEsQueryWrapper<Comment> commentWrapper = new LambdaEsQueryWrapper<>();
commentWrapper.like(Comment::getCommentContent, "test");
// 字段名称你也可以不用FieldUtils.val,直接传入字符串也行
commentWrapper.hasParent("document", w -> w.match("content", "father"));
List<Comment> comments = commentMapper.selectList(commentWrapper);
System.out.println(comments);

// case2.1: 孙子查爹的情况
LambdaEsQueryWrapper<Contact> contactWrapper = new LambdaEsQueryWrapper<>();
contactWrapper.hasParent("author", w -> w.eq(FieldUtils.val(Author::getAuthorName), "cat"));
List<Contact> contacts = contactMapper.selectList(contactWrapper);
System.out.println(contacts);

// case2.2: 2.1的简写
LambdaEsQueryWrapper<Contact> contactWrapper1 = new LambdaEsQueryWrapper<>();
// hasParent之所以可以不指定parentType简写是因为框架可以通过@Join注解中指定的父子关系自动推断出其父type,因此用户可以不指定父type直接查询,但hasChild不能简写,因为一个父亲可能有多个孩子,但一个孩子只能有一个亲爹
contactWrapper1.hasParent(w -> w.eq(FieldUtils.val(Author::getAuthorName), "cat"));
List<Contact> contacts1 = contactMapper.selectList(contactWrapper1);
System.out.println(contacts1);

// case3: parentId查询,返回的是相关的子文档,与case2类似,所以查询用子文档实体及其mapper
commentWrapper = new LambdaEsQueryWrapper<>();
commentWrapper.parentId("doc-1", "comment");
List<Comment> commentList = commentMapper.selectList(commentWrapper);
System.out.println(commentList);

contactWrapper = new LambdaEsQueryWrapper<>();
contactWrapper.parentId("author-2", "contact");
List<Contact> contactList = contactMapper.selectList(contactWrapper);
System.out.println(contactList);
}

@Test
public void testUpdate() {
// case1: 父文档/子文档 根据各自的id更新
Document document = new Document();
document.setEsId("doc-1");
document.setTitle("我是隔壁老王标题");
documentMapper.updateById(FIXED_ROUTING, document);

Contact contact = new Contact();
contact.setContactId("contact-2");
contact.setAddress("update address");
contactMapper.updateById(FIXED_ROUTING, contact);

// case2: 父文档/子文档 根据各自条件更新
Comment comment = new Comment();
comment.setCommentContent("update comment content");
LambdaEsUpdateWrapper<Comment> wrapper = new LambdaEsUpdateWrapper<>();
wrapper.eq(Comment::getCommentContent, "test1");
wrapper.routing(FIXED_ROUTING);
commentMapper.update(comment, wrapper);
}

@Test
public void testDelete() {
// case1: 父文档/子文档 根据各自的id删除
documentMapper.deleteById(FIXED_ROUTING, "doc-1");

//case2: 父文档/子文档 根据各自条件删除
LambdaEsQueryWrapper<Comment> wrapper = new LambdaEsQueryWrapper<>();
wrapper.like(Comment::getCommentContent, "test")
@Test
public void testSelect() {
// 温馨提示,下面wrapper中的type实际上就是索引JoinField中指定的父子名称,与原生语法是一致的
// case1: hasChild查询,返回的是相关的父文档 所以查询用父文档实体及其mapper
LambdaEsQueryWrapper<Document> documentWrapper = new LambdaEsQueryWrapper<>();
documentWrapper.hasChild("comment", w -> w.eq(FieldUtils.val(Comment::getCommentContent), "test1"));
List<Document> documents = documentMapper.selectList(documentWrapper);
System.out.println(documents);

LambdaEsQueryWrapper<Author> authorWrapper = new LambdaEsQueryWrapper<>();
authorWrapper.hasChild("contact", w -> w.match(FieldUtils.val(Contact::getAddress), "city"));
List<Author> authors = authorMapper.selectList(authorWrapper);
System.out.println(authors);

// case2: hasParent查询,返回的是相关的子文档 所以查询用子文档实体及其mapper
LambdaEsQueryWrapper<Comment> commentWrapper = new LambdaEsQueryWrapper<>();
commentWrapper.like(Comment::getCommentContent, "test");
// 字段名称你也可以不用FieldUtils.val,直接传入字符串也行
commentWrapper.hasParent("document", w -> w.match("content", "father"));
List<Comment> comments = commentMapper.selectList(commentWrapper);
System.out.println(comments);

// case2.1: 孙子查爹的情况
LambdaEsQueryWrapper<Contact> contactWrapper = new LambdaEsQueryWrapper<>();
contactWrapper.hasParent("author", w -> w.eq(FieldUtils.val(Author::getAuthorName), "cat"));
List<Contact> contacts = contactMapper.selectList(contactWrapper);
System.out.println(contacts);

// case2.2: 2.1的简写
LambdaEsQueryWrapper<Contact> contactWrapper1 = new LambdaEsQueryWrapper<>();
// hasParent之所以可以不指定parentType简写是因为框架可以通过@Join注解中指定的父子关系自动推断出其父type,因此用户可以不指定父type直接查询,但hasChild不能简写,因为一个父亲可能有多个孩子,但一个孩子只能有一个亲爹
contactWrapper1.hasParent(w -> w.eq(FieldUtils.val(Author::getAuthorName), "cat"));
List<Contact> contacts1 = contactMapper.selectList(contactWrapper1);
System.out.println(contacts1);

// case3: parentId查询,返回的是相关的子文档,与case2类似,所以查询用子文档实体及其mapper
commentWrapper = new LambdaEsQueryWrapper<>();
commentWrapper.parentId("doc-1", "comment");
List<Comment> commentList = commentMapper.selectList(commentWrapper);
System.out.println(commentList);

contactWrapper = new LambdaEsQueryWrapper<>();
contactWrapper.parentId("author-2", "contact");
List<Contact> contactList = contactMapper.selectList(contactWrapper);
System.out.println(contactList);
}

@Test
public void testUpdate() {
// case1: 父文档/子文档 根据各自的id更新
Document document = new Document();
document.setEsId("doc-1");
document.setTitle("我是隔壁老王标题");
documentMapper.updateById(FIXED_ROUTING, document);

Contact contact = new Contact();
contact.setContactId("contact-2");
contact.setAddress("update address");
contactMapper.updateById(FIXED_ROUTING, contact);

// case2: 父文档/子文档 根据各自条件更新
Comment comment = new Comment();
comment.setCommentContent("update comment content");
LambdaEsUpdateWrapper<Comment> wrapper = new LambdaEsUpdateWrapper<>();
wrapper.eq(Comment::getCommentContent, "test1");
wrapper.routing(FIXED_ROUTING);
commentMapper.update(comment, wrapper);
}

@Test
public void testDelete() {
// case1: 父文档/子文档 根据各自的id删除
documentMapper.deleteById(FIXED_ROUTING, "doc-1");

//case2: 父文档/子文档 根据各自条件删除
LambdaEsQueryWrapper<Comment> wrapper = new LambdaEsQueryWrapper<>();
wrapper.like(Comment::getCommentContent, "test")
.routing(FIXED_ROUTING);
commentMapper.delete(wrapper);
}
commentMapper.delete(wrapper);
}

```
相关demo可参考源码的test模块->test目录->join包
2 changes: 1 addition & 1 deletion docs/20.v1.x文档/030.拓展功能/040.嵌套类型.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ date: 2023-03-18 10:00:00
permalink: /pages/v1.x/05702c/
---

::: tip 前言
:::tip 前言
ES底层是Lucene,由于Lucene实际上是不支持嵌套类型的,所有文档都是以扁平的结构存储在Lucene中,ES对嵌套文档的支持,实际上也是采取了一种投机取巧的方式实现的.

嵌套的文档均以独立的文档存入,然后添加关联关系,这就会导致,一条嵌套类型的文档,底层实际上存储了N条数据,而且更新时会株连九族式更新,导致效率低下,而且对于嵌套类型,其查询功能也受限,不支持聚合排序等功能,因此我们并不建议您在实际开发中使用这种类型.
Expand Down

0 comments on commit 1c39a88

Please sign in to comment.