一文搞懂Elasticsearch如何搜索数据

目录

    • 前言
    • query string search
    • query DSL

前言

我们都知道Elasticsearch是一个搜索服务器,所以搜索是它最重要的功能,所以下面就介绍几种Elasticsearch中搜索数据的方法


query string search

我们搜索某个类型下的全部数据使用的方法为_search方法,如

GET /索引名/类型名/_search

使用上面的url进行请求,我们就可以得到如下数据

{
  "took" : 103,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "test",
        "_type" : "test_type",
        "_id" : "VyLjvXQBC0UiotsuDD-X",
        "_score" : 1.0,
        "_source" : {
          "age" : "18"
        }
      },
      {
        "_index" : "test",
        "_type" : "test_type",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "user" : "abc"
        }
      }
    ]
  }
}

响应字段解释:

took:表示搜索耗时,单位为毫秒。

timed_out:表示搜索是否超时。

_shards:表示有多少个分片被搜索了,成功搜索的分片数量、跳过的分片数量以及失败的分片数量。

hits:表示搜索结果。

hits.total:表示搜索到的文档总数量。

hits.hits:表示搜索到的文档数组,默认显示搜索到的前十个文档。

如果我们想搜索某个特定商品,那么就可以使用query string search了。使用query string search很简单,只需要在我们的搜索URL后面添加q参数来指定搜索条件

比如:

搜索用户中为abc的数据,user表示文档中的字段

GET /索引名/类型名/_search?q=user:abc

我们还可以不指定字段,直接搜索相关数据,这样就会搜索全部字段

GET /索引名/类型名/_search?q=abc

我们还可以在搜索条件前加+或者-符合,+是必须包含,-是不包含

query string search一般用于简单的,临时的查询,比如curl,如果查询比较复杂,是不会使用query string search的


query DSL

这是Elasticsearch特定的查询语言,query DSL将json格式的搜索条件放到http request body请求体中,然后发送请求进行搜索,这是Elasticsearch一种比较好的搜索方法,不仅方便构建查询语法,还可以构建各种复杂的语法,实用性较强。

例:

GET /索引/类型/_search

{

    "query" : {

        "match_all" : { }

    },

    "sort": [

        { "age": "desc" }

    ]

}

上面语句表示query表示查询的定义, match_all表示查询指定索引下的所有文档。 sort则是一个排序字段,表示根据 age字段的值降序排列。


DSL其他语法

搜索某个字段是否包含某个字段,使用以下语法

 "query" : {

        "match" : {

            "字段名" : "搜索值"

        }

    }

指定返回个数使用size,在后面指定返回的个数,如果不指定的话,size默认为10。

例:

{

  "query": { "match_all": {} },

  "size": 1

}

分页查询

有了指定返回个数的size,那么我们只需要再指定从第几个文档开始查询,那么就可以实现分页查询了,指定从第几个文档开始查询使用from字段,如果不指定from,则默认值为0。

例:

从第10个文档开始查询,查询10个文档

{

  "query": { "match_all": {} },

  "size": 10,

  "from": 10

}

自定义返回字段

自定义返回字段使用_source字段,默认情况下,查询结果中会返回查询文档的所有字段,如果不需要返回所有字段,则可以自定义返回字段,如下:

{

  "query": { "match_all": {} },

  "_source": ["user","age"]

}

指定查询条件

指定查询条件使用match,我们可以再match中指定需要搜索的字段和值

例:
搜索age为18的文档

{

  "query": { "match": { "age": 18 } }

}

如果我们想搜索多个值,那么在搜索值之间加空格即可

例:
搜索name中包含mm或者yy的文档

{

  "query": { "match": { "name": "mm yy"} }

}

条件查询除了match查询,还有term查询,match查询和term查询的区别在于match查询会将查询值进行分词,然后进行查询,而term查询不会对查询值进行分词

例:

{
  "query": {
    "term": {
      "name":   "mm"
    }
  }
}

terms还有一个方法terms,用于多个查询值的搜索,即满足terms条件中的任意一个或者多个都符合查询条件

例:

{
  "query": {
    "term": {
      "name": [ "aa","bb"]
    }
  }
}

Name中包含aa或者bb或者aa bb的文档都会被输出

短语查询

有时候,一个单词的查询不能满足我们的需求,我们需要查询多个单词组成的短语,短语查询使用match_phrase

例:

{

  "query": { "match_phrase": {  "name": "mm yy"} }

}

注:如果我们使用math,那么表示的是name字段包含mm或者yy的文档,当使用match_phrase时,表示的是查询name包含mm yy的文档

范围搜索

将文档与具有一定范围内字词的字段进行匹配。 Lucene查询的类型取决于字段类型,对于字符串字段,TermRangeQuery,对于数字/日期字段,查询是NumericRangeQuery。

比如我们想查询年龄在18到20之间的文档

{
    "query": {
        "range" : {
            "age" : {
                "gte" : 18,
                "lte" : 20
            }
        }
    }
}

例:

范围查询接受以下参数:

gte:  大于或等于

gt:   大于

lte:  小于或等于

lt:   小于

boost:  设置查询的提升值,默认为1.0


条件查询

很多时候,我们都需要根据文档是否满足我们的条件,满足的才进行输出,这就要使用到ES的Bool查询

Bool查询包括四种子句,must,filter,should,must_not,这里我先介绍must、should、must_not

must

返回的文档必须满足must子句的条件,并且参与计算分值,可以有一个或者多个子句,多个子句相当于and,文档需要满足must中所有子句的条件才能输出

例:
如下查询表示查询name中包含“mm”和“yy”的所有文档

{

  "query": {

    "bool": {

      "must": [

        { "match": { "name": "mm" } },

        { "match": { "name": "yy" } }

      ]

    }

  }

}

should

返回的文档满足should子句中的某个条件。在一个Bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回。minimum_should_match参数定义了至少满足几个子句,在多个子句中,should相当于or

例:
如下查询表示查询name中包含“mm”或者“yy”的所有文档

{

  "query": {

    "bool": {

      "should": [

        { "match": { "name": "mm" } },

        { "match": { "name": "yy" } }

      ]

    }

  }

}

must_not

返回的文档必须不满足must_not定义的条件,如果有多个子句,那么文档必须不满足全部子句的条件才能返回

例:
如下查询表示查询name中不包含“mm”和“yy”的所有文档

{

  "query": {

    "bool": {

      "must_not": [

        { "match": { "name": "mm" } },

        { "match": { "name": "yy" } }

      ]

    }

  }

}

must、should、must_not还可以组合起来使用

例:
如下查询表示查询age为18,state不为1的所有文档

{

  "query": {

    "bool": {

      "must": [

        { "match": { "age": "18" } }

      ],

      "must_not": [

        { "match": { "state": "1" } }

      ]

    }

  }

}

过滤查询

过滤查询就要使用到上面bool查询中的filter子句

我们知道在_search方法返回的结果中有一个_score字段,这个字段表示返回的文档与搜索条件的匹配度,匹配度越高,则得分越多,而且_search方法的默认排序也是按照_score来进行排序的。

但有很多时候我们对这个分数是不需要的,那么这个时候使用filter查询会更好,filter查询不会去计算分值,而且filter查询可以被缓存到内存中,在重复搜索时,速度会比较快,因此效率也就更高一些。

比如,我们搜索年龄18到20的文档数据,就可以使用过滤查询

例:

{

  "query": {

    "bool": {

      "must": { "match_all": {} },

      "filter": {

        "range": {

          "age": {

            "gte": 18,

            "lte": 20

          }

        }

      }

    }

  }

}


聚合查询

在MYSQL等一些聚合查询,我们都使用过GROUP BY聚合函数。而ES的聚合操作和SQL中的GROUP BY分组函数类似,都提供了分组和数理统计的能力

以下是一个最简单的聚合查询,表示根据年龄分组,并按照数量进行排序,数量越多排序越先

ES中的聚合API的调用格式如下:

"aggregations" : {                  // 表示聚合操作,可以使用aggs替代
    "" : {        // 聚合名,可以是任意的字符串。用做响应的key,便于快速取得正确的响应数据。
        "" : {    // 聚合类别,就是各种类型的聚合,如min、terms等
            <aggregation_body>      // 聚合体,不同的聚合有不同的body
        }
        [,"aggregations" : { [<sub_aggregation>]+ } ]? // 嵌套的子聚合,可以有0或多个
    }
    [,"" : { ... } ]* // 另外的聚合,可以有0或多个
}

例:

POST my_index/_search

{
  "size": 1,
  "aggs": {
    "group_by_age": {
      "terms": {
        "field": "age"
      }
    }
  }
  
}

aggs字段表示一个聚合操作,group_by_age是一个操作的名称,不固定,在上面例子我们按照年龄进行分组,所以取名为group_by_age,当你对其他属性进行分组,则可以改成其他名称,terms用来表示分组,在里面指定分组的字段和值

注:如果size为0,则不显示文档信息,size为显示的文档数量

返回

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 2,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "test",
                "_type": "test_type",
                "_id": "VyLjvXQBC0UiotsuDD-X",
                "_score": 1.0,
                "_source": {
                    "name": "哈哈哈",
                    "id": "123",
                    "age": "18"
                }
            }
        ]
    },
    "aggregations": {
        "group_by_age": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "18",
                    "doc_count": 2
                }
            ]
        }
    }
}

返回参数说明:

hits:返回的文档信息

aggregations:聚合信息

buckets中的key表示聚合值,doc_count表示文档数量

除了分组操作,ES的聚合查询还有avg、max、min、sum等,分别表示求平均、求最大值、求最小值、求和

例:查询name为哈哈哈的平均年龄

{
  "query": {
    "match": {
      "name": "哈哈哈"
    }
  },
  "aggs": {
    "my_avg": {
      "avg": {
        "field": "age"
      }
    }
  },
  "_source": ["name", "age"]
}

查询年龄最大的值

{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "max_age": {
      "max": {
        "field": "age"
      }
    }
  },
  "size": 0
}

第一次执行聚合 操作查询,出现了以下错误

Fielddata is disabled on text fields by default. Set fielddata=true on [age] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead

原因:Elasticsearch 5.x版本以后,对text类型的字段进行排序或者聚合等操作,是用单独的数据结构(fielddata)缓存到内存里的,默认是不开启的,需要单独开启,即如果我们想对text类型的字段进行排序或者聚合,需要将字段的fielddata属性设置为true

开启如下:
//在mapping方法中的properties字段开启某个字段的fielddata属性,my_index为所以名,my_type为类型名

PUT my_index/_mapping/my_type
{

   "properties":{
        "age":{
            "type":"text",
            "fielddata":true
        }
    }
  
}

返回 acknowledged:true则表示开启成功

模糊查询

Elasticsearch 模糊查询 可以使用wildcard(通配符查询)、regexp(正则查询)、prefix(前缀查询)。这里主要讲一下wildcard(通配符查询)、prefix(前缀查询)

POST 索引名/类型名(可有可无)/_search

{
  "query": {
    "wildcard": {
      "file_name": {
        "value": "*测试*"
      }
    }
  }
}

wildcard 通配符查询是使用通配符来进行查询,? 匹配任意一个字符, * 匹配 0 或多个字符

如上面的测试可以匹配所有file_name中包含测试的文档

prefix(前缀查询)

匹配包含具有指定前缀的项(not analyzed)的字段的文档。前缀查询对应 Lucene 的 PrefixQuery 。

查询name中以哈为前缀的文档

{
   "query": {
    "prefix": {
      "name":  "哈"
    }
  }
}

注意:模糊查询的性能都比较差,当查询内容较多,数据量较大时建议将该字段设置成text进行分词,然后通过match进行匹配。

你可能感兴趣的:(ES,1024程序员节,elasticsearch,数据库)