Py学习  »  Elasticsearch

elasticsearch(二)---基本数据操作

用户昵称不合法 • 5 年前 • 375 次点击  
阅读 15

elasticsearch(二)---基本数据操作

修改数据

Elasticsearch 提供了近实时的数据操作和搜索功能。默认情况下, 您可以期望从索引/更新/删除数据的时间到一个秒延迟 (刷新间隔), 直到显示在搜索结果中的时间。这是与 SQL 类似的其他平台的重要区别, 其中数据在事务完成后立即可用。

索引、替换文档

索引单个文档。让我们再次记住该命令:

PUT /customer/_doc/1?pretty
{
  "name": "John Doe"
}
复制代码

同样, 上述将将指定的文档索引为客户索引, ID 为1。如果我们再用不同 (或相同) 的文档再次执行上述命令, Elasticsearch 将替换 (即 reindex) 一个新文档, 上面有一个 ID 为1的文件:

PUT /customer/_doc/1?pretty
{
  "name": "Jane Doe"
}
复制代码

上述更改的文件名称, 其 ID 为1从 "无名氏" 到 "无名氏"。另一方面, 如果我们使用不同的 ID, 则会对新文档进行索引, 并且索引中已经存在的现有文档将保持不变。

PUT /customer/_doc/2?pretty
{
  "name": "Jane Doe"
}
复制代码

上面的索引是一个 ID 为2的新文档。

索引时, ID 部分是可选的。如果未指定, Elasticsearch 将生成随机 ID, 然后使用它对文档进行索引。

实际 ID Elasticsearch 生成 (或在前面的示例中显式指定的任何内容) 作为索引 API 调用的一部分返回。

此示例演示如何索引没有显式 ID 的文档:

POST /customer/_doc?pretty
{
  "name": "Jane Doe"
}
复制代码

请注意, 在上述情况下, 我们使用的是POST而不是PUT, 因为我们没有指定 ID。

更新文档

除了能够索引和替换文档之外, 我们还可以更新文档。注意,每次进行更新时, Elasticsearch 都会删除旧文档, 然后用一次快照将更新应用于该文档, 对其进行索引

本示例演示如何通过将名称字段更改为 "无名氏" 来更新以前的文档 (ID 为 1):

POST /customer/_doc/1/_update?pretty
{
  "doc": { "name": "Jane Doe" }
}
复制代码

本示例演示如何通过将 "名称" 字段更改为 "无名氏", 并同时向其添加 "年龄" 字段来更新我们以前的文档 (ID 1):

POST /customer/_doc/1/_update?pretty
{
  "doc": { "name": "Jane Doe", "age": 20 }
}
复制代码

删除文档

删除文档相当简单。此示例演示如何删除 ID 为2的客户:

DELETE /customer/_doc/2?pretty
复制代码

批量处理

除了能够索引、更新和删除单个文档之外, Elasticsearch 还提供了使用bulk API在批处理中执行上述任何操作的能力。此功能很重要, 因为它提供了一个非常有效的机制, 尽可能快地完成多个操作, 尽可能少使用网络往返。

例如, 以下调用在一个批量操作中索引两个文档 (id 1-无名氏和 id 2-无名氏):

POST /customer/_doc/_bulk?pretty
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
复制代码

本示例更新第一个文档 (id 为 1), 然后在一个批量操作中删除第二个文档 (id 为 2):

POST /customer/_doc/_bulk?pretty
{"update":{"_id":"1"}}
{"doc": { "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}
复制代码

注意, 对于删除操作, 在它之后没有相应的源文档, 因为删除只要求删除文档的 ID。

由于其中一个操作失败, 批量 API 不会失败。如果单个操作因任何原因而失败, 它将继续处理其后面的其余操作。当批量 API 返回时, 它将为每个操作提供一个状态 (与发送的顺序相同), 以便您可以检查特定操作是否失败。

浏览数据

现在, 我们已经看到了基本知识, 让我们尝试一个更现实的数据集。我已经准备了一个关于客户银行帐户信息的虚拟 JSON 文档示例。每个文档都具有以下架构:

{
    "account_number": 0,
    "balance": 16623,
    "firstname": "Bradshaw",
    "lastname": "Mckenzie",
    "age": 29,
    "gender": "F",
    "address": "244 Columbus Place",
    "employer": "Euron",
    "email": "bradshawmckenzie@euron.com",
    "city": "Hobucken",
    "state": "CO"
}
复制代码

搜索API

现在让我们从一些简单的搜索开始。运行搜索有两种基本方法: 一种是通过rest 请求 URI发送搜索参数, 另一种是通过rest 请求正文发送。请求正文方法允许您更具表现力, 也可以用更可读的 JSON 格式定义搜索。

我们将尝试一个请求 URI 方法的示例, 但对于本教程的其余部分, 我们将专门使用请求正文方法。

用于搜索的 REST API 可从端点访问。本示例返回银行索引中的所有文档:_search

GET /bank/_search?q=*&sort=account_number:asc&pretty
复制代码

让我们先解剖一下上面的查询语句。我们在bank索引中搜索 (端点), q=*参数指示 Elasticsearch 匹配索引中的所有文档。sort=account_number:asc该参数指示使用每个文档的字段升序对结果进行排序。pretty该参数, 只是告诉 Elasticsearch 返回漂亮的打印 JSON 结果。

响应 (部分显示):

{
  "took" : 63,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1000,
    "max_score" : null,
    "hits" : [ {
      "_index" : "bank",
      "_type" : "_doc",
      "_id" : "0",
      "sort": [0],
      "_score" : null,
      "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"}
    }, {
      "_index" : "bank",
      "_type" : "_doc",
      "_id" : "1",
      "sort": [1],
      "_score" : null,
      "_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
    }, ...
    ]
  }
}
复制代码

对于响应, 我们看到以下部分:

took-Elasticsearch 执行搜索的时间 (以毫秒为单位)

timed_out–告诉我们搜索超时与否

_shards–告诉我们搜索了多少碎片, 以及成功/失败的搜索碎片的计数

hits–搜索结果

hits.total–与搜索条件匹配的文档总数

hits.hits–实际的搜索结果数组 (默认为前10个文档)

hits.sort-为结果排序键 (如果按分数排序则丢失)

下面是使用备选请求正文方法的相同的精确搜索:




    
GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}
复制代码

这里的区别在于, 我们不是在 URI 中传递, 而是向 API 提供 JSON 样式的查询请求正文。

注意, 当你得到搜索结果, Elasticsearch 已经请求结束。 与许多其他平台 (如 SQL) 不同, SQL最初可能会获得查询结果的部分子集, 然后要使用某种方法(如翻页) 继续返回到服务器再次查询获取其余的结果。
复制代码

引入查询语言

Elasticsearch 提供了一个 JSON 样式的域特定语言, 您可以使用它来执行查询。这称为查询 DSL。查询语言非常全面, 乍一看可能很吓人, 但真正了解它的最好方法是从几个基本示例开始。

回到最后一个示例, 我们执行了以下查询:

GET /bank/_search
{
  "query": { "match_all": {} }
}
复制代码

我们还可以通过其他参数来影响搜索结果。在上面的部分中, 我们通过size设置查询结果数量:

GET /bank/_search
{
  "query": { "match_all": {} },
  "size": 1
}
复制代码

请注意, 如果size未指定, 则默认为10

本示例执行并返回文档10到 19:

GET /bank/_search
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}
复制代码

本示例按帐户余额降序排列结果:

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}
复制代码

执行搜索

既然我们已经看到了一些基本的搜索参数, 让我们在查询 DSL 中多挖掘一些。

让我们先来看看返回的文档字段。默认情况下, 完整的 JSON 文档作为所有搜索的一部分返回。这称为_source源 (搜索命中中的字段)。如果我们不希望返回整个源文档, 我们就有能力请求返回源中的几个字段。

此示例演示如何从搜索中返回两个字段:

GET /bank/_search
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}
复制代码

类似 select account_number,balance from table;

以前, 我们已经看到了如何使用查询来匹配所有文档。现在让我们介绍一个名为 " 匹配查询" 的新查询, 它可以被看作是一个基本的搜索查询 (即针对特定字段或字段集进行的搜索)。

本示例返回编号为20的帐户:

GET /bank/_search
{
  "query": { "match": { "account_number": 20 } }
}
复制代码

本示例返回address中包含 mill 一词的所有帐户:

GET /bank/_search
{
  "query": { "match": { "address": "mill" } }
}
复制代码

本示例返回address中包含 milllane 术语的所有帐户:

GET /bank/_search
{
  "query": { "match": { "address": "mill lane" } }
}
复制代码

此示例是 match 的变体, 它返回地址中包含 mill lane 短语的所有帐户:

GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}
复制代码

现在让我们介绍一下 bool查询。bool查询允许我们使用布尔逻辑将较小的查询组成更大的查询。

本示例构成两个查询, 并返回address中包含 milllane 的所有帐户:

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
复制代码

在上面的示例中, 子句指定bool must要视为匹配的文档必须为 true 的所有查询。

本示例构成两个查询, 并返回address中包含 milllane 的所有帐户:

GET /bank/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
复制代码

在上面的示例中, 子句bool should指定一个查询列表, 其中有一个都是 true, 才能将文档视为匹配项。

本示例构成两个查询, 并返回address中不包含 milllane 的所有帐户:

GET /bank/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
复制代码

本示例返回40岁的人的所有帐户, 但不居住在 ID (阿霍) 中:

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}
复制代码

执行筛选

在上一节中, 我们跳过一个名为_score "文档分数" (搜索结果中的字段) 的小细节。

分数是一个数值, 它是文档与我们指定的搜索查询匹配程度的相对度量值。

分数越高, 文档越相关, 分数越低, 文档越不相关。

但是查询并不总是需要产生分数, 特别是当它们仅用于 "筛选" 文档集时。

Elasticsearch 检测这些情况, 并自动优化查询执行, 以不计算无用的分数。

例如, 让我们介绍范围查询, 它允许我们按一系列值筛选文档。这通常用于数字或日期筛选。

本示例使用 bool 查询返回包含20000和30000之间的余额的所有帐户。换言之, 我们希望找到一个余额大于或等于20000且小于或等于30000的帐户。

GET /bank/_search
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}
复制代码

解剖上面, 布尔查询包含查询 (查询部分) 和查询 (筛选器部分)。在上述情况下, 范围查询是完全无意义的, 因为只要在这个范围内的文件,他们的分数应该相同,没有谁比谁更相关。所以,我们可以不用关心他们的文档分数,从而使用filter筛选文档。

执行聚合

聚合提供了从数据中分组和提取统计信息的能力。考虑聚合的最简单方法是大致将它等同于 sql group和 sql 聚合函数。在 Elasticsearch 中, 您有能力执行返回命中的搜索, 同时返回聚合结果, 并在一个响应中与命中全部分开。这是非常强大和高效的, 在这个意义上, 您可以运行查询和多个聚合, 并获得两个 (或两个以上) 操作的结果, 以避免网络往返。

首先, 本示例按状态对所有帐户进行分组, 然后返回按降序 (也为默认值) 排序的前10个:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      }
    }
  }
}
复制代码

在 SQL 中, 上述聚合在概念上类似于:

SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
复制代码

响应 (部分显示):

{
  "took": 29,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped" : 0,
    "failed": 0
  },
  "hits" : {
    "total" : 1000,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "group_by_state" : {
      "doc_count_error_upper_bound": 20,
      "sum_other_doc_count": 770,
      "buckets" : [ {
        "key" : "ID",
        "doc_count" : 27
      }, {
        "key" : "TX",
        "doc_count" : 27
      }, {
        "key" : "AL",
        "doc_count" : 25
      }, {
        "key" : "MD",
        "doc_count" : 25
      }, {
        "key" : "TN",
        "doc_count" : 23
      }, {
        "key" : "MA",
        "doc_count" : 21
      }, {
        "key" : "NC",
        "doc_count" : 21
      }, {
        "key" : "ND",
        "doc_count" : 21
      }, {
        "key" : "ME",
        "doc_count" : 20
      }, {
        "key" : "MO",
        "doc_count" : 20
      } ]
    }
  }
}
复制代码

我们可以看到, 在 (ID爱达荷州) 有27个帐户, 其次是27帐户 (TX得克萨斯州), 其次是25帐户 (AL阿拉巴马州), 等等。

请注意, 我们设置为不显示搜索命中size=0, 因为我们只希望看到聚合结果在响应中。

本示例计算平均帐户余额 (仅针对按降序排序的前10个状态):

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
复制代码

在上一个聚合的基础上, 现在让我们按降序排序平均余额:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
复制代码

本示例演示如何按年龄(年龄20-29、30-39 和 40-49), 然后按性别分组, 然后最终获得平均帐户余额 (按年龄括号) (按性别分列):

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          },
          {
            "from": 30,
            "to": 40
          },
          {
            "from": 40,
            "to": 50
          }
        ]
      },
      "aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender.keyword"
          },
          "aggs": {
            "average_balance": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      }
    }
  }
}
复制代码

结论

Elasticsearch 是一个简单而复杂的产品。迄今为止, 我们已经了解了它的基本知识、如何查看它的内部以及如何使用一些 REST api 来处理它。


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/xAOlCdGgBI
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/22755
 
375 次点击