当前位置: 首页 > news >正文

ES知识合集(四):高级篇

实时查询

通常对一个通过上面方法写入到 Elasticsearch 的文档,在默认的情况下并不马上可以进行搜索。这是因为在 Elasticsearch 的设计中,有一个叫做 refresh 的操作。它可以使更改可见以进行搜索的操作。通常会有一个 refresh timer 来定时完成这个操作。这个周期为1秒。这也是我们通常所说的 Elasticsearch 可以实现秒级的搜索。当然这个 timer 的周期也可以在索引的设置中进行配置。如果我们想让我们的结果马上可以对搜索可见,我们可以用如下的方法:

PUT twitter/_doc/1?refresh=true

{

  "user""GB",

  "uid"1,

  "city""Beijing",

  "province""Beijing",

  "country""China"

}

上面的方式可以强制使 Elasticsearch 进行 refresh 的操作,当然这个是有代价的。频繁的进行这种操作,可以使我们的 Elasticsearch 变得非常慢。另外一种方式是通过设置 refresh=wait_for。这样相当于一个同步的操作,它等待下一个 refresh 周期发生完后,才返回。这样可以确保我们在调用上面的接口后,马上可以搜索到我们刚才录入的文档:

PUT twitter/_doc/1?refresh=wait_for

{

  "user""GB",

  "uid"1,

  "city""Beijing",

  "province""Beijing",

  "country""China"

}

查询后更新

在关系数据库中,我们通常是对数据库进行搜索,让后才进行修改。在这种情况下,我们事先通常并不知道文档的 id。我们需要通过查询的方式来进行查询,让后进行修改。ES 也提供了相应的 REST 接口。

要结合脚本才可以实现

POST twitter/_update_by_query

{

  "query": {

    "match": {

      "user""GB"

    }

  },

  "script": {

    "source""ctx._source.city = params.city;ctx._source.province = params.province;ctx._source.country = params.country",

    "lang""painless",

    "params": {

      "city""上海",

      "province""上海",

      "country""中国"

    }

  }

}

存在更新不存在新增

仅在文档事先存在的情况下,我们在前面的代码中看到的部分更新才有效。 如果具有给定 id 的文档不存在,Elasticsearch 将返回一个错误,指出该文档丢失。 让我们了解如何使用更新 API 进行 upsert 操作。 术语 “upsert” 宽松地表示更新或插入,即更新文档(如果存在),否则,插入新文档。

doc_as_upsert 参数检查具有给定ID的文档是否已经存在,并将提供的 doc 与现有文档合并。 如果不存在具有给定 id 的文档,则会插入具有给定文档内容的新文档。

下面的示例使用 doc_as_upsert 合并到 id 为 3 的文档中,或者如果不存在则插入一个新文档:

POST /catalog/_update/3

{

  "doc": {

    "author""Albert Paro",

    "title""Elasticsearch 5.0 Cookbook",

    "description""Elasticsearch 5.0 Cookbook Third Edition",

    "price""54.99"

  },

  "doc_as_upsert"true

}

查询后删除

在关系数据库中,我们通常是对数据库进行搜索,让后才进行删除。在这种情况下,我们事先通常并不知道文档的 id。我们需要通过查询的方式来进行查询,让后进行删除。ES 也提供了相应的 REST 接口。

POST twitter/_delete_by_query

{

  "query": {

    "match": {

      "city""上海"

    }

  }

}

批处理命令_bulk 

因为每一次操作都是一个 REST 请求,对于大量的数据进行操作的话,这个显得比较慢。ES 创建一个批量处理的命令给我们使用。这样我们在一次的 REST 请求中,我们就可以完成很多的操作。这无疑是一个非常大的好处。下面,我们来介绍一下这个 _bulk 命令。

POST _bulk

"index" : { "_index" "twitter""_id"1} }

{"user":"双榆树-张三","message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"北京","province":"北京","country":"中国","address":"中国北京市海淀区","location":{"lat":"39.970718","lon":"116.325747"}}

"index" : { "_index" "twitter""_id"2 }}

{"user":"东城区-老刘","message":"出发,下一站云南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区台基厂三条3号","location":{"lat":"39.904313","lon":"116.412754"}}

"index" : { "_index" "twitter""_id"3} }

{"user":"东城区-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区","location":{"lat":"39.893801","lon":"116.408986"}}

"index" : { "_index" "twitter""_id"4} }

{"user":"朝阳区-老贾","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区建国门","location":{"lat":"39.718256","lon":"116.367910"}}

"index" : { "_index" "twitter""_id"5} }

{"user":"朝阳区-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区国贸","location":{"lat":"39.918256","lon":"116.467910"}}

"index" : { "_index" "twitter""_id"6} }

{"user":"虹桥-老吴","message":"好友来了都今天我生日,好友来了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中国","address":"中国上海市闵行区","location":{"lat":"31.175927","lon":"121.383328"}}

在上面的命令中,我们使用了 bulk 指令来完成我们的操作。在输入命令时,我们需要特别的注意:千万不要添加除了换行以外的空格,否则会导致错误。在上面我们使用的 index 用来创建一个文档。为了说明问题的方便,我们在每一个文档里,特别指定了每个文档的 id。当执行完我们的批处理 bulk 命令后,我们可以看到:

bulk 指令是高效的,因为一个请求就可以处理很多个操作。在实际的使用中,我们必须注意的是:一个好的起点是批量处理 1,000 到 5,000 个文档,总有效负载在 5MB 到 15MB 之间。如果我们的 payload 过大,那么可能会造成请求的失败。

使用create批量创建

POST _bulk

"create" : { "_index" "twitter""_id"1} }

{"user":"双榆树-张三","message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"北京","province":"北京","country":"中国","address":"中国北京市海淀区","location":{"lat":"39.970718","lon":"116.325747"}}

"index" : { "_index" "twitter""_id"2 }}

{"user":"东城区-老刘","message":"出发,下一站云南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区台基厂三条3号","location":{"lat":"39.904313","lon":"116.412754"}}

"index" : { "_index" "twitter""_id"3} }

{"user":"东城区-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区","location":{"lat":"39.893801","lon":"116.408986"}}

"index" : { "_index" "twitter""_id"4} }

{"user":"朝阳区-老贾","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区建国门","location":{"lat":"39.718256","lon":"116.367910"}}

"index" : { "_index" "twitter""_id"5} }

{"user":"朝阳区-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区国贸","location":{"lat":"39.918256","lon":"116.467910"}}

"index" : { "_index" "twitter""_id"6} }

{"user":"虹桥-老吴","message":"好友来了都今天我生日,好友来了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中国","address":"中国上海市闵行区","location":{"lat":"31.175927","lon":"121.383328"}}

 index 和 create 的区别。index 总是可以成功,它可以覆盖之前的已经创建的文档,但是 create 则不行,如果已经有以那个 id 为名义的文档,就不会成功。

批量删除

POST _bulk

"delete" : { "_index" "twitter""_id"1 }}

批量更新

POST _bulk

"update" : { "_index" "twitter""_id"2 }}

{"doc": { "city""长沙"}}

分页搜索

1.使用from,size分页搜索

  深分页性能会有所降低,并且from, size 最大不能超过 10000 超过10000 ES会直接报错。

例如,如果更改 mapping 并希望将所有现有数据重新索引到新索引中,您可能没有足够的内存来对所有结果进行排序以返回最后一页的数据。

对于大量的数据而言,我们尽量避免使用 from+size 这种方法。这里的原因是 index.max_result_window 的默认值是 10K,也就是说 from+size 的最大值是1万。搜索请求占用堆内存和时间与 from+size 成比例,这限制了内存。假如你想 hit 从 990 到 1000,那么每个 shard 至少需要 1000 个文档:

2.使用scroll分页

GET twitter/_search?scroll=1m

{

  "query": {

    "match": {

      "city""北京"

    }

  },

  "size"2

}

这里的 scroll=1m,表明 Elasticsearch 允许等待的时间是1分钟。如果在一分钟之内,接下来的 scroll 请求没有到达的话,那么当前的请求的上下文将会丢失。

返回的数据是

{

  "_scroll_id" "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAFh8WWUdCVlRMUllRb3UzMkdqb0IxVnZNUQ==",

  "took" 31,

  "timed_out" false,

  "_shards" : {

    "total" 1,

    "successful" 1,

    "skipped" 0,

    "failed" 0

  }

  ...

}

在这里,我们可以看到一个返回的 _scroll_id。这个 _scroll_id 将会被用于接下来的请求。

利用上次请求返回来的 _scroll_id,再次请求以获得下一个 page 的信息:

GET _search/scroll

{

  "scroll""1m",

  "scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAHC8WWUdCVlRMUllRb3UzMkdqb0IxVnZNUQ=="

}

在这里必须指出的是:

  • 这里填写的 scroll_id 是上一个请求返回的值
  • 这个 scroll_id 的有效期是我们在第一次搜索时定义的 1m,也就是1分钟。如果超过了,这个就没有用

如果完成此过程,则需要清理上下文,因为上下文在超时之前仍会占用计算资源。 如下面的屏幕快照所示,您可以使用 scroll_id 参数在 DELETE API 中指定一个或多个上下文:

DELTE_search/scroll

{

  "scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAHC8WWUdCVlRMUllRb3UzMkdqb0IxVnZNUQ=="

}

注意事项:  使用scroll时,数据是一致的,不会实时更新,生成scroll时,数据生成快照,有效期到期之前有新增数据  scroll查询不能感知。      scroll占用内存,当快照不使用时,应当及时清除。     scroll到期后,在java程序中查询会抛出异常,应当及时捕获处理。

3.使用after_search分页(推荐)

为了避免过度使得我们的 cluster 繁忙,通常 Scroll 接口被推荐作为深层次的 scrolling,但是因为维护 scroll 上下文也是非常昂贵的,所以这种方法不推荐作为实时用户请求。search_after 参数通过提供实时 cursor 来解决此问题。 我们的想法是使用上一页的结果来帮助检索下一页。

我们先创建一些测试数据

POST _bulk

"index" : { "_index" "twitter""_id"1} }

{"user":"双榆树-张三""DOB":"1980-01-01""message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"北京","province":"北京","country":"中国","address":"中国北京市海淀区","location":{"lat":"39.970718","lon":"116.325747"}}

"index" : { "_index" "twitter""_id"2 }}

{"user":"东城区-老刘""DOB":"1981-01-01""message":"出发,下一站云南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区台基厂三条3号","location":{"lat":"39.904313","lon":"116.412754"}}

"index" : { "_index" "twitter""_id"3} }

{"user":"东城区-李四""DOB":"1982-01-01""message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区","location":{"lat":"39.893801","lon":"116.408986"}}

"index" : { "_index" "twitter""_id"4} }

{"user":"朝阳区-老贾","DOB":"1983-01-01""message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区建国门","location":{"lat":"39.718256","lon":"116.367910"}}

"index" : { "_index" "twitter""_id"5} }

{"user":"朝阳区-老王","DOB":"1984-01-01""message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区国贸","location":{"lat":"39.918256","lon":"116.467910"}}

"index" : { "_index" "twitter""_id"6} }

{"user":"虹桥-老吴""DOB":"1985-01-01""message":"好友来了都今天我生日,好友来了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中国","address":"中国上海市闵行区","location":{"lat":"31.175927","lon":"121.383328"}}

这里共有6个文档。假设检索第一页的查询如下所示:

GET twitter/_search

{

  "size"2,

  "query": {

    "match": {

      "city""北京"

    }

  },

  "sort": [

    {

      "DOB": {

        "order""asc"

      }

    },

    {

      "user.keyword": {

        "order""asc"

      }

    }

  ]

}

结果为:

{

  "took" 29,

  "timed_out" false,

  "_shards" : {

    "total" 1,

    "successful" 1,

    "skipped" 0,

    "failed" 0

  },

  "hits" : {

    "total" : {

      "value" 5,

      "relation" "eq"

    },

    "max_score" null,

    "hits" : [

      {

        "_index" "twitter",

        "_type" "_doc",

        "_id" "1",

        "_score" null,

        "_source" : {

          "user" "双榆树-张三",

          "DOB" "1980-01-01",

          "message" "今儿天气不错啊,出去转转去",

          "uid" 2,

          "age" 20,

          "city" "北京",

          "province" "北京",

          "country" "中国",

          "address" "中国北京市海淀区",

          "location" : {

            "lat" "39.970718",

            "lon" "116.325747"

          }

        },

        "sort" : [

          315532800000,

          "双榆树-张三"

        ]

      },

      {

        "_index" "twitter",

        "_type" "_doc",

        "_id" "2",

        "_score" null,

        "_source" : {

          "user" "东城区-老刘",

          "DOB" "1981-01-01",

          "message" "出发,下一站云南!",

          "uid" 3,

          "age" 30,

          "city" "北京",

          "province" "北京",

          "country" "中国",

          "address" "中国北京市东城区台基厂三条3号",

          "location" : {

            "lat" "39.904313",

            "lon" "116.412754"

          }

        },

        "sort" : [

          347155200000,

          "东城区-老刘"

        ]

      }

    ]

  }

}

上述请求的结果包括每个文档的 sort 值数组。 这些 sort 值可以与 search_after 参数一起使用,以开始返回在这个结果列表之后的任何文档。 例如,我们可以使用上一个文档的 sort 值并将其传递给 search_after 以检索下一页结果:

GET twitter/_search

{

  "size"2,

  "query": {

    "match": {

      "city""北京"

    }

  },

  "search_after": [

    347155200000,

    "东城区-老刘"

  ],

  "sort": [

    {

      "DOB": {

        "order""asc"

      }

    },

    {

      "user.keyword": {

        "order""asc"

      }

    }

  ]

}

搜索结果为:

{

  "took" 47,

  "timed_out" false,

  "_shards" : {

    "total" 1,

    "successful" 1,

    "skipped" 0,

    "failed" 0

  },

  "hits" : {

    "total" : {

      "value" 5,

      "relation" "eq"

    },

    "max_score" null,

    "hits" : [

      {

        "_index" "twitter",

        "_type" "_doc",

        "_id" "3",

        "_score" null,

        "_source" : {

          "user" "东城区-李四",

          "DOB" "1982-01-01",

          "message" "happy birthday!",

          "uid" 4,

          "age" 30,

          "city" "北京",

          "province" "北京",

          "country" "中国",

          "address" "中国北京市东城区",

          "location" : {

            "lat" "39.893801",

            "lon" "116.408986"

          }

        },

        "sort" : [

          378691200000,

          "东城区-李四"

        ]

      },

      {

        "_index" "twitter",

        "_type" "_doc",

        "_id" "4",

        "_score" null,

        "_source" : {

          "user" "朝阳区-老贾",

          "DOB" "1983-01-01",

          "message" "123,gogogo",

          "uid" 5,

          "age" 35,

          "city" "北京",

          "province" "北京",

          "country" "中国",

          "address" "中国北京市朝阳区建国门",

          "location" : {

            "lat" "39.718256",

            "lon" "116.367910"

          }

        },

        "sort" : [

          410227200000,

          "朝阳区-老贾"

        ]

      }

    ]

  }

}

注意:当我们使用 search_after 时,from 值必须设置为 0 或者 -1。

它与 scroll API 非常相似,但与它不同,search_after 参数是无状态的,它始终针对最新版本的搜索器进行解析。 因此,排序顺序可能会在步行期间发生变化,具体取决于索引的更新和删除。

计数

我们经常会查询我们的索引里到底有多少文档,那么我们可以使用_count重点来查询:

GET twitter/_count

如果我们想知道满足条件的文档的数量,我们可以采用如下的格式:

GET twitter/_count

{

  "query": {

    "match": {

      "city""北京"

    }

  }

}

查询

 

在我们使用 match query 时,默认的操作是 OR,我们可以做如下的查询:

GET twitter/_search

{

  "query": {

    "match": {

      "user": {

        "query""朝阳区-老贾",

        "operator""or"

      }

    }

  }

}

这是因为默认的操作是 or 操作。上面查询的结果是任何文档匹配:“”,“”,“”,“”及“”这5个字中的任何一个将被显示:

"hits" : [

  {

    "_index" "twitter",

    "_type" "_doc",

    "_id" "4",

    "_score" 4.4209847,

    "_source" : {

      "user" "朝阳区-老贾",

      "message" "123,gogogo",

      "uid" 5,

      "age" 35,

      "city" "北京",

      "province" "北京",

      "country" "中国",

      "address" "中国北京市朝阳区建国门",

      "location" : {

        "lat" "39.718256",

        "lon" "116.367910"

      }

    }

  },

  {

    "_index" "twitter",

    "_type" "_doc",

    "_id" "5",

    "_score" 2.9019678,

    "_source" : {

      "user" "朝阳区-老王",

      "message" "Happy BirthDay My Friend!",

      "uid" 6,

      "age" 50,

      "city" "北京",

      "province" "北京",

      "country" "中国",

      "address" "中国北京市朝阳区国贸",

      "location" : {

        "lat" "39.918256",

        "lon" "116.467910"

      }

    }

  },

  {

    "_index" "twitter",

    "_type" "_doc",

    "_id" "2",

    "_score" 0.8713734,

    "_source" : {

      "user" "东城区-老刘",

      "message" "出发,下一站云南!",

      "uid" 3,

      "age" 30,

      "city" "北京",

      "province" "北京",

      "country" "中国",

      "address" "中国北京市东城区台基厂三条3号",

      "location" : {

        "lat" "39.904313",

        "lon" "116.412754"

      }

    }

  },

  {

    "_index" "twitter",

    "_type" "_doc",

    "_id" "6",

    "_score" 0.4753614,

    "_source" : {

      "user" "虹桥-老吴",

      "message" "好友来了都今天我生日,好友来了,什么 birthday happy 就成!",

      "uid" 7,

      "age" 90,

      "city" "上海",

      "province" "上海",

      "country" "中国",

      "address" "中国上海市闵行区",

      "location" : {

        "lat" "31.175927",

        "lon" "121.383328"

      }

    }

  },

  {

    "_index" "twitter",

    "_type" "_doc",

    "_id" "3",

    "_score" 0.4356867,

    "_source" : {

      "user" "东城区-李四",

      "message" "happy birthday!",

      "uid" 4,

      "age" 30,

      "city" "北京",

      "province" "北京",

      "country" "中国",

      "address" "中国北京市东城区",

      "location" : {

        "lat" "39.893801",

        "lon" "116.408986"

      }

    }

  }

]

我们也可以设置参数 minimum_should_match 来设置至少匹配的 term。比如:

GET twitter/_search

{

  "query": {

    "match": {

      "user": {

        "query""朝阳区-老贾",

        "operator""or",

        "minimum_should_match"3

      }

    }

  }

}

上面显示我们至少要匹配“”,“”,“”,“” 及 “贾” 这5个中的3个字才可以。显示结果:

"hits" : [

  {

    "_index" "twitter",

    "_type" "_doc",

    "_id" "4",

    "_score" 4.4209847,

    "_source" : {

      "user" "朝阳区-老贾",

      "message" "123,gogogo",

      "uid" 5,

      "age" 35,

      "city" "北京",

      "province" "北京",

      "country" "中国",

      "address" "中国北京市朝阳区建国门",

      "location" : {

        "lat" "39.718256",

        "lon" "116.367910"

      }

    }

  },

  {

    "_index" "twitter",

    "_type" "_doc",

    "_id" "5",

    "_score" 2.9019678,

    "_source" : {

      "user" "朝阳区-老王",

      "message" "Happy BirthDay My Friend!",

      "uid" 6,

      "age" 50,

      "city" "北京",

      "province" "北京",

      "country" "中国",

      "address" "中国北京市朝阳区国贸",

      "location" : {

        "lat" "39.918256",

        "lon" "116.467910"

      }

    }

  }

]

我们也可以改为 and 操作:

GET twitter/_search

{

  "query": {

    "match": {

      "user": {

        "query""朝阳区-老贾",

        "operator""and"

      }

    }

  }

}

显示的结果是:

"hits" : [

  {

    "_index" "twitter",

    "_type" "_doc",

    "_id" "4",

    "_score" 4.4209847,

    "_source" : {

      "user" "朝阳区-老贾",

      "message" "123,gogogo",

      "uid" 5,

      "age" 35,

      "city" "北京",

      "province" "北京",

      "country" "中国",

      "address" "中国北京市朝阳区建国门",

      "location" : {

        "lat" "39.718256",

        "lon" "116.367910"

      }

    }

  }

]

相关文章:

  • Odoo 为特定产品主数据设置质检控制点,以实现在采购收货或生产过程中自动触发质量检查
  • 【Elasticsearch】分词机制详解(含实战案例)
  • Vue动态路由
  • webuploader分片上传示例,服务端上传文件到腾讯云CDN Teo 应用示例
  • 《Elasticsearch 分布式搜索在聊天记录检索中的深度优化》
  • Boost dlib opencv vs2022 C++ 源码安装集成配置
  • Vue3 + TypeScript + Element Plus 使用【设置表格列宽,组合式函数 hook】在原有页面实现表格列宽设置本地持久化实例总结
  • 使用 FastMCP 实现 Word 文档与 JSON 数据互转的 Python 服务
  • C++ RPC 远程过程调用详细解析
  • STM32 vs RT1176:正交编码器实现原理与工程实践全解析
  • [智能客服project] AI提示词配置 | 主协调器 | 闲鱼协议工具
  • 是否需要预先安装 CUDA Toolkit?——按使用场景分级推荐及进阶说明
  • PyCharm 中更改缓存和插件目录
  • 19.vue.js的style的lang=scss、less(2)
  • 函数式编程 stream流 lambda表达式
  • 龟兔赛跑算法(Floyd‘s Cycle-Finding Algorithm)寻找重复数
  • UI设计中的大数据可视化:让数据“说话”
  • NuttX 调度器源码学习
  • OD 算法题 B卷【路灯照明II】
  • C++编程语言
  • 做qq群头像网站/百度搜索历史记录
  • asp动态网站开发实例/seo优化工作有哪些
  • 网站的角色设置如何做/网络运营推广具体做什么工作
  • 哈尔滨做网站哪里好/百度网络电话
  • dreamware做网站首页/江苏seo推广
  • 网站建设威客平台/外链网站