浏览数据 - shuiyuebingdian/ElasticSearch GitHub Wiki

现在,我们已经对基础知识有所了解,让我们尝试在更现实的数据集上工作。我准备了一个有关客户银行帐户信息的虚拟JSON文档样本。每个文档具有以下架构:

{
    "account_number": 0,
    "balance": 16623,
    "firstname": "Bradshaw",
    "lastname": "Mckenzie",
    "age": 29,
    "gender": "F",
    "address": "244 Columbus Place",
    "employer": "Euron",
    "email": "[email protected]",
    "city": "Hobucken",
    "state": "CO"
}

出于好奇,我从www.json-generator.com/中生成了此数据,因此请忽略数据的实际值和语义,因为它们都是随机生成的。
您可以从此处下载示例数据集(accounts.json)。将其解压缩到当前目录,然后将其加载到集群中,如下所示:

curl -H "Content-Type: application/json" -XPOST 'localhost:9200/bank/account/_bulk?pretty&refresh' --data-binary "@accounts.json"
curl 'localhost:9200/_cat/indices?v'

search API

现在让我们从一些简单的搜索开始。 两种基本方式:

  1. 通过REST请求URI发送搜索参数
  2. 通过REST请求主体发送搜索参数
    request主体方法使您可以更具表现力,还可以使用更具可读性的JSON格式定义搜索。我们将尝试一个请求URI方法的示例,但在本教程的其余部分中,我们将仅使用请求主体的方法。

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

GET /bank/_search?q=*&sort=account_number:asc&pretty

让我们首先剖析搜索调用。q=*参数指示Elasticsearch匹配索引中的所有文档。sort=account_number:asc参数指示使用每个文档的account_number字段以升序对结果进行排序。pretty同样,该参数只是告诉Elasticsearch返回漂亮打印的JSON结果。

{
  "took" : 63,
  "timed_out" : false,
  "_shards" : {
	"total" : 5,
	"successful" : 5,
	"failed" : 0
  },
  "hits" : {
	"total" : 1000,
	"max_score" : null,
	"hits" : [ {
	  "_index" : "bank",
	  "_type" : "account",
	  "_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":"[email protected]","city":"Hobucken","state":"CO"}
	}, {
	  "_index" : "bank",
	  "_type" : "account",
	  "_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":"[email protected]","city":"Brogan","state":"IL"}
	}, ...
	]
  }
}

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

  • took – Elasticsearch执行搜索的时间(以毫秒为单位)
  • timed_out –告诉我们搜索是否超时
  • _shards –告诉我们搜索了多少个分片,以及成功/失败的搜索分片的数量
  • hits - 搜索结果
  • hits.total –符合我们搜索条件的文件总数
  • hits.hits –搜索结果的实际数组(默认为前10个文档)
  • hits.sort -结果的排序键(如果按得分排序则丢失)
  • hits._score和max_score-现在暂时忽略这些字段

以下是使用替代请求body方法进行的完全相同的搜索:

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

此处的区别在于q=*,我们将JSON样式的查询请求正文发布到_searchAPI ,而不是传递URI 。我们将在下一部分中讨论此JSON查询。

重要的是要了解,一旦返回搜索结果,Elasticsearch将完全完成请求,并且不会维护任何类型的服务器端资源或在结果中打开游标。这与许多其他平台(例如SQL)形成鲜明对比,在SQL中,您最初可能会先获取部分查询结果,然后如果要获取(或分页)其余内容,则必须连续返回服务器。使用某种状态服务器端游标的结果。

引入查询语言

Elasticsearch提供了一种JSON样式的特定于域的语言,可用于执行查询。这称为Query DSL。查询语言非常全面,乍一看可能令人生畏,但实际学习它的最佳方法是从一些基本示例开始。

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

GET /bank/_search
{
  "query": { "match_all": {} }
}

剖析以上内容,query部分告诉我们查询定义是什么,而该match_all部分只是我们要运行的查询的类型。该match_all查询简单搜索指定索引中的所有文档。
除了query参数外,我们还可以传递其他参数来影响搜索结果。在以上部分的示例中,我们传入 sort,在这里传入size:

GET /bank/_search
{
  "query": { "match_all": {} },
  "size": 1
} 

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

此示例执行a match_all并返回文档11到20:

GET /bank/_search
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}

实现分页搜索结果时,此功能很有用。请注意,如果from未指定,则默认为0。

本示例执行match_all并按帐户余额的降序对结果进行排序,并返回前10个(默认大小)的文档。

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}

执行搜索

现在,我们已经了解了一些基本的搜索参数,下面让我们进一步探讨一下Query DSL。首先让我们看一下返回的文档字段。默认情况下,完整的JSON文档将作为所有搜索的一部分返回。这称为来源(_source搜索结果中的字段)。如果我们不希望返回整个源文档,则可以只返回源中的少数几个字段。

本示例说明了如何从搜索中返回两个字段account_number和balance(在中_source):

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

请注意,上面的示例只是简化了该_source字段。它仍将仅返回一个命名_source的字段,但其中仅balance包含和account_number。

如果您来自SQL背景,则以上内容在概念上与SQL SELECT FROM字段列表有些相似。

现在让我们进入查询部分。以前,我们已经了解了如何使用match_all查询来匹配所有文档。现在让我们介绍一个称为match query的新查询,可以将其视为基本的字段搜索查询(即,针对特定字段或一组字段进行的搜索)。

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

GET /bank/_search
{
  "query": { "match": { "account_number": 20 } }
}

本示例返回地址中包含词“ mill”的所有帐户:

GET /bank/_search
{
  "query": { "match": { "address": "mill" } }
}

本示例返回地址中包含词语“ mill”或“ lane”的所有帐户:

GET /bank/_search
{
  "query": { "match": { "address": "mill lane" } }
}

此示例是match(match_phrase)的一种变体,它返回地址中包含短语“ mill lane”的所有帐户:

GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}

现在让我们介绍一下bool查询。该bool查询使我们可以使用布尔逻辑将较小的查询组合为较大的查询。
本示例组成两个match查询,并返回地址中包含“ mill”和“ lane”的所有帐户:

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

在上面的示例中,该bool must子句指定了将匹配必须所有条件为true的的文档。

相反,此示例组成两个match查询,并返回地址中包含“ mill”或“ lane”的所有帐户:

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

在上面的示例中,该bool should子句指定了一个查询列表,对于将匹配其中任一查询都为true的文档。

本示例组成两个match查询,并返回地址中既不包含“ mill”也不包含“ lane”的所有帐户:

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

在上面的示例中,该bool must_not子句指定了一个查询列表,对于被视为匹配的文档,没有一个查询列表必须为真。

我们可以在bool查询中同时组合must,should和must_not子句。此外,我们可以在任何这些bool子句中编写查询,以模仿任何复杂的多级布尔逻辑。

此示例返回40岁但未使用ID(aho)的任何人的所有帐户:

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

执行过滤器

在上一节中,我们跳过了一个称为文档评分(_score搜索结果中的字段)的小细节。分数是一个数值,是文档与我们指定的搜索查询匹配程度的相对度量。得分越高,文档越相关,得分越低,文档越不相关。

但是查询并不总是需要产生分数,特别是当它们仅用于“过滤”文档集时。Elasticsearch会检测到这些情况并自动优化查询执行,以免计算无用的分数。

我们在上一节中介绍的bool查询还支持filter子句,这些子句允许使用查询来限制其他子句将匹配的文档,而无需更改分数的计算方式。作为示例,让我们介绍一下range query,它允许我们按一定范围的值过滤文档。通常用于数字或日期过滤。

本示例使用布尔查询返回所有余额在20000到30000(含)之间的帐户。换句话说,我们要查找余额大于或等于20000且小于或等于30000的帐户。

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

剖析以上内容,布尔查询包含一个match_all查询(查询部分)和一个range查询(过滤器部分)。我们可以将任何其他查询替换为查询和过滤器部分。在上述情况下,范围查询非常有意义,因为落入该范围的文档都“相等”匹配,即,没有文档比另一个文档更相关。

除了match_all,match,bool,和range查询,有很多可用的其他查询类型的。

执行聚合

汇总功能可以对数据进行分组和提取统计信息。考虑聚合的最简单方法是将其大致等同于SQL GROUP BY和SQL聚合函数。在Elasticsearch中,您可以执行搜索以返回匹配结果,同时在一个响应中返回与匹配结果分开的汇总结果。从某种意义上说,这是非常强大和高效的,您可以运行查询和多次聚合,并一次性获得两个(或任一)操作的结果,从而避免使用简洁的简化API进行网络往返。

首先,此示例按状态对所有帐户进行分组,然后返回按状态计数递减排序(默认)的前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

以及响应(部分显示):

{
  "took": 29,
  "timed_out": false,
  "_shards": {
	"total": 5,
	"successful": 5,
	"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个帐户,其次是TX(德克萨斯州)27个帐户,其次是AL(阿拉巴马州)25个帐户,依此类推。
请注意,我们设置size=0为不显示搜索结果,因为我们只想查看响应中的汇总结果。

在上一个汇总的基础上,此示例按州计算平均帐户余额(同样,仅对按计数降序排列的前十个州):

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

注意我们如何将average_balance聚合嵌套在聚合内部group_by_state。这是所有聚合的通用模式。您可以在聚合中任意嵌套聚合,以从数据中提取所需的枢轴汇总。

基于先前的汇总,现在让我们按降序对平均余额进行排序:

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来使用它。我希望本教程可以使您更好地了解什么是Elasticsearch,更重要的是,可以启发您进一步尝试其其余的出色功能!