四大嵌套查询
# 背景
MySQL和ES在嵌套查询这块有非常大的差异,所以在2.0版本中特地梳理此章节,帮助各位主公快速了解它们之间的差异并上手
提示
其中MP中已经有的在Easy-Es中用法和功能和它保持一致,降低用户学习及使用成本,只需要学习MP中没有,ES中独有的即可.
# ES四大嵌套查询
MySQL | Mybatis-Plus | ES | Easy-Es |
---|---|---|---|
and 嵌套 | and(Consumer) | must | and(Consumer) |
or 嵌套 | or (Consumer) | should | or (Consumer) |
无 | 无 | filter | filter(Consumer) |
无 | 无 | must_not | not(Consumer) |
# ES四大拼接查询
MySQL | Mybatis-Plus | ES | Easy-Es |
---|---|---|---|
and 拼接 | 默认 | must | 默认 |
or 拼接 | or() | should | or() |
无 | 无 | filter | filter() |
无 | 无 | must_not | not() |
提示
如果您有用过MP,理解上面的差异就比较简单,如果您尚未用过MP也没关系,咱只需要搞清楚嵌套类型与拼接类型的差异即可. 另外关于must和filter的选择,它们在功能上类似,都是表示必须满足的条件,不同之处在于filter不计算得分,性能表现更好,但不支持根据得分排序.
# 嵌套与拼接如何理解和使用
简单来说,嵌套就是有括号的,拼接就是无括号的,或者说嵌套就是里面有东西的,拼接是里面没东西的,怎么理解这段话呢?以大家熟悉的MySQL中的一段SQL为例:
// 这里面的or就是拼接or
where name = '老汉' or name = '痴汉';
// 用MP或EE来写就是
wrapper.eq(name,"老汉").or().eq(name,"老汉");
// 这里面的or是嵌套or
where name = '老汉' or (age = 18 and size = 18)
// 用MP或EE来写就是
wrapper.eq(name,"老汉").or(i->i.eq(age,18).eq(size,18));
2
3
4
5
6
7
8
9
10
通过上面的例子大家应该很好理解拼接和嵌套的差异了,对应到es中,嵌套就是把嵌套中的所有查询条件封装进一个新创建的boolQuery中,然后拼接至根boolQuery,而拼接则是把查询条件直接拼接进根boolQuery中. 在EE中条件与条件直接默认的拼接方式是以and拼接,由于95%的使用场景都是and拼接,所以直接省略了and这种拼接,这点和MP一样
// sql
where name = '老汉' and size = 18
// 用EE或者MP可以直接写为
wrapper.eq(name,"老汉").eq(size,18)
2
3
4
如果你需要改写默认的拼接方式只需要加上对应的拼接类型即可,例如:
// sql
where name = '老汉' or size = 18
// 用EE直接写为
wrapper.eq(name,"老汉").or().eq(size,18);
// sql
where name = '老汉' and age != 18
// 用EE写为
wrapper.eq(name,"老汉").not().eq(age,18);
// 所有表'非'的条件都可以用not()来拼接
wrapper.eq(name,"老汉").not().eq(age,18).not().match(desc,'是个纯洁的好男人');
2
3
4
5
6
7
8
9
10
11
12
所以,如果你条件中只有一项内容,直接使用拼接就可以解决,如果条件中有多项内容,则可以使用嵌套,关于嵌套和拼接的理解,是不是很简单,你学废了吗?
# 优势对比
相比MySQL只有2中类型嵌套,ES一共有4种,并且在封装方式上差异也非常大,MySQL中的查询条件是以FIFO队列的形式进行封装 但ES中是以树形结构的形式进行封装,在层级比较深的查询中,其难度和复杂程度就算是ES老手也容易搞错,好在有了Easy-Es, 您又可以重回MP时代,大幅减少开发负担和出错的可能,为了保持和MP一样的语法,又要屏蔽ES和MySQL之间巨大的差异,这块 内容我花了近一年的碎片时间才完成,是整个框架中最难啃的一块骨头,不过一切都是值得的,不信咱们接着往下看.
下面我们以一段具体的复杂查询使用案例来对比,优势一目了然:
// MySQL语法
where business_type = 1
and (state = 9 or (state = 8 and bidding_sign = 1))
or (business_type = 2 and state in (2,3));
// Easy-Es及Mybatis-Plus语法
wrapper.eq("business_type", 1)
.and(a -> a.eq("state", 9).or(b -> b.eq("state", 8).eq("bidding_sign", 1)))
.or(i -> i.eq("business_type", 2).in("state", 2, 3));
// ES原生的RestHighLevel语法
List<Integer> values = Arrays.asList(2, 3);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.termQuery("business_type", 1));
boolQueryBuilder.must(QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("state", 9))
.should(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("state", 8))
.must(QueryBuilders.termQuery("bidding_sign", 1))));
boolQueryBuilder.should(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("business_type", 2))
.must(QueryBuilders.termsQuery("state", values)));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
上面的例子树的最大深度仅为2,如果深度超过2以后ES原生语法就很难看得懂了,更别说正确编码,就算您是ES老手也容易栽坑里,但使用EASY-ES则可以1:1轻松又简单还原复杂SQL.
尽管我们的语法和MP一毛一样,但为了防止很多小白不会用或者没接触过MP,我尽量每种类型的使用都写一个demo供主公们参考:
/**
* 场景一: 嵌套and的使用
*/
@Test
public void testNestedAnd() {
// 下面查询条件等价于MySQL中的 select * from document where star_num in (1, 2) and (title = '老汉' or title = '推*')
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.in(Document::getStarNum, 1, 2)
.and(w -> w.eq(Document::getTitle, "老汉").or().eq(Document::getTitle, "推*"));
List<Document> documents = documentMapper.selectList(wrapper);
}
/**
* 场景二: 拼接and的使用
*/
@Test
public void testAnd(){
// 下面查询条件等价于MySQL中的 select * from document where title = '老汉' and content like '推*'
// 拼接and比较特殊,因为使用场景最多,所以条件与条件之间默认就是拼接and,所以可以直接省略,这点和MP是一样的
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(Document::getTitle, "老汉")
.match(Document::getContent, "推*");
List<Document> documents = documentMapper.selectList(wrapper);
}
/**
* 场景二: 嵌套or的使用
*/
@Test
public void testNestedOr() {
// 下面查询条件等价于MySQL中的 select * from document where star_num = 1 or (title = '老汉' and creator = '糟老头子')
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(Document::getStarNum, 1)
.or(i -> i.eq(Document::getTitle, "老汉").eq(Document::getCreator, "糟老头子"));
List<Document> documents = documentMapper.selectList(wrapper);
}
/**
* 场景三: 拼接or的使用
*/
@Test
public void testOr() {
// 下面查询条件等价于MySQL中的 select * from document where title = '老汉' or title = '痴汉'
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(Document::getTitle, "老汉")
.or()
.eq(Document::getTitle, "痴汉");
List<Document> documents = documentMapper.selectList(wrapper);
}
/**
* 场景四: 嵌套filter的使用 其实和场景一一样,只不过filter中的条件不计算得分,无法按得分排序,查询性能稍高
*/
@Test
public void testNestedFilter() {
// 下面查询条件等价于MySQL中的 select * from document where star_num in (1, 2) and (title = '老汉' or title = '推*')
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.in(Document::getStarNum, 1, 2)
.filter(w -> w.eq(Document::getTitle, "老汉").or().eq(Document::getTitle, "推*"));
List<Document> documents = documentMapper.selectList(wrapper);
}
/**
* 场景五: 拼接filter的使用 filter中的条件不计算得分,无法按得分排序,查询性能稍高
*/
@Test
public void testFilter() {
// 下面查询条件等价于MySQL中的 select * from document where title = '老汉'
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.filter().eq(Document::getTitle, "老汉");
List<Document> documents = documentMapper.selectList(wrapper);
}
/**
* 场景六: 嵌套mustNot的使用
*/
@Test
public void testNestedNot() {
// 下面查询条件等价于MySQL中的 select * from document where title = '老汉' and (size != 18 and age != 18)
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(Document::getTitle, "老汉")
.not(i->i.eq(size,18).eq(age,18));
List<Document> documents = documentMapper.selectList(wrapper);
}
/**
* 场景六: 拼接not()的使用
*/
@Test
public void testNot() {
// 下面查询条件等价于MySQL中的 select * from document where title = '老汉' and size != 18
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(Document::getTitle, "老汉")
.not()
.eq(size,18);
List<Document> documents = documentMapper.selectList(wrapper);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
以上便是一些高频使用场景中的案例,只需要理解用法,然后今后无论查询条件有多么复杂,嵌套层级有多么的"深",无论您ES水平有多菜,您都可以像ES专家一样轻松拿捏,家人们一起把公屏扣在牛*上!