elasticsearch中查询进阶(分页,布尔,高亮,聚合),mapping,settings,ignore_above等设置

本文介绍Elasticsearch中的高级查询技巧,包括排序、分页、布尔查询等,并详细解析映射(mappings)的概念及应用,帮助读者更好地理解和使用Elasticsearch。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

💮目录

进阶查询

sort排序

代码:

GET a1/doc/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}

desc是降序排序,asc是升序

结果如下:
image-20210131212249663

注意,并不是所有的数据类型都能排序!!!!!!!**

分页查询

随着数据量的不端增大,查询结果也展示的越来越长,很多时候我们仅仅只是查询几条数据,不用全部显示出来。这个时候就要用到分页查询了。

只要指定的数据量

这个代码的意思是从第0个开始给2个

GET a1/doc/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0,
  "size": 2
}

布尔查询

查询city为河北的

# 查询city是河北的
GET a1/doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "city": "河北"
          }
        }
      ]
    }
  }
}

结果:image-20210131220621232

must关键字–and

查询city是河北的and age为25

# 查询city是河北的and age为25
GET a1/doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "city": "河北"
          }
        },
        {
          "match": {
            "age": 18
          }
        }
      ]
    }
  }
}

结果:

image-20210131220747733

should关键字–or

查询city是河北的 ,或者age为25

# 查询city是河北的 ,或者age为25
GET a1/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "city": "河北"
          }
        },
        {
          "match": {
            "age": 18
          }
        }
      ]
    }
  }
}

结果:

image-20210131220914579

must_not关键字–not

查询city不是河北,age也不是18的

GET a1/doc/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
            "city": "河北"
          }
        },
        {
          "match": {
            "age": 18
          }
        }
      ]
    }
  }
}

结果:

image-20210131221012169

filter关键字–大于小于

查询city是河北和年龄大于18

# 查询city是河北, 年龄大于18
GET a1/doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "city": "河北"
          }
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gt": 18 
          }
        }
      }
    }
  }
}

结果:

image-20210131221124859

查询city是河北和年龄大于等于18

# 查询city是河北,年龄大于等于18
GET a1/doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "city": "河北"
          }
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gte": 18 
          }
        }
      }
    }
  }
}

结果:

image-20210131221243248

小结:

  • must:与关系,相当于关系型数据库中的and
  • should:或关系,相当于关系型数据库中的or
  • must_not:非关系,相当于关系型数据库中的not
  • filter:过滤条件。
  • range:条件筛选范围。
  • gt:大于,相当于关系型数据库中的>
  • gte:大于等于,相当于关系型数据库中的>=
  • lt:小于,相当于关系型数据库中的<
  • lte:小于等于,相当于关系型数据库中的<=

减少输出字段,结果过滤

当我们在获取数据的时候,不太希望所有的数据都展示出来就像select name from user的sql语句一样,可以单独将name字段提取出来,这样个操作的代码是加上_source字段进行筛选。

# 查询city是河北,年龄大于等于18, 查询出来得数据只要city
GET a1/doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "city": "河北"
          }
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gte": 18 
          }
        }
      }
    }
  },
  "_source": ["city"]
}

结果:

image-20210201094132711

高亮查询

当我们想在搜索关键字的时候加高亮显示,我们就可以使用高亮查询,假设我们这里想在描述中查询带有丽质词语的数据,并且加上高亮

GET a1/doc/_search
{
  "query": {
    "match": {
      "desc": "丽质"
    }
  },
  "_source": ["desc"],
  "highlight": {
    "fields": {
      "desc":{}
    }
  }
}

结果:

image-20210201095036313

都给套上了<em></em>标签

自己加样式

要是想自己设定样式可以这么做:

GET a1/doc/_search
{
  "query": {
    "match": {
      "desc": "丽质"
    }
  },
  "_source": ["desc"],
  "highlight": {
    "pre_tags": "<b style='color :grenn'>", 
    "post_tags": "</b>", 
    "fields": {
      "desc":{}
    }
  }
}


结果是:
image-20210201095436740

聚合函数

最大最小求平均就可以使用聚合函数

求平均年龄(avg),其中的my_avg就相当与一个别名
# 求平均年龄
GET a1/doc/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "my_avg": {
      "avg": {
        "field": "age"
      }
    }
  }
}
求最大的年龄值(max):
GET a1/doc/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "NAME": {
      "max": {
        "field": "age"
      }
    }
  },
  "size": 0
}

image-20210201100245988

求和(sum):

求年龄的和

GET a1/doc/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "my_sum": {
      "sum": {
        "field": "age"
      }
    }
  },
  "size": 0
}

结果:

image-20210201100530263

分组查询(range)左包右不包

现在想要查询的所有人的年龄段,并且按照15-20,20-25,25,30分组,并且算出每组的平均年龄

GET a1/doc/_search
{
  "size": 0,
  "query": {
    "match_all": {}
  },
  "aggs": {
    "avg_group": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 15,
            "to": 25
          },
          {
            "from": 25,
            "to":30
          },
          {
            "from": 30,
            "to": 35
          }
        ]
      }
    }
  }
}

结果:

image-20210201101201897

Mapping

我们已经自由奔放够了!

我们应该知道,在关系型数据库中,必须先定义表结构,才能插入数据,并且,表结构不会轻易改变。而我们呢,我们怎么玩elasticsearch的呢?

所以,接下来,我们研究一下mappings这个小老弟,到底是怎么回事!

映射是什么?

其实,映射mappings没那么神秘!说白了,就相当于原来由elasticsearch自动帮我们定义表结构。现在,我们要自己来了,旨在创建索引的时候,有更多定制的内容,更加的贴合业务场景。OK,坐好了,开车!
elasticsearch中的映射用来定义一个文档及其包含的字段如何存储和索引的过程。例如,我们可以使用映射来定义:

  • 哪些字符串应该被视为全文字段。
  • 哪些字段包含数字、日期或者地理位置。
  • 定义日期的格式。
  • 自定义的规则,用来控制动态添加字段的的映射。

身为吃瓜群众的小老弟,不懂没关系,往下走!

映射类型

每个索引都有一个映射类型(这话必须放在elasticsearch6.x版本后才能说,之前版本一个索引下有多个类型),它决定了文档将如何被索引。

映射类型有:

  • 元字段(meta-fields):元字段用于自定义如何处理文档关联的元数据,例如包括文档的_index_type_id_source字段。
  • 字段或属性(field or properties):映射类型包含与文档相关的字段或者属性的列表。

还不懂,没关系,继续往下走!

字段的数据类型
  • 简单类型,如文本(text)、关键字(keyword)、日期(date)、整形(long)、双精度(double)、布尔(boolean)或ip
  • 可以是支持JSON的层次结构性质的类型,如对象或嵌套。
  • 或者一种特殊类型,如geo_pointgeo_shapecompletion

为了不同的目的,以不同的方式索引相同的字段通常是有用的。例如,字符串字段可以作为全文搜索的文本字段进行索引,也可以作为排序或聚合的关键字字段进行索引。或者,可以使用标准分析器、英语分析器和法语分析器索引字符串字段。

这就是多字段的目的。大多数数据类型通过fields参数支持多字段。

映射约束

在索引中定义太多的字段有可能导致映射爆炸!因为这可能会导致内存不足以及难以恢复的情况,为此。我们可以手动或动态的创建字段映射的数量:

  • index.mapping.total_fields.limit:索引中的最大字段数。字段和对象映射以及字段别名都计入此限制。默认值为1000。
  • index.mapping.depth.limit:字段的最大深度,以内部对象的数量来衡量。例如,如果所有字段都在根对象级别定义,则深度为1.如果有一个子对象映射,则深度为2,等等。默认值为20。
  • index.mapping.nested_fields.limit:索引中嵌套字段的最大数量,默认为50.索引1个包含100个嵌套字段的文档实际上索引101个文档,因为每个嵌套文档都被索引为单独的隐藏文档。

mappings之dynamic的三种状态

前言

一般的,mapping则又可以分为动态映射(dynamic mapping)和静态(显式)映射(explicit mapping)和精确(严格)映射(strict mappings),具体由dynamic属性控制。

动态(“dynamic”:true)

动态的就是默认的,也就是之前的操作都是可以的,我们在es中怎么去设定dynamic的三种形态呢?

# 动态
PUT a2
{
  "mappings": {
    "doc":{
      "dynamic":true,
      "properties":{
        "name":{
          "type":"text"
        },
        "age":{
          "type":"long"
        }
      }
    }
  }
}

通过这行代码我们可以看出当前索引的一些配置

GET a2/_mapping

结果:

{
  "a2" : {
    "mappings" : {
      "doc" : {
        "dynamic" : "true",
        "properties" : {
          "age" : {
            "type" : "long"
          },
          "name" : {
            "type" : "text"
          }
        }
      }
    }
  }
}

可以清楚的看出来"dynamic" : "true"

之前的所有操作都是可以做的,这里就不做演示

静态(“dynamic”:false)

静态索引的建立如下:

PUT a3
{
  "mappings": {
    "doc":{
      "dynamic":false,
      "properties":{
        "name":{
          "type":"text"
        },
        "age":{
          "type":"long"
        }
      }
    }
  }
}

这个时候我们往这个a3索引来添加一些数据对比一下静态与动态的区别

PUT a3/doc/1
{
  "name":"ccyxy",
  "age":19
}

PUT a3/doc/2
{
  "name":"cyx",
  "age":19,
  "city":"河北"
}

PUT a3/doc/3
{
  "age":19,
  "city":"河北"
}

这里添加的数据都是成功的,我们现在来查询一下name:cyx的数据看看

GET a3/doc/_search
{
  "query": {
    "match": {
      "name": "cyx"
    }
  }
}

结果:

image-20210201113753887

是可以将数据查询出来的,并且还包含我们未设定的字段city,那如果我们使用city字段来进行查询呢?

我们来查询一下city:河北的数据

GET a3/doc/_search
{
  "query": {
    "match": {
      "city": "河北"
    }
  }
}

结果:

image-20210201113935429

可以看出结果是没有数据,

可以看到elasticsearch并没有为新增的sex建立映射关系。所以查询不到。
当elasticsearch察觉到有新增字段时,因为dynamic:false的关系,会忽略该字段,但是仍会存储该字段。
在有些情况下,dynamic:false依然不够,所以还需要更严谨的策略来进一步做限制。

严格(“dynamic”:“strict”)

创建代码:

PUT a4
{
  "mappings": {
    "doc":{
      "dynamic":"strict",
      "properties":{
        "name":{
          "type":"text"
        },
        "age":{
          "type":"long"
        }
      }
    }
  }
}

当我们加入创建字段中有的数据时:

PUT a4/doc/1
{
  "name":"ccyxy",
  "age":19
}

这个插入是成功的

当我们要加入未创建字段city时:

PUT a3/doc/2
{
  "name":"cyx",
  "age":19,
  "city":"河北"
}

结果:

image-20210201114444647

可以看出在严格模式下是不可以这么做的

错误提示,严格动态映射异常!说人话就是,当dynamic:strict的时候,elasticsearch如果遇到新字段,会抛出异常。
上述这种严谨的作风洒家称为——严格模式!

小结
  • 动态映射(dynamic:true):动态添加新的字段(或缺省)。
  • 静态映射(dynamic:false):忽略新的字段。在原有的映射基础上,当有新的字段时,不会主动的添加新的映射关系,只作为查询结果出现在查询中。
  • 严格模式(dynamic: strict):如果遇到新的字段,就抛出异常。

一般静态映射用的较多。就像HTMLimg标签一样,src为自带的属性,你可以在需要的时候添加id或者class属性。
当然,如果你非常非常了解你的数据,并且未来很长一段时间不会改变,strict不失为一个好选择。

Mapping的其他设置

index

首先来创建一个mappings

PUT m4
{
  "mappings": {
    "doc": {
      "dynamic": false,
      "properties": {
        "name": {
          "type": "text",
          "index": true
        },
        "age": {
          "type": "long",
          "index": false
        }
      }
    }
  }
}

可以看到,我们在创建索引的时候,为每个属性添加一个index参数。那会有什么效果呢?
先来添加一篇文档:

PUT m4/doc/1
{
  "name": "王莹",
  "age": 18
}

再来查询看效果:

GET m4/doc/_search
{
  "query": {
    "match": {
      "name": "王莹"
    }
  }
}

GET m4/doc/_search
{
  "query": {
    "match": {
      "age": 18
    }
  }
}

name查询没问题,但是,以age作为查询条件就有问题了:

  "error": {
    "root_cause": [
      {
        "type": "query_shard_exception",
        "reason": "failed to create query: {\n  \"match\" : {\n    \"age\" : {\n      \"query\" : 18,\n      \"operator\" : \"OR\",\n      \"prefix_length\" : 0,\n      \"max_expansions\" : 50,\n      \"fuzzy_transpositions\" : true,\n      \"lenient\" : false,\n      \"zero_terms_query\" : \"NONE\",\n      \"auto_generate_synonyms_phrase_query\" : true,\n      \"boost\" : 1.0\n    }\n  }\n}",
        "index_uuid": "GHBPeT5pRnSi3g6DkpIkow",
        "index": "m4"
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "phase": "query",
    "grouped": true,
    "failed_shards": [
      {
        "shard": 0,
        "index": "m4",
        "node": "dhkqLLTsRemm7qEgRdpvTg",
        "reason": {
          "type": "query_shard_exception",
          "reason": "failed to create query: {\n  \"match\" : {\n    \"age\" : {\n      \"query\" : 18,\n      \"operator\" : \"OR\",\n      \"prefix_length\" : 0,\n      \"max_expansions\" : 50,\n      \"fuzzy_transpositions\" : true,\n      \"lenient\" : false,\n      \"zero_terms_query\" : \"NONE\",\n      \"auto_generate_synonyms_phrase_query\" : true,\n      \"boost\" : 1.0\n    }\n  }\n}",
          "index_uuid": "GHBPeT5pRnSi3g6DkpIkow",
          "index": "m4",
          "caused_by": {
            "type": "illegal_argument_exception",
            "reason": "Cannot search on field [age] since it is not indexed."
          }
        }
      }
    ]
  },
  "status": 400
}

返回的是报错结果,这其中就是index参数在起作用。

小结:index属性默认为true,如果该属性设置为false,那么,elasticsearch不会为该属性创建索引,也就是说无法当做主查询条件。

copy_to

现在,再来学习一个copy_to属性,该属性允许我们将多个字段的值复制到组字段中,然后将组字段作为单个字段进行查询。

PUT m5
{
  "mappings": {
    "doc": {
      "dynamic":false,
      "properties": {
        "first_name":{
          "type": "text",
          "copy_to": "full_name"
        },
        "last_name": {
          "type": "text",
          "copy_to": "full_name"
        },
        "full_name": {
          "type": "text"
        }
      }
    }
  }
}

PUT m5/doc/1
{
  "first_name":"tom",
  "last_name":"ben"
}
PUT m5/doc/2
{
  "first_name":"john",
  "last_name":"smith"
}

GET m5/doc/_search
{
  "query": {
    "match": {
      "first_name": "tom"
    }
  }
}

GET m5/doc/_search
{
  "query": {
    "match": {
      "full_name": "tom"
    }
  }
}

上例中,我们将first_namelast_name都复制到full_name中。并且使用full_name查询也返回了结果:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "m5",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 0.2876821,
        "_source" : {
          "first_name" : "tom",
          "last_name" : "ben"
        }
      }
    ]
  }
}

返回结果表示查询成功。那么想要查询tom或者smith该怎么办?

GET m5/doc/_search
{
  "query": {
    "match": {
      "full_name": {
        "query": "tom smith",
        "operator": "or"
      }
    }
  }
}

结果,都是查询到的:

image-20210201163422998

要是将关系转为and

GET m5/doc/_search
{
  "query": {
    "match": {
      "full_name": {
        "query": "tom smith",
        "operator": "and"
      }
    }
  }
}

结果:

image-20210201163514060

copy_to还支持将相同的属性值复制给不同的字段。

PUT m6
{
  "mappings": {
    "doc": {
      "dynamic":false,
      "properties": {
        "first_name":{
          "type": "text",
          "copy_to": "full_name"
        },
        "last_name": {
          "type": "text",
          "copy_to": ["field1", "field2"]
        },
        "field1": {
          "type": "text"
        },
        "field2": {
          "type": "text"
        }
      }
    }
  }
}


PUT m6/doc/1
{
  "first_name":"tom",
  "last_name":"ben"
}
PUT m6/doc/2
{
  "first_name":"john",
  "last_name":"smith"
}

上例中,只需要将copy_to的字段以数组的形式封装即可。无论是通过field1还是field2都可以查询。
小结:

  • copy_to复制的是属性值而不是属性
  • copy_to如果要应用于聚合请将filddata设置为true
  • 如果要将属性值复制给多个字段,请用数组,比如copy_to:["field1", "field2"]

对象属性

现在,有一个个人信息文档如下:

PUT m7/doc/1
{
  "name":"tom",
  "age":18,
  "info":{
    "addr":"北京",
    "tel":"10010"
  }
}

首先,这样嵌套多层的mappings该如何设计呢?

PUT m7
{
  "mappings": {
    "doc": {
      "dynamic": false,
      "properties": {
        "name": {
          "type": "text"
        },
        "age": {
          "type": "text"
        },
        "info": {
          "properties": {
            "addr": {
              "type": "text"
            },
            "tel": {
              "type" : "text"
            }
          }
        }
      }
    }
  }
}

现在如果要以info中的tel为条件怎么写查询语句呢?

GET m7/doc/_search
{
  "query": {
    "match": {
      "info.tel": "10086"
    }
  }
}

上例中,info既是一个属性,也是一个对象,我们称为info这类字段为对象型字段。该对象内又包含addrtel两个字段,如上例这种以嵌套内的字段为查询条件的话,查询语句可以以字段点子字段的方式来写即可。

settings设置

设置主、复制分片

在创建一个索引的时候,我们可以在settings中指定分片信息:

PUT a9
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  }
}

number_of_shards主分片

number_of_replicas副主分片

ignore_above

长度超过ignore_above设置的字符串将不会被索引或存储(个人认为会存储,但不会为该字段建立索引,也就是该字段不能被检索)。 对于字符串数组,ignore_above将分别应用于每个数组元素,并且不会索引或存储比ignore_above更长的字符串元素。

PUT w1
{
  "mappings": {
    "doc":{
      "properties":{
        "t1":{
          "type":"keyword",
          "ignore_above": 5
        },
        "t2":{
          "type":"keyword",
          "ignore_above": 10   
        }
      }
    }
  }
}
PUT w1/doc/1
{
  "t1":"elk",          
  "t2":"elasticsearch"  
}
GET w1/doc/_search   
{
  "query":{
    "term": {
      "t1": "elk"
    }
  }
}

GET w1/doc/_search  
{
  "query": {
    "term": {
      "t2": "elasticsearch"
    }
  }
}

其中"t1": "elk"是可以返回数据的,但是"t2": "elasticsearch"这个并不能返回数据,因为设置了"ignore_above": 10,他已经超过了长度10

①,该字段将忽略任何超过10个字符的字符串。
②,此文档已成功建立索引,也就是说能被查询,并且有结果返回。
③,该字段将不会建立索引,也就是说,以该字段作为查询条件,将不会有结果返回。
④,有结果返回。
⑤,则将不会有结果返回,因为t2字段对应的值长度超过了ignove_above设置的值。

该参数对于防止Lucene的术语字节长度限制也很有用,限制长度是32766
注意,该ignore_above设置可以利用现有的领域进行更新PUT地图API
对于值ignore_above是字符数,但Lucene的字节数为单位。如果您使用带有许多非ASCII字符的UTF-8文本,您可能需要设置限制,32766 / 4 = 8191因为UTF-8字符最多可占用4个字节。
如果我们观察上述示例中,我们可以看到在设置映射类型时,字段的类型是keyword,也就是说ignore_above参数仅针对于keyword类型有用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值