|
| 1 | +## Elasticsearch 基本概念 |
| 2 | + |
| 3 | +Index:Elasticsearch用来存储数据的逻辑区域,它类似于关系型数据库中的database 概念。一个index可以在一个或者多个shard上面,同时一个shard也可能会有多个replicas。 |
| 4 | + |
| 5 | +Document:Elasticsearch里面存储的实体数据,类似于关系数据中一个table里面的一行数据。 |
| 6 | + |
| 7 | +document由多个field组成,不同的document里面同名的field一定具有相同的类型。document里面field可以重复出现,也就是一个field会有多个值,即multivalued。 |
| 8 | + |
| 9 | +Document type:为了查询需要,一个index可能会有多种document,也就是document type. 它类似于关系型数据库中的 table 概念。但需要注意,不同document里面同名的field一定要是相同类型的。 |
| 10 | + |
| 11 | +Mapping:它类似于关系型数据库中的 schema 定义概念。存储field的相关映射信息,不同document type会有不同的mapping。 |
| 12 | + |
| 13 | +下图是ElasticSearch和关系型数据库的一些术语比较: |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | +Relationnal database | Elasticsearch |
| 18 | +---|--- |
| 19 | +Database| Index |
| 20 | +Table | Type |
| 21 | +Row | Document |
| 22 | +Column | Field |
| 23 | +Schema | Mapping |
| 24 | +Schema | Mapping |
| 25 | +Index | Everything is indexed |
| 26 | +SQL | Query DSL |
| 27 | +SELECT * FROM table… | GET http://… |
| 28 | +UPDATE table SET | PUT http://… |
| 29 | + |
| 30 | + |
| 31 | +## Python Elasticsearch DSL 使用简介 |
| 32 | + |
| 33 | +连接 Es: |
| 34 | + |
| 35 | +```python |
| 36 | +import elasticsearch |
| 37 | + |
| 38 | +es = elasticsearch.Elasticsearch([{'host': '127.0.0.1', 'port': 9200}]) |
| 39 | +``` |
| 40 | + |
| 41 | +先看一下搜索,`q` 是指搜索内容,空格对 `q` 查询结果没有影响,`size` 指定个数,`from_` 指定起始位置,`filter_path` 可以指定需要显示的数据,如本例中显示在最后的结果中的只有 `_id` 和 `_type`。 |
| 42 | + |
| 43 | +```python |
| 44 | +res_3 = es.search(index="bank", q="Holmes", size=1, from_=1) |
| 45 | +res_4 = es.search(index="bank", q=" 39225 5686 ", size=1000, filter_path=['hits.hits._id', 'hits.hits._type']) |
| 46 | +``` |
| 47 | + |
| 48 | +查询指定索引的所有数据: |
| 49 | + |
| 50 | +其中,index 指定索引,字符串表示一个索引;列表表示多个索引,如 `index=["bank", "banner", "country"]`;正则形式表示符合条件的多个索引,如 `index=["apple*"]`,表示以 `apple` 开头的全部索引。 |
| 51 | + |
| 52 | +`search` 中同样可以指定具体 `doc-type`。 |
| 53 | + |
| 54 | + |
| 55 | +```python |
| 56 | +from elasticsearch_dsl import Search |
| 57 | + |
| 58 | +s = Search(using=es, index="index-test").execute() |
| 59 | +print s.to_dict() |
| 60 | +``` |
| 61 | + |
| 62 | +根据某个字段查询,可以多个查询条件叠加: |
| 63 | + |
| 64 | +```python |
| 65 | +s = Search(using=es, index="index-test").query("match", sip="192.168.1.1") |
| 66 | +s = s.query("match", dip="192.168.1.2") |
| 67 | +s = s.excute() |
| 68 | +``` |
| 69 | + |
| 70 | +多字段查询: |
| 71 | + |
| 72 | +```python |
| 73 | +from elasticsearch_dsl.query import MultiMatch, Match |
| 74 | + |
| 75 | +multi_match = MultiMatch(query='hello', fields=['title', 'content']) |
| 76 | +s = Search(using=es, index="index-test").query(multi_match) |
| 77 | +s = s.execute() |
| 78 | + |
| 79 | +print s.to_dict() |
| 80 | +``` |
| 81 | + |
| 82 | +还可以用 `Q()` 对象进行多字段查询,`fields` 是一个列表,`query` 为所要查询的值。 |
| 83 | + |
| 84 | + |
| 85 | +```python |
| 86 | +from elasticsearch_dsl import Q |
| 87 | + |
| 88 | +q = Q("multi_match", query="hello", fields=['title', 'content']) |
| 89 | +s = s.query(q).execute() |
| 90 | + |
| 91 | +print s.to_dict() |
| 92 | +``` |
| 93 | + |
| 94 | +`Q()` 第一个参数是查询方法,还可以是 `bool`。 |
| 95 | + |
| 96 | +```python |
| 97 | + |
| 98 | +q = Q('bool', must=[Q('match', title='hello'), Q('match', content='world')]) |
| 99 | +s = s.query(q).execute() |
| 100 | + |
| 101 | +print s.to_dict() |
| 102 | +``` |
| 103 | + |
| 104 | +通过 `Q()` 进行组合查询,相当于上面查询的另一种写法。 |
| 105 | + |
| 106 | +```python |
| 107 | +q = Q("match", title='python') | Q("match", title='django') |
| 108 | +s = s.query(q).execute() |
| 109 | +print(s.to_dict()) |
| 110 | +# {"bool": {"should": [...]}} |
| 111 | + |
| 112 | +q = Q("match", title='python') & Q("match", title='django') |
| 113 | +s = s.query(q).execute() |
| 114 | +print(s.to_dict()) |
| 115 | +# {"bool": {"must": [...]}} |
| 116 | + |
| 117 | +q = ~Q("match", title="python") |
| 118 | +s = s.query(q).execute() |
| 119 | +print(s.to_dict()) |
| 120 | +# {"bool": {"must_not": [...]}} |
| 121 | +``` |
| 122 | + |
| 123 | +过滤,在此为范围过滤,`range` 是方法,`timestamp` 是所要查询的 `field` 名字,`gte` 为大于等于,`lt` 为小于,根据需要设定即可。 |
| 124 | + |
| 125 | +关于 `term` 和 `match` 的区别,`term` 是精确匹配,`match` 会模糊化,会进行分词,返回匹配度分数,(`term` 如果查询小写字母的字符串,有大写会返回空即没有命中,`match` 则是不区分大小写都可以进行查询,返回结果也一样) |
| 126 | + |
| 127 | +```python |
| 128 | +# 范围查询 |
| 129 | +s = s.filter("range", timestamp={"gte": 0, "lt": time.time()}).query("match", country="in") |
| 130 | +# 普通过滤 |
| 131 | +res_3 = s.filter("terms", balance_num=["39225", "5686"]).execute() |
| 132 | +``` |
| 133 | + |
| 134 | +其他写法: |
| 135 | + |
| 136 | +```python |
| 137 | +s = Search() |
| 138 | +s = s.filter('terms', tags=['search', 'python']) |
| 139 | +print(s.to_dict()) |
| 140 | +# {'query': {'bool': {'filter': [{'terms': {'tags': ['search', 'python']}}]}}} |
| 141 | + |
| 142 | +s = s.query('bool', filter=[Q('terms', tags=['search', 'python'])]) |
| 143 | +print(s.to_dict()) |
| 144 | +# {'query': {'bool': {'filter': [{'terms': {'tags': ['search', 'python']}}]}}} |
| 145 | +s = s.exclude('terms', tags=['search', 'python']) |
| 146 | +# 或者 |
| 147 | +s = s.query('bool', filter=[~Q('terms', tags=['search', 'python'])]) |
| 148 | +print(s.to_dict()) |
| 149 | +# {'query': {'bool': {'filter': [{'bool': {'must_not': [{'terms': {'tags': ['search', 'python']}}]}}]}}} |
| 150 | +``` |
| 151 | + |
| 152 | +聚合可以放在查询,过滤等操作的后面叠加,需要加 `aggs`。 |
| 153 | + |
| 154 | +`bucket` 即为分组,其中第一个参数是分组的名字,自己指定即可,第二个参数是方法,第三个是指定的 `field`。 |
| 155 | + |
| 156 | +`metric` 也是同样,`metric` 的方法有 `sum`、`avg`、`max`、`min` 等,但是需要指出的是,有两个方法可以一次性返回这些值,`stats` 和 `extended_stats`,后者还可以返回方差等值。 |
| 157 | + |
| 158 | +```python |
| 159 | +# 实例1 |
| 160 | +s.aggs.bucket("per_country", "terms", field="timestamp").metric("sum_click", "stats", field="click").metric("sum_request", "stats", field="request") |
| 161 | + |
| 162 | +# 实例2 |
| 163 | +s.aggs.bucket("per_age", "terms", field="click.keyword").metric("sum_click", "stats", field="click") |
| 164 | + |
| 165 | +# 实例3 |
| 166 | +s.aggs.metric("sum_age", "extended_stats", field="impression") |
| 167 | + |
| 168 | +# 实例4 |
| 169 | +s.aggs.bucket("per_age", "terms", field="country.keyword") |
| 170 | + |
| 171 | +# 实例5,此聚合是根据区间进行聚合 |
| 172 | +a = A("range", field="account_number", ranges=[{"to": 10}, {"from": 11, "to": 21}]) |
| 173 | + |
| 174 | +res = s.execute() |
| 175 | +``` |
| 176 | +最后依然要执行 `execute()`,此处需要注意,`s.aggs` 操作不能用变量接收(如 `res=s.aggs`,这个操作是错误的),聚合的结果会保存到 `res` 中显示。 |
| 177 | + |
| 178 | + |
| 179 | +排序 |
| 180 | +```python |
| 181 | +s = Search().sort( |
| 182 | + 'category', |
| 183 | + '-title', |
| 184 | + {"lines" : {"order" : "asc", "mode" : "avg"}} |
| 185 | +) |
| 186 | +``` |
| 187 | + |
| 188 | +分页 |
| 189 | +```python |
| 190 | +s = s[10:20] |
| 191 | +# {"from": 10, "size": 10} |
| 192 | +``` |
| 193 | + |
| 194 | +一些扩展方法,感兴趣的同学可以看看: |
| 195 | + |
| 196 | +```python |
| 197 | +s = Search() |
| 198 | + |
| 199 | +# 设置扩展属性使用`.extra()`方法 |
| 200 | +s = s.extra(explain=True) |
| 201 | + |
| 202 | +# 设置参数使用`.params()` |
| 203 | +s = s.params(search_type="count") |
| 204 | + |
| 205 | +# 如要要限制返回字段,可以使用`source()`方法 |
| 206 | +# only return the selected fields |
| 207 | +s = s.source(['title', 'body']) |
| 208 | +# don't return any fields, just the metadata |
| 209 | +s = s.source(False) |
| 210 | +# explicitly include/exclude fields |
| 211 | +s = s.source(include=["title"], exclude=["user.*"]) |
| 212 | +# reset the field selection |
| 213 | +s = s.source(None) |
| 214 | + |
| 215 | +# 使用dict序列化一个查询 |
| 216 | +s = Search.from_dict({"query": {"match": {"title": "python"}}}) |
| 217 | + |
| 218 | +# 修改已经存在的查询 |
| 219 | +s.update_from_dict({"query": {"match": {"title": "python"}}, "size": 42}) |
| 220 | +``` |
| 221 | + |
| 222 | +参考文档: |
| 223 | + |
| 224 | +http://fingerchou.com/2017/08/12/elasticsearch-dsl-with-python-usage-1/ |
| 225 | + |
| 226 | +http://fingerchou.com/2017/08/13/elasticsearch-dsl-with-python-usage-2/ |
| 227 | + |
| 228 | +https://blog.csdn.net/JunFeng666/article/details/78251788 |
0 commit comments