其查询的意图:查询"user"属性的值为"kimchy"或"elasticsearch"的文档。
2.1 terms 查询机制
试想如下一个场景,在微博应用场景中,需要查看关注你的所有用户发布的微博,那这个查询第一步应该是得到关注你的所有用户列表,然后查询微博的发布者ID在关注你的列表集合中的所有文档。这个关注列表一般不会很小,如果要分两步来实现的话,查询中传入的terms这个集合会很大,相当于关系型数据库中的in (很大的集合),那有没有一种机制,支持类似关系型数据库中 a.id in ( select b.id from B b where …)这种方式呢?答案当然是有的。 下面下能给出本次试验的基础数据:
PUT /users/_doc/2
{
“followers” : [“1”, “3”]
}
PUT /tweets/_doc/1
{
“user” : “1”
}
//其查询写法如下:
GET /tweets/_search
{
“query” : {
“terms” : {
“user” : {
“index” : “users”,
“type” : “_doc”,
“id” : “2”,
“path” : “followers”
}
}
}
}
查询terms支持如下参数:
-
index 指定查询索引名(相当于关系型数据库database)
-
type 指定类型名(相当于关系型数据库table)
-
id 指定文档的id(相当于关系型数据库的row 主键id)
-
path:值来自哪个字段,支持级联,例如followers.id等嵌套json属性。
-
routing:指定路由字段值。
从上可知,terms过滤器的值将从具有指定类型的索引中的指定id的文档中的字段中获取。在内部执行get请求以从指定路径(_source字段中存储的值)中提取。
使用大量词根执行terms查询请求可能相当缓慢,因为每个词根都需要额外的处理和占用内存。为了防止出现这种情况,可以设置最大支持的terms的个数,默认为65536。可以通过设置index.max_terms_count来更改此默认值。
2.2 java(Demo示例)
public static void testTermsQuery() {
RestHighLevelClient client = EsClient.getClient();
try {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(“esdemo”);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(
QueryBuilders.termsQuery(“context”, “fox”, “brown”)
);
// TermsLookup termsLookup = new TermsLookup(“esdemo”, “matchquerydemo”, “1”, “context”);
// termsLookup.routing(“1”);
// sourceBuilder.query(
// QueryBuilders.termsLookupQuery(“context”, termsLookup);
// );
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
查找与一个或多个指定词根相匹配的文档。必须匹配的项的数量取决于指定的最小值或脚本。必须匹配的项的数量根据每个文档的不同而不同,并且由最小值控制的项应该匹配字段,或者由最小值计算的每个文档应该匹配脚本,先从示例开始讲起:
PUT /my-index
{
“mappings”: {
“_doc”: {
“properties”: {
“required_matches”: {
“type”: “long”
}
}
}
}
}
首先为_doc类型新增一个数值型的字段,这里取值为required_matches。
PUT /my-index/_doc/1?refresh
{
“codes”: [“ghi”, “jkl”],
“required_matches”: 2
}
PUT /my-index/_doc/2?refresh
{
“codes”: [“def”, “ghi”,“test”,“ak”],
“required_matches”: 3
}
在存入es中,会自己决定需要匹配的词根个数,低于这个数值,则不会返回该文档。
GET /my-index/_search
{
“query”: {
“terms_set”: {
“codes” : {
“terms” : [“abc”, “def”, “ghi”],
“minimum_should_match_field”: “required_matches”
}
}
}
}
通过属性minimum_should_match_field指定需要匹配的个数,但这个数值来源于文档内部的字段,故该属性值就是指定匹配个数的来源属性名称。该查询结构也支持脚本,其脚本指定字段为minimum_should_match_script,关于script脚本将会在专门的章节中讲述。
范围查询。查询的类型取决于字段类型,对于string字段,是TermRangeQuery,而对于number/date字段,查询是NumericRangeQuery。以下示例返回年龄在10到20岁之间的所有文档。
GET _search
{
“query”: {
“range” : {
“age” : {
“gte” : 10,
“lte” : 20,
“boost” : 2.0
}
}
}
}
range query支持如下参数:
-
gte 大于等于
-
gt 大于
-
lte 小于等于
-
lt 小于
-
boost 权重(重要程度)
4.1 data maths(日期函数)
日期表达式以一个日期(基准日期,锚定日期)开始,可以是now,也可以是以||结尾的日期字符串。这个锚定日期可以有选择地跟随一个或多个数学表达式,例如:
-
+1h 增加一小时
-
-1d 减少一天
-
/d - 向日取整 (返回该天的整点)
-
/M -向月取整(返回该月的第一天的整点)
日期支持如下时间单位:
-
y 年
-
M 月
-
w 周
-
d 日
-
h 小时(12表示法)
-
H 小时(24表示法)
-
m 分钟
-
s 小时
假设现在是2001-01-01 12:00:00,以下是一些例子:
1、now+1h
当前时间加1小时,最终表示为:2001-01-01 13:00:00
2、now-1h
当前时间减1小时,最终表示为:2001-01-01 11:00:00
3、now-1h/d
先减去一小时,为2001-01-01 11:00:00,然后舍弃d后面的时间,这里是舍弃11:00:00,故最终表示为2001-01-01 00:00:00
4、2001.02.01||+1M/d
2001-02-01先加一个月,变为2001-03-01,然后舍弃天后的实际,最终表示为2001-03-01 00:00:00
4.2 日期类型范围查询示例(使用date maths)
GET _search
{
“query”: {
“range” : {
“date” : {
“gte” : “now-1d/d”,
“lt” : “now/d”
}
}
}
}
例如当前时间为2018-10-24 12:25:35,则代表查询的含义为date字段的值大于等于2018-10-23 00:00:00 小于 2018-10-24 00:00:00。
当使用日期数学将日期四舍五入到最近的日、月、小时等时,四舍五入的日期取决于范围的两端是否包含或排除。舍入移动到舍入范围的最后一毫秒,舍出到舍入范围的第一毫秒。关于各运算符的舍入舍出规则如下:
- gt
2014-11-18||/M becomes 2014-11-30T23:59:59.999, 使用gt(不包含e),是向上,取当月最后一天23:59:59
- gte
2014-11-18||/M becomes 2014-11-01,如果运算符为大于等于,则向下舍弃,取当月第一天零点
- lt
2014-11-18||/M becomes 2014-11-01,如果运算算法小于,则向下舍弃
- lte
2014-11-18||/M becomes 2014-11-30T23:59:59.999, 小于等于,则向上,取当月最后一天的23:59:59
4.3 日期类型范围查询示例(使用日期格式转换)
GET _search
{
“query”: {
“range” : {
“born” : {
“gte”: “01/01/2012”,
“lte”: “2013”,
“format”: “dd/MM/yyyy||yyyy”
}
}
}
}
format使用 双竖线|| 做分隔符号,上面表示,查询born字段 大于等于2012-01-01 00:00:00 小于等于2013-01-01 00:00:00。
如果需要年月日,消息分钟,请用如下写法:
QueryBuilders.rangeQuery(“post_date”)
.gte(“2018-10-25T14:12:10”)
.lte(“2018-10-27T14:12:10”)
.format(“yyyy-MM-dd’T’HH:mm:ss||yyyy-MM-dd’T’HH:mm:ss”)。// ‘T’用来分隔 年月日 与 时分秒。
返回指定字段中至少有一个非空值(null)的文档。示例如下:
GET /_search
{
“query”: {
“exists” : { “field” : “user” }
}
}
对于上面的查询,下面的所有文档都将符合要求:
{ “user”: “jane” }
{ “user”: “” } // 空字符串不为null
{ “user”: “-” }
{ “user”: [“jane”] }
{ “user”: [“jane”, null ] } //因为存在jane值不为null,则匹配。
5.1 null_value映射
自定义null值。例如将"null"字符串定义为null值。
PUT /example
{
“mappings”: {
“_doc”: {
“properties”: {
“user”: {
“type”: “keyword”,
“null_value”: “null”
}
}
}
}
}
其作用是会将user字段的null值索引为_null_字符串,则下面的文档将能被exists匹配:
{ “user”: null }
{ “user”: [null] }
5.2 查询不存在某个字段的文档
GET /_search
{
“query”: {
“bool”: {
“must_not”: {
“exists”: {
“field”: “user”
}
}
}
}
}
查询不存在属性user的文档。
词根前缀查询,对查询词根不会使用分词器进行分词。使用示例如下:
GET /_search
{ “query”: {
“prefix” : { “user” : “ki” }
}
}
同样,也可以未字段设置权重(boost)。示例如下:
GET /_search
{ “query”: {
“prefix” : { “user” : { “value” : “ki”, “boost” : 2.0 } }
}
}
其代表含义可转换为关系型数据库查询语句:where a.user like ‘ki%’。
思考一下,如果存在有文档的user字段的值为"Kimmi",使用如下字符查询条件:
{ “query”: {
“prefix” : { “user” : “Ki” }
}
}
该查询能匹配到结果吗?在默认使用标准分词器的环境中,是无法匹配到数据的,其原因如下:首先,在存储文档时,首先会对"kimmi"字段进行分词,返回的词根为kimmi(全小写),将这些词根存入到Elasticsearch(lucene)的倒排索引中,然后进行查询时,并不会使用分词器对 prefix进行分词,故查询字符串为Ki,是无法匹配到上述文档的,要向匹配到文档,请使用小写的查询ki。这也是ES中的term(词根精确查询)与关系型数据库的一个非常重要的区别。
JAVA代码查询示例:
public static void testTermQuery_prefix() {
RestHighLevelClient client = EsClient.getClient();
try {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(“twitter”);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(
QueryBuilders.prefixQuery(“user”, “ki”)
.boost(2.0f)
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
通配符匹配。支持的通配符为_和?。其中_代表任何的字符序列,包含空字符,而?代表任意的单个字符。这种查询需慎重,特别是对于以通配符开头的查询,例如"a"或"?b",因为这种需要遍历整个倒排索引,通常建议使用 “查询字符加通配符”,例如"a"或"a*b"这类。
其查询举例如下:
GET /_search
{
“query”: {
“wildcard” : { “user” : “ki*y” }
}
}
对应的java查询如下:
public static void testWildcardQuery() {
RestHighLevelClient client = EsClient.getClient();
try {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(“esdemo”);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(
QueryBuilders.wildcardQuery(“user”, “ki*il”)
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
正则表达式查询。
模糊匹配,基于词根编辑距离来实现。所谓的词根编辑距离(其术语为: Levenshtein Edit Distance)是指一个词(字符序列)经过多少次单字符的修改能转换为另外一个词的次数。例如 cat --> cak 其编辑距离为1。
支持模糊匹配的查询API,其参数fuzziness可选值:
-
0:表示精确匹配。
-
1:表示允许出现1个编辑距离。
-
2:表示允许出现2个编辑距离。
-
auto:当词根长度小于3时,则精确匹配;当词根长度大于3并且小于6时,允许1个编辑长度的词根匹配;当词根大于等于6后,允许2个编辑距离的词根匹配。默认为auto。
注意:fuzzy query是词根级别的查询,不会对查询字符串进行分析。
fuzziness 允许的编辑距离,默认为AUTO。
prefix_length 词根的前prefix_length个字符不允许出现编辑距离,指一个词根前面的部分必须是精确匹配,因为模糊匹配,一般是用来解决书写错误,或语法(因为的负数)等,前面的字符一般不会书写错误。
-
max_expansions 最大去查找匹配词根的个数,默认为50。
-
transpositions 是否支持位置变化,例如ab-ba,如果transpositions为true,则ab-ba的编辑距离为1,如果transpositions为false,则其编辑距离为2。默认为false。
其JAVA示例如下:
假设es数据库中缓存有如下文档:{“message”:“ab and hell”}
/**
- 日期范围查询demo
*/
public static void testFuzzyQuery() {
RestHighLevelClient client = EsClient.getClient();
try {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(“twitter”);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(
QueryBuilders.fuzzyQuery(“message”, “ba”)
.transpositions(true)
.fuzziness(Fuzziness.ONE) // 如果transpositions设置为false,则无法匹配到上述文档,如果将fuzziness(Fuzziness.TWO),则
// 可匹配到文档,但如果transpositions=true,并且fuzziness(Fuzziness.ONE)同样可匹配到文档。
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
总结:心得体会
既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。
学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。
面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。
最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。
大厂Java架构核心笔记(适合中高级程序员阅读):
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
…(img-umy44pLz-1713345706126)]
[外链图片转存中…(img-7RoWk7D0-1713345706126)]
[外链图片转存中…(img-NRQwqdWV-1713345706127)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
总结:心得体会
既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。
学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。
面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。
最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。
大厂Java架构核心笔记(适合中高级程序员阅读):
[外链图片转存中…(img-PhjsmYQv-1713345706127)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!