当前位置:   article > 正文

Elasticsearch (ES) 搜索引擎: 搜索功能:搜索分页、搜索匹配、全文搜索、搜索建议、字段排序_es搜索引擎如何实现搜索功能

es搜索引擎如何实现搜索功能

原文链接:https://xiets.blog.csdn.net/article/details/132348920

版权声明:原创文章禁止转载

专栏目录:Elasticsearch 专栏(总目录)

ES 搜索 API 官网文档:Search APIs

先创建一个索引,并写入一些文档用于搜索示例:

PUT /hotel                                      // 酒店索引
{
    "mappings": {
        "properties": {
            "name": {                           // 名称
                "type": "text",
                "fields": {
                    "keyword_name": {
                        "type": "keyword"
                    }
                }
            },
            "price": {                          // 价格
                "type": "double"
            },
            "decoration_date": {                // 装修日期
                "type": "date",
                "format": "yyyy-MM-dd"
            },
            "international": {                  // 是否国际酒店 (国际酒店可以接待外宾)
                "type": "boolean"
            },
            "location": {                       // 地理位置坐标
                "type": "geo_point"
            },
            "tag": {                            // 标签
                "type": "keyword"
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

写入一些文档示例:

PUT /hotel/_doc/001
{
    "name": "龙门国际大酒店",
    "price": 300.00,
    "decoration_date": "2024-06-06",
    "international": true,
    "location": {
        "lat": 22.5377084,
        "lon": 113.9308322
    },
    "tag": ["WIFI", "停车场", "冰箱", "微波炉", "洗衣机"]
}

PUT /hotel/_doc/002
{
    "name": "龙门精选假日酒店",
    "price": 200.00,
    "decoration_date": "2023-05-04",
    "international": true,
    "location": {
        "lat": 22.531667,
        "lon": 113.9497277
    },
    "tag": ["WIFI", "停车场", "微波炉", "洗衣机"]
}

PUT /hotel/_doc/003
{
    "name": "龙门客栈古风酒店",
    "price": 350.00,
    "decoration_date": "2021-10-08",
    "international": false,
    "location": {
        "lat": 22.53396,
        "lon": 114.0554156
    }
}

PUT /hotel/_doc/004
{
    "name": "悦来时尚宾馆",
    "price": 99.00,
    "decoration_date": "2023-08-08",
    "international": false,
    "location": {
        "lat": 22.5325899,
        "lon": 113.922899
    },
    "tag": ["冰箱", "微波炉", "洗衣机"]
}

PUT /hotel/_doc/005
{
    "name": "悦来文雅大酒店",
    "price": 550.00,
    "decoration_date": "2020-11-11",
    "international": true,
    "location": {
        "lat": 22.4829366,
        "lon": 114.0913511
    },
    "tag": ["WIFI", "停车场"]
}

PUT /hotel/_doc/006
{
    "name": "烟雨楼文雅假日酒店",
    "price": 600.00,
    "decoration_date": "2024-12-12",
    "international": false,
    "location": {
        "lat": 22.9266059,
        "lon": 113.8363914
    },
    "tag": ["WIFI", "微波炉", "洗衣机"]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

1. 搜索辅助

1.1 返回部分字段

官网API:The _source option

搜索结果中的文档数据封装在响应的 _source 字段中,搜索时可以只返回指定文档字段,请求格式:

POST /<index>/_search
{
    "_source": "field1",                // 指定返回的文档字段 (单个字段)
    "query": {
        // ... 查询条件
    }
}

POST /<index>/_search
{
    "_source": ["field1", "field2"],    // 指定返回的文档字段 (多个字段)
    "query": {
        // ... 查询条件
    }
}

POST /<index>/_search
{
    "_source": ["prefix*", "*suffix", , "abc*def"],     // 支持通配符(*)模式
    "query": {
        // ... 查询条件
    }
}

POST /<index>/_search
{
    "_source": false,                   // 不返回任何文档字段 (文档ID、索引名词 等相关字段还是会返回)
    "query": {
        // ... 查询条件
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

返回部分字段搜索示例:

POST /hotel/_search
{
    "_source": ["name", "price"],           // 只返回 name 和 price 这两个字段
    "query": {
        "term": {                           // 搜索国际酒店
            "international": {
                "value": true
            }
        }
    }
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 0.6931471,
        "hits": [
            {
                "_index": "hotel",
                "_id": "001",
                "_score": 0.6931471,
                "_source": {
                    "name": "龙门国际大酒店",
                    "price": 300
                }
            },
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 0.6931471,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200
                }
            },
            {
                "_index": "hotel",
                "_id": "005",
                "_score": 0.6931471,
                "_source": {
                    "name": "悦来文雅大酒店",
                    "price": 550
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

1.2 搜索结果匹配数

官网API:Count API

如果搜索数据匹配的结果非常多,需要使用分页返回结果,需要告诉客户端结果文档的总数。ES 的 _count API 可以查询搜索匹配的文档总数量。

_count API 请求格式:

POST /<index>/_count
{
    "query": {
        // ... 查询条件
    }
}

// 返回格式
{
    "count": 123,       // 匹配的文档数量
    "_shards": {        // 匹配文档的分配信息
        // ...
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

1.3 搜索结果分页

官网API:Paginate search results

使用分页搜索,需要告诉 ES 从匹配的文档列表中的第几个文档开始返回,以及最多要返回多少个文档,搜索参数 fromsize 就是分别表示开始返回的文档位置和数量,from 默认值为 0,size 默认值为 10。

分页搜索 请求格式:

POST /<index>/_search
{
    "from": 0,      // 返回结果的起始文档位置
    "size": 20,     // 最多需要返回的文档数量
    "query": {
        // ... 查询条件
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

结果计数与分页请求示例:

POST /hotel/_count
{
    "query": {
        "term": {
            "tag": {    // 搜索有 WIFI 的酒店的数量
                "value": "WIFI"
            }
        }
    }
}

// 返回
{
    "count": 4,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    }
}

POST /hotel/_search
{
    "from": 2,      // 从索引为 2 的位置开始返回
    "size": 1,      // 最多返回 1 个文档
    "query": {
        "term": {
            "tag": {
                "value": "停车场"  // 搜索有停车场的酒店
            }
        }
    }
}

// 返回
{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 0.75783724,
        "hits": [
            {
                "_index": "hotel",
                "_id": "005",
                "_score": 0.75783724,
                "_source": {
                    "name": "悦来文雅大酒店",
                    "price": 550,
                    "decoration_date": "2020-11-11",
                    "international": true,
                    "location": {
                        "lat": 22.4829366,
                        "lon": 114.0913511
                    },
                    "tag": ["WIFI", "停车场"]
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

1.4 搜索请求性能分析

使用 ES 执行一个搜索请求,如果查询条件非常复杂,搜索请求可能响应比较慢。ES 提供了 profile 功能,可以详细列出执行一个搜索请求的细节,以及每一个步骤的耗时,可以帮助用户对查询条件进行性能分析,以便优化查询条件。

启用 profile 功能,只需要在搜索请求中增加 "profile": true 的参数:

POST /<index>/_search
{
    "profile": true,
    "query": {
        // ... 查询条件
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

性能分析搜索请求示例:

POST /hotel/_search
{
    "profile": true,            // 启用性能分析
    "query": {
        "match": {
            "name": "龙门"      // match 搜索不能用 {"value": "keyword"} 的形式提供值, 而是直接提供
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

执行上面的搜索请求后,将返回一段冗长的信息。除了在 hits 节点返回匹配的文档外,主要还返回了一个 profile 节点,该节点详细记录了搜索的过程。从下面的返回信息中可以看出,全文搜索 “龙门”,拆分成了两个子搜索 “name:龙 name:门”,并给出了子搜索结果的详细信息。

完整的搜索响应:

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 1.4251624,
        "hits": [
            {
                "_index": "hotel",
                "_id": "001",
                "_score": 1.4251624,
                "_source": {
                    "name": "龙门国际大酒店",
                    "price": 300,
                    "decoration_date": "2024-06-06",
                    "international": true,
                    "location": {
                        "lat": 22.5377084,
                        "lon": 113.9308322
                    },
                    "tag": ["WIFI", "停车场", "冰箱", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 1.3494902,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200,
                    "decoration_date": "2023-05-04",
                    "international": true,
                    "location": {
                        "lat": 22.531667,
                        "lon": 113.9497277
                    },
                    "tag": ["WIFI", "停车场", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "003",
                "_score": 1.3494902,
                "_source": {
                    "name": "龙门客栈古风酒店",
                    "price": 350,
                    "decoration_date": "2021-10-08",
                    "international": false,
                    "location": {
                        "lat": 22.53396,
                        "lon": 114.0554156
                    }
                }
            }
        ]
    },
    "profile": {
        "shards": [
            {
                "id": "[hNLgGgpaQoq_u57-x6tz1A][hotel][0]",
                "searches": [
                    {
                        "query": [
                            {
                                "type": "BooleanQuery",
                                "description": "name:龙 name:门",
                                "time_in_nanos": 459669,
                                "breakdown": {
                                    "set_min_competitive_score_count": 0,
                                    "match_count": 3,
                                    "shallow_advance_count": 0,
                                    "set_min_competitive_score": 0,
                                    "next_doc": 4916,
                                    "match": 418,
                                    "next_doc_count": 3,
                                    "score_count": 3,
                                    "compute_max_score_count": 0,
                                    "compute_max_score": 0,
                                    "advance": 31626,
                                    "advance_count": 2,
                                    "count_weight_count": 0,
                                    "score": 5501,
                                    "build_scorer_count": 4,
                                    "create_weight": 226208,
                                    "shallow_advance": 0,
                                    "count_weight": 0,
                                    "create_weight_count": 1,
                                    "build_scorer": 191000
                                },
                                "children": [
                                    {
                                        "type": "TermQuery",
                                        "description": "name:龙",
                                        "time_in_nanos": 150875,
                                        "breakdown": {
                                            "set_min_competitive_score_count": 0,
                                            "match_count": 0,
                                            "shallow_advance_count": 6,
                                            "set_min_competitive_score": 0,
                                            "next_doc": 0,
                                            "match": 0,
                                            "next_doc_count": 0,
                                            "score_count": 3,
                                            "compute_max_score_count": 6,
                                            "compute_max_score": 13708,
                                            "advance": 1417,
                                            "advance_count": 5,
                                            "count_weight_count": 0,
                                            "score": 3417,
                                            "build_scorer_count": 6,
                                            "create_weight": 60917,
                                            "shallow_advance": 5707,
                                            "count_weight": 0,
                                            "create_weight_count": 1,
                                            "build_scorer": 65709
                                        }
                                    },
                                    {
                                        "type": "TermQuery",
                                        "description": "name:门",
                                        "time_in_nanos": 85873,
                                        "breakdown": {
                                            "set_min_competitive_score_count": 0,
                                            "match_count": 0,
                                            "shallow_advance_count": 6,
                                            "set_min_competitive_score": 0,
                                            "next_doc": 0,
                                            "match": 0,
                                            "next_doc_count": 0,
                                            "score_count": 3,
                                            "compute_max_score_count": 6,
                                            "compute_max_score": 2790,
                                            "advance": 2708,
                                            "advance_count": 5,
                                            "count_weight_count": 0,
                                            "score": 791,
                                            "build_scorer_count": 6,
                                            "create_weight": 36333,
                                            "shallow_advance": 1459,
                                            "count_weight": 0,
                                            "create_weight_count": 1,
                                            "build_scorer": 41792
                                        }
                                    }
                                ]
                            }
                        ],
                        "rewrite_time": 11708,
                        "collector": [
                            {
                                "name": "SimpleTopScoreDocCollector",
                                "reason": "search_top_hits",
                                "time_in_nanos": 22249
                            }
                        ]
                    }
                ],
                "aggregations": [],
                "fetch": {
                    "type": "fetch",
                    "description": "",
                    "time_in_nanos": 87833,
                    "breakdown": {
                        "load_stored_fields": 18042,
                        "load_source": 751,
                        "load_stored_fields_count": 3,
                        "next_reader_count": 2,
                        "load_source_count": 3,
                        "next_reader": 9916
                    },
                    "debug": {
                        "stored_fields": [
                            "_id",
                            "_routing",
                            "_source"
                        ]
                    },
                    "children": [
                        {
                            "type": "FetchSourcePhase",
                            "description": "",
                            "time_in_nanos": 1459,
                            "breakdown": {
                                "process_count": 3,
                                "process": 1167,
                                "next_reader": 292,
                                "next_reader_count": 2
                            },
                            "debug": {
                                "fast_path": 3
                            }
                        },
                        {
                            "type": "StoredFieldsPhase",
                            "description": "",
                            "time_in_nanos": 1792,
                            "breakdown": {
                                "process_count": 3,
                                "process": 1292,
                                "next_reader": 500,
                                "next_reader_count": 2
                            }
                        }
                    ]
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218

1.5 搜索匹配评分分析

官网API:Explain API

当执行搜索请求搜索文档时,搜索引擎会对匹配的文档打分,如果没有指定排序规则,ES 将使用默认的打分算法对文档排序。有时我们需要知道指定搜索条件对某个文档具体的打分详情(包括不匹配时的详情),以便对搜索条件进行优化。ES 提供了 _explain API 可以查看搜索时的匹配详情。

_explain API 请求格式,查询「指定搜索条件」对「某个具体文档」的匹配详情:

POST /<index>/_explain/<doc_id>
{
    "query": {
        // ... 搜索条件
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

_explain API 请求示例:

POST /hotel/_explain/001
{
    "query": {
        "match": {
            "name": "哈哈"
        }
    }
}
// 返回
{
    "_index": "hotel",
    "_id": "001",
    "matched": false,       // 不匹配
    "explanation": {
        "value": 0,
        "description": "no matching term",
        "details": []
    }
}

POST /hotel/_explain/001
{
    "query": {
        "match": {
            "name": "龙门"
        }
    }
}
// 返回
{
    "_index": "hotel",
    "_id": "001",
    "matched": true,
    "explanation": {
        "value": 1.4251624,                 // 匹配总分 (所有子查询匹配得分之和)
        "description": "sum of:",
        "details": [
            {
                "value": 0.7125812,         // 子查询匹配得分
                "description": "weight(name:龙 in 0) [PerFieldSimilarity], result of:",
                "details": [
                    {
                        "value": 0.7125812,
                        "description": "score(freq=1.0), computed as boost * idf * tf from:",
                        "details": [
                            {
                                "value": 2.2,
                                "description": "boost",
                                "details": []
                            },
                            {
                                "value": 0.6931472,
                                "description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
                                "details": [
                                    {
                                        "value": 3,
                                        "description": "n, number of documents containing term",
                                        "details": []
                                    },
                                    {
                                        "value": 6,
                                        "description": "N, total number of documents with field",
                                        "details": []
                                    }
                                ]
                            },
                            {
                                "value": 0.46728975,
                                "description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
                                "details": [
                                    {
                                        "value": 1,
                                        "description": "freq, occurrences of term within document",
                                        "details": []
                                    },
                                    {
                                        "value": 1.2,
                                        "description": "k1, term saturation parameter",
                                        "details": []
                                    },
                                    {
                                        "value": 0.75,
                                        "description": "b, length normalization parameter",
                                        "details": []
                                    },
                                    {
                                        "value": 7,
                                        "description": "dl, length of field",
                                        "details": []
                                    },
                                    {
                                        "value": 7.5,
                                        "description": "avgdl, average length of field",
                                        "details": []
                                    }
                                ]
                            }
                        ]
                    }
                ]
            },
            {
                "value": 0.7125812,
                "description": "weight(name:门 in 0) [PerFieldSimilarity], result of:",
                "details": [
                    {
                        "value": 0.7125812,
                        "description": "score(freq=1.0), computed as boost * idf * tf from:",
                        "details": [
                            {
                                "value": 2.2,
                                "description": "boost",
                                "details": []
                            },
                            {
                                "value": 0.6931472,
                                "description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
                                "details": [
                                    {
                                        "value": 3,
                                        "description": "n, number of documents containing term",
                                        "details": []
                                    },
                                    {
                                        "value": 6,
                                        "description": "N, total number of documents with field",
                                        "details": []
                                    }
                                ]
                            },
                            {
                                "value": 0.46728975,
                                "description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
                                "details": [
                                    {
                                        "value": 1,
                                        "description": "freq, occurrences of term within document",
                                        "details": []
                                    },
                                    {
                                        "value": 1.2,
                                        "description": "k1, term saturation parameter",
                                        "details": []
                                    },
                                    {
                                        "value": 0.75,
                                        "description": "b, length normalization parameter",
                                        "details": []
                                    },
                                    {
                                        "value": 7,
                                        "description": "dl, length of field",
                                        "details": []
                                    },
                                    {
                                        "value": 7.5,
                                        "description": "avgdl, average length of field",
                                        "details": []
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168

1.6 搜索通用格式

搜索请求的通用格式:

POST /<index>/_search
{
    "_source": ["field1", "field2"],        // (可选) 返回部分文档字段, 默认返回所有文档字段
    "profile": true,                        // (可选) 是否开启性能分析, 默认为false
    "from": 0,                              // (可选) 返回结果的起始文档位置, 默认为0
    "size": 20,                             // (可选) 最多需要返回的文档数量, 默认为10
    "query": {                              // 搜索API
        "<query_type>": {   // 搜索类型, "term", "range", "bool", "match", "geo_distance" 等
            // 搜索参数, 不同的搜索类型有不同的参数
        }
    },
    "sort": [               // (可选) 排序逻辑
        // 可以按多个字段排序, 优先按数组靠前的元素字段排序
    ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2. 遍历文档: match_all

官网API:Match all query

match_all 查询用于遍历文档(即查询所有文档)。ES 使用 match_all 查询时,不对文档打分(默认所有文档都满分匹配)。查询所有文档通常需要和分页查询一起使用。

match_all 查询请求格式:

POST /<index>/_search
{
    "from": 0,                  // 返回结果的起始文档位置
    "size": 20,                 // 最多需要返回的文档数量
    "query": {
        "match_all": {          // 查询所有文档
            "boost": 1.0        // 用于减少或增加查询的相关性分数的浮点数, 默认为1.0, match_all 查询所有文档都是满分值(默认为1.0分),
        }                       // 其他查询类型也有 boost 参数, 主要用于由多个子查询组成的复合查询时, 可以提升某个子查询的相关性 (提升得分), 
    }                           // 0-1 之间的值会降低相关性, 默认值为1表示相关性不变, 大于1会提升相关性。
}

POST /_search                   // 如果没有指定索引, 则查询所有索引
{
    "from": 0,
    "size": 20,
    "query": {
        "match_all": {
            "boost": 1.0
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

match_all 查询请求示例:

POST /hotel/_search
{
    "from": 1,
    "size": 3, 
    "query": {
        "match_all": {
            "boost": 1.0
        }
    }
}

// 返回
{
    "took": 0,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 1,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200,
                    "decoration_date": "2023-05-04",
                    "international": true,
                    "location": {
                        "lat": 22.531667,
                        "lon": 113.9497277
                    },
                    "tag": ["WIFI", "停车场", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "003",
                "_score": 1,
                "_source": {
                    "name": "龙门客栈古风酒店",
                    "price": 350,
                    "decoration_date": "2021-10-08",
                    "international": false,
                    "location": {
                        "lat": 22.53396,
                        "lon": 114.0554156
                    }
                }
            },
            {
                "_index": "hotel",
                "_id": "004",
                "_score": 1,
                "_source": {
                    "name": "悦来时尚宾馆",
                    "price": 99,
                    "decoration_date": "2023-08-08",
                    "international": false,
                    "location": {
                        "lat": 22.5325899,
                        "lon": 113.922899
                    },
                    "tag": ["冰箱", "微波炉", "洗衣机"]
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

3. 精准搜索: Term 级别查询

官网API:Term-level queries

ES 针对不同的数据类型提供了丰富的搜索匹配功能,主要包括用于精准匹配的 Term 查询,以及用于分词匹配的 match 查询。

Term 级别查询的类型:

  • termtermsterms_set:等值匹配(全匹配)。
  • range:数值/日期范围匹配。
  • exists:文档字段是否存在指定字段,存在则返回。
  • ids:根据ID查询文档。
  • prefix:匹配文档字段值的前缀。
  • regexp:正则表达式匹配。
  • fuzzy:模糊查询(编辑距离查询)。
  • wildcard:通配符模式匹配。

其中 prefixregexpfuzzywildcard 属于 慢查询,如果 search.allow_expensive_queries 设置为 false,将不会执行慢查询。

3.1 等值匹配: term、terms、terms_set

官网API:

term 查询用于等值匹配查询(全匹配查询),term 匹配的字段类型支持 数值型布尔型日期时间关键字类型 (包括这些类型的数组类型)。terms 查询则是 term 的扩展形式,用于同时查询一个或多个值(多个值之间是逻辑或的关系)。terms_set 查询与 terms 相似,terms_set 表示最少需要匹配给定查询值中的指定数量。

termtermsterms_set 查询的请求格式:

POST /<index>/_search
{
    "query": {
        "term": {                       // term 查询
            "<field>": {                // 要搜索的字段
                "value": "<value>"      // 搜索值
            }
        }
    }
}

POST /<index>/_search
{
    "query": {
        "term": {                       // term 查询
            "<field>": "<value>"        // 直接以 "<field>": "<value>" 的形式提供搜索值, 称为「简短搜索」
        }
    }
}

POST /<index>/_search
{
    "query": {
        "terms": {                                  // terms 查询
            "<field>": ["<value1>", "<value2>"]     // 搜索字段和多个搜索值, 多个值之间是逻辑或的关系 (即只要匹配其中一个即可)
        }
    }
}

POST /<index>/_search
{
    "query": {
        "terms_set": {                                          // terms_set 查询
            "<field>": {                                        // 查询字段
                "terms": ["<value1>", "<value2>"],              // 需要查询的多个字段值
                "minimum_should_match_field": "<num_field>"     // 至少需要匹配多个字段值("terms")中的其中多少个值 (需要指定为整数类型的文档字段名称),
            }                                                   // 也可以用 minimum_should_match_script 字段使用脚本匹配来代替 minimum_should_match_field
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

term 查询请求示例:

POST /hotel/_search
{
    "query": {
        "term": {                           // term 查询
            "name.keyword_name": {          // 搜索 name 字段的 keyword_name 子字段,  
                "value": "龙门精选假日酒店"    // name 字段是 text 类型不能使用 term 查询,
            }                               // 而 name.keyword_name 子字段是 keyword 类型, 可以使用 term 查询。
        }
    }
}

// 返回
{
    "took": 0,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 1.540445,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 1.540445,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200,
                    "decoration_date": "2023-05-04",
                    "international": true,
                    "location": {
                        "lat": 22.531667,
                        "lon": 113.9497277
                    },
                    "tag": ["WIFI", "停车场", "微波炉", "洗衣机"]
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

terms 查询请求示例:

POST /hotel/_search
{
    "query": {
        "terms": {                      // terms 查询
            "tag": ["冰箱", "微波炉"]     // 搜索有 冰箱 或 微波炉 的酒店
        }
    }
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 4,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "hotel",
                "_id": "001",
                "_score": 1,
                "_source": {
                    "name": "龙门国际大酒店",
                    "price": 300,
                    "decoration_date": "2024-06-06",
                    "international": true,
                    "location": {
                        "lat": 22.5377084,
                        "lon": 113.9308322
                    },
                    "tag": ["WIFI", "停车场", "冰箱", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 1,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200,
                    "decoration_date": "2023-05-04",
                    "international": true,
                    "location": {
                        "lat": 22.531667,
                        "lon": 113.9497277
                    },
                    "tag": ["WIFI", "停车场", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "004",
                "_score": 1,
                "_source": {
                    "name": "悦来时尚宾馆",
                    "price": 99,
                    "decoration_date": "2023-08-08",
                    "international": false,
                    "location": {
                        "lat": 22.5325899,
                        "lon": 113.922899
                    },
                    "tag": ["冰箱", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "006",
                "_score": 1,
                "_source": {
                    "name": "烟雨楼文雅假日酒店",
                    "price": 600,
                    "decoration_date": "2024-12-12",
                    "international": false,
                    "location": {
                        "lat": 22.9266059,
                        "lon": 113.8363914
                    },
                    "tag": ["WIFI", "微波炉", "洗衣机"]
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

terms_set 查询请求示例:

// 先创建一个示例索引
PUT /project_lang
{
    "mappings": {
        "properties": {
            "lang_name": {
                "type": "keyword"
            },
            "min_match_count": {
                "type": "integer"
            }
        }
    }
}

// 写入一些文档
PUT /project_lang/_doc/001
{
    "lang_name": ["C", "C++", "Java", "Go"],    // 数组类型的字段
    "min_matche_count": 2                       // 用于 terms_set 查询 lang_name 时, 至少需要匹配给出的查询值数组中的数量
}
PUT /project_lang/_doc/002
{
    "lang_name": ["Go", "Rust", "Java"],
    "min_matche_count": 1
}
PUT /project_lang/_doc/003
{
    "lang_name": ["Python", "Go", "JavaScript"],
    "min_matche_count": 2
}


// terms_set 查询
POST /project_lang/_search
{
    "query": {
        "terms_set": {
            "lang_name": {                                      // 查询字段
              "terms": ["Go", "Python"],                        // 查询值数组
              "minimum_should_match_field": "min_matche_count"  // 使用 min_matche_count 字段的值表示最少需要匹配 terms 中的数量
            }
        }
    }
}
// 查询返回
{
	"took": 3,
	"timed_out": false,
	"_shards": {
		"total": 1,
		"successful": 1,
		"skipped": 0,
		"failed": 0
	},
	"hits": {
		"total": {
			"value": 2,
			"relation": "eq"
		},
		"max_score": 1.5615244,
		"hits": [{
				"_index": "project_lang",
				"_id": "003",
				"_score": 1.5615244,
				"_source": {
					"lang_name": ["Python", "Go", "JavaScript"],    // 查询的是: "terms": ["Go", "Python"]
					"min_matche_count": 2                           // 至少要匹配 ["Go", "Python"] 中的 2 个值
				}
			},
			{
				"_index": "project_lang",
				"_id": "002",
				"_score": 0.18711406,
				"_source": {
					"lang_name": ["Go", "Rust", "Java"],            // 查询的是: "terms": ["Go", "Python"]
					"min_matche_count": 1                           // 至少要匹配 ["Go", "Python"] 中的 1 个值
				}
			}
		]
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

3.2 范围匹配: range

官网API:Range query

range 查询用于范围匹配,一般只用于 数值类型日期类型 等可比较大小的字段类型的查询。

range 查询需要指定查询的字段值边界,有以下请求参数用于指定范围边界:

  • gt:大于;
  • gte:大于等于;
  • lt:小于;
  • lte:小于等于。

range 查询请求格式:

POST /<index>/_search
{
    "query": {
        "ragne": {                  // range 查询
            "<field>": {            // 要查询的字段
                "gt": "value1",     // 范围匹配条件
                "lte": "value2"
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

range 查询请求示例:

POST /hotel/_search
{
    "_source": ["name", "price"],   // 返回部分字段
    "query": {
        "range": {                  // range 查询
            "price": {              // 查询价格在 [200, 300] 之间的酒店
                "gte": 200,
                "lte": 300
            }
        }
    }
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "hotel",
                "_id": "001",
                "_score": 1,
                "_source": {
                    "name": "龙门国际大酒店",
                    "price": 300
                }
            },
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 1,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

3.3 存在匹配: exists

官网API:Exists query

ES 写入文档时,可以只写入部分字段,或者某个字段写入 null 值。而 exists 查询就是用于查询某个字段是否存在。

字段存在(满足exists匹配)的条件有:

  • 值存在且不为null
  • 值是数组,但不是空数组,并且数组元素不全为null

exists 查询请求格式:

POST /<index>/_search
{
    "query": {
        "exists": {                     // exists 查询
            "field": "<field_name>"     // 要查询的字段
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

exists 查询请求示例:

POST /hotel/_search
{
    "_source": ["name", "price", "tag"], 
    "query": {
        "exists": {                     // 查询有 tag 字段值文档
            "field": "tag"
        }
    }
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 5,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "hotel",
                "_id": "001",
                "_score": 1,
                "_source": {
                    "name": "龙门国际大酒店",
                    "price": 300,
                    "tag": ["WIFI", "停车场", "冰箱", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 1,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200,
                    "tag": ["WIFI", "停车场", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "004",
                "_score": 1,
                "_source": {
                    "name": "悦来时尚宾馆",
                    "price": 99,
                    "tag": ["冰箱", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "005",
                "_score": 1,
                "_source": {
                    "name": "悦来文雅大酒店",
                    "price": 550,
                    "tag": ["WIFI", "停车场"]
                }
            },
            {
                "_index": "hotel",
                "_id": "006",
                "_score": 1,
                "_source": {
                    "name": "烟雨楼文雅假日酒店",
                    "price": 600,
                    "tag": ["WIFI", "微波炉", "洗衣机"]
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

3.4 IDs匹配: ids

官网API:IDs

ids 查询直接通过文档ID查询文档,查询格式:

POST /<index>/_search
{
    "query": {
        "ids" : {
            "values" : ["id1", "id2", "id3"]    // 只要匹配其中一个ID即可, 相当于 value IN values 判断
        }
    }
}

// 也可以直接根据ID获取文档: GET /<index>/_doc/<doc_id>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

ids 查询示例:

GET /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "ids" : {
            "values" : ["001", "002"]
        }
    }
}

// 返回
{
    "took": 2, 
    "timed_out": false, 
    "_shards": {
        "total": 1, 
        "successful": 1, 
        "skipped": 0, 
        "failed": 0
    }, 
    "hits": {
        "total": {
            "value": 2, 
            "relation": "eq"
        }, 
        "max_score": 1, 
        "hits": [
            {
                "_index": "hotel", 
                "_id": "001", 
                "_score": 1, 
                "_source": {
                    "name": "龙门国际大酒店"
                }
            }, 
            {
                "_index": "hotel", 
                "_id": "002", 
                "_score": 1, 
                "_source": {
                    "name": "龙门精选假日酒店"
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

3.5 前缀匹配: prefix

官网API:Prefix query

prefix 查询用于查询指定字段中包含特定前缀的文档,前缀查询只能用于查询 关键字(keyword)、文本(text) 和 通配符(wildcard) 类型的字段。

prefix 查询属于慢查询,可以使用 index_prefixes 映射参数加快前缀查询速度。

如果禁用了慢查询(search.allow_expensive_queries 设置为了 false),则不会执行 prefix 查询。但如果启用了 index_prefixes,则会构建一个优化的查询,该查询将不被认为是慢查询。

prefix 查询格式:

POST /<index>/_search
{
    "query": {
        "prefix": {                     // 前缀查询
            "<field>": {                // 查询字段
                "value": "<value>"      // 前缀值, 支持简短查询: "<field>": "<value>"
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

prefix 查询示例:

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "prefix": {
            "name.keyword_name": "龙门"
        }
    }
}

// 返回
{
	"took": 2,
	"timed_out": false,
	"_shards": {
		"total": 1,
		"successful": 1,
		"skipped": 0,
		"failed": 0
	},
	"hits": {
		"total": {
			"value": 3,
			"relation": "eq"
		},
		"max_score": 1,
		"hits": [{
				"_index": "hotel",
				"_id": "001",
				"_score": 1,
				"_source": {
					"name": "龙门国际大酒店"
				}
			},
			{
				"_index": "hotel",
				"_id": "002",
				"_score": 1,
				"_source": {
					"name": "龙门精选假日酒店"
				}
			},
			{
				"_index": "hotel",
				"_id": "003",
				"_score": 1,
				"_source": {
					"name": "龙门客栈古风酒店"
				}
			}
		]
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

3.6 正则表达式匹配: regexp

官网API:Regexp query

regexp 查询将使用正则表达式来匹配文档字段值。regexp 查询只能用于查询 关键字(keyword) 和 文本(text) 类型的字段。

regexp 查询属于慢查询,如果禁用了慢查询(search.allow_expensive_queries 设置为了 false),则不会执行 regexp 查询。

regexp 查询格式:

POST /<index>/_search
{
    "query": {
        "regexp": {                     // 正则表达式查询
            "<field>": {                // 查询的字段
                "value": "<regexp>"     // 正则表达式, 支持简短方式: "<field>": "<regexp>"
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

regexp 查询示例:

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
    "regexp": {
        "name.keyword_name": {
            "value": ".*国际.*"
        }
    }
    }
}

// 返回
{
    "took": 4, 
    "timed_out": false, 
    "_shards": {
        "total": 1, 
        "successful": 1, 
        "skipped": 0, 
        "failed": 0
    }, 
    "hits": {
        "total": {
            "value": 1, 
            "relation": "eq"
        }, 
        "max_score": 1, 
        "hits": [
            {
                "_index": "hotel", 
                "_id": "001", 
                "_score": 1, 
                "_source": {
                    "name": "龙门国际大酒店"
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

3.7 编辑距离查询: fuzzy

官网API:Fuzzy query

fuzzy 查询属于模糊查询,用于搜索与搜索词相似的词的文档,相似的衡量标准使用的编辑距离为 莱文斯坦距离

编辑距离是将一个词语变成另一词语所需的单个字符更改的次数,这里的更改包括:

  • 改变一个字符:box -> fox
  • 删除一个字符:black -> lack
  • 添加一个字符:sic -> sick
  • 交换两个字符:act -> cat

其中上面的每一个更改动作都表示一个编辑距离,例如将 hex 变为 hi,可以经过删掉一个字符x(hex -> he),然后把 e 改为 i(he -> hi),这就表示将 hex 变为 hi 需要经过 2 个编辑距离。

fuzzy 查询只能用于查询 关键字(keyword) 和 文本(text) 类型的字段。

fuzzy 查询属于慢查询,如果禁用了慢查询(search.allow_expensive_queries 设置为了 false),则不会执行 fuzzy 查询。

fuzzy 查询格式:

GET /<index>/_search
{
    "query": {
        "fuzzy": {                      // fuzzy查询
            "<field>": {                // 查询字段
                "value": "<value>",     // 查询值
                "fuzziness": "AUTO",    // (可选) 允许的最大编辑距离, 默认为 "AUTO" 表示自动确定
                "transpositions": true, // (可选) 编辑是否包括两个相邻字符的交换 (ab -> ba), 默认为 true
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

最大编辑距离 fuzziness 的值参考:Fuzziness

fuzzy 查询示例:

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "fuzzy": {
            "name.keyword_name": {
                "value": "门龙洲际大酒店"       // 经过 2 个编辑距离(门和龙交换, 洲改为国)就变为了 "龙门国际大酒店"
            }
        }
    }
}

// 返回
{
    "took": 13, 
    "timed_out": false, 
    "_shards": {
        "total": 1, 
        "successful": 1, 
        "skipped": 0, 
        "failed": 0
    }, 
    "hits": {
        "total": {
            "value": 1, 
            "relation": "eq"
        }, 
        "max_score": 1.100318, 
        "hits": [
            {
                "_index": "hotel", 
                "_id": "001", 
                "_score": 1.100318, 
                "_source": {
                    "name": "龙门国际大酒店"
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

3.8 通配符匹配: wildcard

官网API:Wildcard query

wildcard 通配符查询返回与通配符搜索词匹配的文档。* 用于匹配零个或多个字符,? 用于匹配单个字符。wildcard 查询只能用于查询 关键字(keyword)、文本(text) 和 通配符(wildcard) 类型的字段。

wildcard 查询属于慢查询,如果禁用了慢查询(search.allow_expensive_queries 设置为了 false),则不会执行 wildcard 查询。

wildcard 查询格式:

POST /<index>/_search
{
    "query": {
        "wildcard": {               // 通配符查询
            "<field>": {            // 查询字段
                "value": "<value>"  // 查询值, *匹配零个或多个字符, ?匹配单个字符
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

wildcard 查询示例:

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "wildcard": {
            "name.keyword_name": {
                "value": "*国际?酒店"
            }
        }
    }
}

// 返回
{
    "took": 6, 
    "timed_out": false, 
    "_shards": {
        "total": 1, 
        "successful": 1, 
        "skipped": 0, 
        "failed": 0
    }, 
    "hits": {
        "total": {
            "value": 1, 
            "relation": "eq"
        }, 
        "max_score": 1, 
        "hits": [
            {
                "_index": "hotel", 
                "_id": "001", 
                "_score": 1, 
                "_source": {
                    "name": "龙门国际大酒店"
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

4. 布尔查询: bool

官网API:Boolean query

布尔查询是一种复合查询,可以根据多个普通的子查询使用逻辑与或非组合出满足各种复杂条件的查询。一个布尔查询包含了多个子查询,每一个子查询的结果都是一个布尔值,而多个子查询的结果再根据逻辑与或非的关系又组合成最终的一个布尔值用于最终确定是否匹配文档。布尔查询对文档的打分是按各子查询的匹配程度对文档就行综合打分。

布尔查询中多个子查询结果支持的逻辑组合方式

  • must:逻辑与,必须匹配所有查询条件;
  • should:逻辑或,至少匹配其中一个查询条件;
  • must_not:逻辑非,必须不匹配所有查询条件;
  • filter:逻辑与,必须匹配所有查询条件,但该条件的匹配程度不参与打分计算(相当于不参与打分的must)。

bool 查询请求格式:

POST /<index>/_search
{
    "query": {
        "bool": {                               // 布尔查询, 布尔查询是多个子查询结果的逻辑组合, 
            "must|should|must_not|filter": [    // 所以这里是多个子查询组成的数组, 节点名称表示的是子查询结果的逻辑组合方式。
                {
                    "term|match|range|exists|geo_distance|bool": {
                        // 单个子查询 (支持各种查询类型, 包括 分词匹配的match查询、地理距离查询 和 bool查询)
                    }
                },
                {
                    "term|match|range|exists|geo_distance|bool": {
                        // 子查询
                    }
                }
            ]
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4.1 逻辑与: must 查询

bool 查询的 must 查询示例:

POST /hotel/_search
{
    "_source": ["name", "international"],
    "query": {
        "bool": {                           // bool 查询
            "must": [                       // 查询名称包含“龙门”的国际酒店
                {
                    "match": {
                        "name": "龙门"
                    }
                },
                {
                    "term": {
                        "international": {
                            "value": true
                        }
                    }
                }
            ]
        }
    }
}

// 返回
{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 2.1183095,
        "hits": [
            {
                "_index": "hotel",
                "_id": "001",
                "_score": 2.1183095,
                "_source": {
                    "name": "龙门国际大酒店",
                    "international": true
                }
            },
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 2.0426373,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "international": true
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

4.2 逻辑或: should 查询

bool 查询的 should 查询示例:

POST /hotel/_search
{
    "_source": ["name", "price"],
    "query": {
        "bool": {                       // bool 查询
            "should": [                 // 查询名称包含“假日”或“精选”的酒店
                {
                    "match": {
                        "name": "假日"
                    }
                },
                {
                    "match": {
                        "name": "精选"
                    }
                }
            ]
        }
    }
}

// 返回
{
    "took": 6,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 5.003666,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 5.003666,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200
                }
            },
            {
                "_index": "hotel",
                "_id": "006",
                "_score": 1.9034984,
                "_source": {
                    "name": "烟雨楼文雅假日酒店",
                    "price": 600
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

4.3 逻辑非: must_not 查询

bool 查询的 must_not 查询示例:

POST /hotel/_search
{
    "_source": ["name", "price"],
    "query": {
        "bool": {                       // bool 查询
            "must_not": [               // 搜索名称不包含“国际”, 也不包含“酒店”的酒店
                {
                    "match": {
                        "name": "国际"
                    }
                },
                {
                    "match": {
                        "name": "酒店"
                    }
                }
            ]
        }
    }
}

// 返回
{
    "took": 6,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 0,
        "hits": [
            {
                "_index": "hotel",
                "_id": "004",
                "_score": 0,
                "_source": {
                    "name": "悦来时尚宾馆",
                    "price": 99
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

4.4 过滤条件: filter 查询

bool 查询的 filter 查询示例:

POST /hotel/_search
{
    "_source": ["name", "tag"],
    "query": {
        "bool": {                       // bool 查询
            "must": [                   // 名称必须包含“龙门”
                {
                    "match": {
                        "name": "龙门"
                    }
                }
            ],
            "filter": [                 // 并且必须有停车场 (该条件只过滤, 不参与打分)
                {
                    "term": {
                        "tag": {
                            "value": "停车场"
                        }
                    }
                }
            ]
        }
    }
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1.4251624,
        "hits": [
            {
                "_index": "hotel",
                "_id": "001",
                "_score": 1.4251624,
                "_source": {
                    "name": "龙门国际大酒店",
                    "tag": ["WIFI", "停车场", "冰箱", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 1.3494902,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "tag": ["WIFI", "停车场", "微波炉", "洗衣机"]
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

5. 全文搜索: match

官网API:Full text queries

全文搜索一般指对 文本(text)类型数据 的搜索。与 term、range 等全匹配的结构化搜索不同,全文搜索可以部分匹配。全文搜索首先对搜索词进行分析(分词),根据分析结果再构建出多个子查询。

全文搜索包含 matchmulti_matchmatch_phrasequery_stringsimple_query_string 等查询方式。

5.1 match 查询

官网API:Match query

match 查询是全文搜索的主要方式。

match 查询请求格式:

POST /<index>/_search
{
    "query": {
        "match": {                              // match 搜索
            "<field>": "<query_keyword>"        // 搜索的字段和搜索词, 简短模式
        }
    }
}

POST /<index>/_search
{
    "query": {
        "match": {                              // match 搜索
            "<field>": {                        // 搜索的字段
                "query": "<query_keyword>",     // (必选) 搜索词
                "analyzer": "<analyzer>",       // (可选) 对搜索词分词的分析器, 默认使用创建索引时指定的分析器
                "operator": "OR|AND",           // (可选) 对分词结果匹配的逻辑, "OR"表示只需要匹配其中一个分词, "AND"表示必须匹配所有分词, 默认为"OR"
                "minimum_should_match", "3",    // (可选) 至少需要匹配的分词数量, 可以是 正负整数、正负百分比、整数与百分比组合, 详见API文档
                "boost": 1.0,                   // (可选) 用于减少或增加查询的相关性分数的浮点数, 默认为1.0
                // ...
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

match 查询请求示例:

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "match": {              // match 查询
            "name": "精选假日"
        }
    }
}
// 返回
{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 5.003666,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 5.003666,
                "_source": {
                    "name": "龙门精选假日酒店"
                }
            },
            {
                "_index": "hotel",
                "_id": "006",
                "_score": 1.9034984,
                "_source": {
                    "name": "烟雨楼文雅假日酒店"
                }
            }
        ]
    }
}

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "match": {
            "name": {
                "analyzer": "standard",     // 使用指定分析器 ("standard"就是ES默认的中文分析器)
                "query": "精选假日",
                "operator": "AND"           // 所有的分词都必须匹配
            }
        }
    }
}
// 返回
{
    "took": 5,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 5.003666,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 5.003666,
                "_source": {
                    "name": "龙门精选假日酒店"
                }
            }
        ]
    }
}

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "match": {
            "name": {
                "query": "精选假日",
                "minimum_should_match": "75%"   // 至少需要匹配结果分词的75%
            }
        }
    }
}
// 返回
{
    "took": 4,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
        "value": 1,
            "relation": "eq"
        },
        "max_score": 5.003666,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 5.003666,
                "_source": {
                    "name": "龙门精选假日酒店"
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128

5.2 multi_match 查询

官网API:Multi-match query

multi_matchmatch 查询的多字段版本。multi_match 查询可以使用同一个查询关键词同时查询多个 text 类型的字段(只要其中一个字段有匹配的即可),相当于使用 bool 查询的 should 查询封装了两个 match 子查询。

multi_match 查询请求格式:

POST /<index>/_search
{
    "query": {
        "multi_match": {                            // multi_match 搜索
            "query": "<query_keyword>",             // 查询的搜索词
            "fields": ["<field1>", "<field2>"]      // 查询的字段 (所有字段都必须是 text 类型, 只要有其中一个字段匹配了搜索词即可)
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

搜索示例中只有一个字段是 text 类型,这里不做示例。

5.3 match_phrase 查询

官网API:Match phrase query

match_phrase 查询用于匹配切确的短语或临近的词语,也就是希望不分词,或者分词后中间最多只能间隔指定字数的距离。

match_phrase 查询请求格式:

POST /<index>/_search
{
    "query": {
        "match_phrase": {                   // match_phrase 搜索
            "<field>": "<query_keyword>"    // 查询的字段和搜索词
        }
    }
}

POST /<index>/_search
{
    "query": {
        "match_phrase": {                   // match_phrase 搜索
            "<field>": {                    // 查询的字段
                "query": "<query_keyword>", // 查询的搜索词
                "slop": 2                   // 临近匹配, 分词之间允许的最大字数距离
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

match_phrase 查询请求示例:

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "match_phrase": {                   // match_phrase 查询
            "name": {                       // 查询字段
                "query": "精选假日"          // 查询短语 (不部分匹配)
            }
        }
    }
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 5.0036654,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 5.0036654,
                "_source": {
                    "name": "龙门精选假日酒店"
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

match_phrase 查询请求示例(临近的词语):

POST /hotel/_search
{
    "_source": ["name"], 
    "query": {
        "match_phrase": {               // match_phrase 查询
            "name": {                   // 查询字段
                "query": "文雅酒店",     // 查询短语
                "slop": 1               // 分词后中间最多可以有1个字的距离
            }
        }
    }
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 1.7047069,
        "hits": [
            {
                "_index": "hotel",
                "_id": "005",
                "_score": 1.7047069,
                "_source": {
                    "name": "悦来文雅大酒店"   // "文雅"和"酒店"之间有1个字的距离, 匹配搜索。没有匹配到 "烟雨楼文雅假日酒店", 因为"文雅"和"酒店"之间有2个字的距离。
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

6. 全文搜索: query_string

官网API:Query string query

query_string 查询使用严格的解析器解析查询字符串,并根据解析的结果逻辑查询文档。

query_string 查询使用查询语法(Query string syntax)来解析和分割基于运算符的查询字符串,例如 ANDNOT。在执行搜索之前将独立分析每一个分割的文本(Text analysis)。

使用 query_string 查询可以创建复杂的搜索逻辑,其中包括 通配符、跨多个字段、与或非逻辑关系 等搜索。query_string 查询是语法严格的,如果查询字符串包含任何无效的语法,将返回错误。

注意:由于任何无效的语法都会返回错误,因此不建议使用 query_string 查询。如果不需要支持查询语法,应考虑 match 查询。如果需要查询语法功能,可以使用对语法不太严格的 simple_query_string 查询。

query_string 查询格式:

POST /<index>/_search
{
    "query": {
        "query_string": {
            "query": "<query_syntax>",      // 查询语法
            "default_field": "*",           // (可选) 如果 <query_syntax> 中没有提供查询的字段, 则默认查询 default_field 指定的字段, 支持通配符(*)
            "allow_leading_wildcard": true, // (可选) 如果为true, 则通配符 * 和 ? 允许作为查询字符串的第一个字符, 默认为 true
            "analyze_wildcard": false,      // (可选) 如果为true, 将尝试分析查询字符串中的通配符, 默认为false
            "analyzer": "standard",         // (可选) 使用的分析器, 默认使用创建索引时指定的分析器
            "default_operator": "OR|AND",   // (可选) 对分词结果匹配的默认逻辑, "OR"表示只需要匹配其中一个分词, "AND"表示必须匹配所有分词, 默认为"OR"
            "minimum_should_match": "75%",  // (可选) 至少需要匹配的分词数量, 可以是 正负整数、正负百分比、整数与百分比组合, 详见API文档
            "boost": 1.0,                   // (可选) 用于减少或增加查询结果相关性得分的浮点倍数, 默认为1.0
            "fields": ["<field*>"],         // (可选) 字符串或数组, 要搜索的字段, 支持通配符(*), 使用此参数可以跨多个字段搜索
            // ...
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

query_string 查询示例:

POST /<index>/_search
{
    "query": {
        "query_string": {
            "query": "(ShenZhen City) OR (Big HuaWei)",
            "default_field": "content",
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上述查询将搜查 content 字段中匹配(match) ShenZhen CityBig HuaWei 的文档。

query_string 查询中的 query 查询字符串,使用一套称为“mini-language”的语法。该语法也适用于使用 GET URI 请求来执行搜索的 q 查询参数。

后面将介绍 query_string.query 查询字符串支持的各种语法操作。

6.1 逻辑运算

query_string.query 查询字符串的逻辑运算,支持 ANDORNOT 逻辑运算(可以使用 &&||! 代替),并且支持使用 ( ) 括号分组。

POST /<index>/_search
{
    "query": {
        "query_string": {
            "query": "(ABC OR 123) AND (NOT DEF)",
            "default_field": "content"
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上面的搜索请求表示查询索引文档的 content 字段,需要匹配(match) ABC 或者 123,并且不是 DEF

后面为简单演示,只展示 query_string.query 查询字符串的值。

6.2 字段名称

query_string 查询中,如果没有在 query 查询字符串中指明要查询的字段,则默认查询 default_field 指定的字段,或者查询 fields 指定的多个字段。

query_string 查询也可以直接在 query 查询字符串中指明要查询的字段,语法格式:

POST /<index>/_search
{
    "query": {
        "query_string": {
            "query": "<field>:<string>",   // 指定要查询的 字段 和 查询字符串, 用冒号分割
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

query_string.query 查询字符串取值的各种语法格式:

查询字符串语法(query_string.query)含义
status:active查询 status 字段值包含 active
title:(quick OR brown)查询 title 字段值包含 quickbrown
author:"John Smith"查询的 "John Smith" 字符串使用了 引号,表示匹配的是一个不可分割的 短语(可以分词匹配,但匹配到不分割的结果才返回),匹配 "A John Smith B",但不匹配 "John AB Smith",也不匹配 "Smith John"
first\ name:Alice查询 first name 字段值包含 Alice,字段名称包含空格需要使用\转义。
book.\*:(quick OR brown)字段名称可以使用通配符*(需使用\转义为通配符),匹配的字段包括 book.titlebook.contentbook.date 等。
_exists_:title存在 title 字段,并且具有任何非空值。

6.3 通配符

query_string.query 查询字符串的字段取值也支持通配符,?表示一个字符,*表示0个或多个字符。

通配符查询语法示例:

POST /<index>/_search
{
    "query": {
        "query_string": {
            "query": "qu?ck bro*"
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意:通配符查询可能会使用大量内存并且性能非常糟糕。为了提高效率,纯通配符 filed:* 的查询将被重写为 _exists_:<field> 查询。

6.4 正则表达式

query_string.query 查询字符串的使用 "/" 包括起来表示使用正则表达式匹配,例如:

POST /<index>/_search
{
    "query": {
        "query_string": {
            "query": "name:/joh?n(ath[oa]n)/"
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

6.5 模糊查询

模糊查询使用 ~ 运算符:

quikc~ brwn~ foks~

上面的查询使用 Damerau-Levenshtein distance 编辑距离查找最多 2 次更改的所有查询分词。其中单个字符的插入、删除、替换,或者两个相邻字符的调换,称为 1 次更改(编辑距离为1)。

默认的编辑距离为 2,但编辑距离 1 应该足以捕获 80% 的所有人类的拼写错误,可以指定为:quikc~1

注意:避免将模糊查询与通配符混合在一起。

不支持混合使用模糊和通配符运算符 。混合时,不应用其中一种运算符。例如,你可以搜索 app~1(模糊) 或 app*(通配符),但 app*~1 搜索不会应用模糊操作符(~1)。

6.6 邻近查询

虽然短语查询(例如"john smith")期望所有分词的顺序完全相同,但邻近查询允许指定的分词相距较远或采用不同的顺序。就像模糊查询可以指定分词中字符的最大编辑距离一样,邻近搜索允许我们指定短语中分词的最大编辑距离:

"fox quick"~5

上面的查询表示 foxquick 直接最多只能有 5 个单词。字段中的文本与查询字符串中指定的原始顺序越接近,该文档就越相关。与上面的示例查询相比,文本字段值中的 "quick fox" 将被认为比 "quick brown fox" 更相关。

6.7 范围

query_string.query 查询字符串可以为 日期数字字符串 字段指定查询值范围。范围使用中括号[]和大括号{}表示:

  • 闭区间范围:[min TO max]
  • 开区间范围:{min TO max}
  • 半开区间范围:[min TO max}{min TO max]

范围取值示例:

查询字符串语法(query_string.query)含义
date:[2024-01-01 TO 2024-12-31]查询 date 字段值在 2024-01-01 <= date <= 2024-12-31 的范围,即 2024 年一整年。
count:[1 TO 5]1 <= count <= 5
tag:{alpha TO omega}介于 alphaomega 之间,但不包括 alphaomega
count:[10 TO *]count >= 10,*表示无穷(无界)。
date:{* TO 2012-01-01}date < 2012-01-01
count:[1 TO 5}1 <= count < 5

如果是单边无界,可以使用以下语法:

  • age:>10
  • age:>=10
  • age:<10
  • age:<=10

还可以使用 ANDOR 运算符组合:

  • age:(>=10 AND <20)age:(+>=10 +<20)
  • age:(<0 OR >=1000)

6.8 提升相关性

使用 ^ 运算符,可以提升某个查询词匹配的相关性。

例如查询 quick^2 fox,表示查询 quickfox,但 quick 的相关性(boost)需提升为 2,也就是 quick 的匹配分数较高。

相关性 boost 是一个正浮点数,默认值为 10 - 1 之间的提升会降低相关性。

相关性提升也可以用在短语或组:"john smith"^2(foo bar)^4

6.9 布尔运算符

query_string 搜索字段,默认情况下只需要匹配其中一个分词即可(使用 default_operatorminimum_should_match 自主控制的除外),可以使用 +- 运算符组合出 必选匹配 和 必须不匹配 的逻辑:

POST /<index>/_search
{
    "query": {
        "query_string": {
            "query": "content:(quick brown +fox -news)"
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面查询 content 字段的语法含义:

  • quickbrown 可选。
  • fox 必须匹配。
  • news 必须不匹配。

使用 布尔查询(bool) 也可以达到相同效果:

POST /<index>/_search
{
    "query": {
        "bool": {
            "should":   { "match": "quick brown" },
            "must":     { "match": "fox"         },
            "must_not": { "match": "news"        }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

6.10 分组

多个查询词或子句可以使用括号()组合在一起形成子查询:

(quick OR brown) AND fox

组可用于定位特定字段,或提升子查询的结果:

status:(active OR pending) title:(full text search)^2

6.11 保留字符

query_string.query 的查询语法会使用一些特殊字符用作运算符,这些字符称为保留字符。

保留字符有:+ - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /

如果要把保留字符当做普通的字符串搜索,需要在前面加上 \ 转义。例如,搜索 (1+1)=2,需要将查询编写为 \(1\+1\)\=2

6.12 空格和空查询

空格不被视为运算符。

如果查询字符串为空或仅包含空格,则查询将产生空结果集。

6.13 搜索多个字段

query_string 查询可以使用 fields 参数实现跨多个字段查询。

针对使用 query_string.query 查询多个字段的想法是将每个查询项扩展为 OR 子句,如下所示:

field1:query_term OR field2:query_term | ...

多字段查询示例:

GET /<index>/_search
{
    "query": {
        "query_string": {
            "fields": [ "content", "name" ],
            "query": "this AND that"
        }
    }
}

// 上面的查询相当于:

GET /<index>/_search
{
    "query": {
        "query_string": {
            "query": "(content:this OR name:this) AND (content:that OR name:that)"
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

由于多个查询是根据各个搜索词生成的,因此可以使用带有 tie_breakerdis_max 查询自动将它们组合起来。例如使用提升相关性操作符 ^name 字段的相关性提示为 5

GET /<index>/_search
{
    "query": {
        "query_string" : {
            "fields" : ["content", "name^5"],
            "query" : "this AND that OR thus",
            "tie_breaker" : 0
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

可以通过简单的通配符(*)搜索某个字段的所有子字段:

GET /<index>/_search
{
    "query": {
        "query_string" : {
            "fields" : ["city.*"],
            "query" : "this AND that OR thus"
        }
    }
}

// 或者在 query_string.query 查询字符串中提供子字段通配符搜索, 上面搜索相当于:

GET /<index>/_search
{
    "query": {
        "query_string" : {
            "query" : "city.\\*:(this AND that OR thus)"
        }
    }
}

// 注意:
//      由于 `\`(反斜杠) 是 JSON 字符串中的特殊字符,需要对其进行转义,
//      因此上面的 `query_string.query` 中出现了两个反斜杠。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

fields 参数中的通配符(*)匹配的字段也可以使用 ^ 提升相关性:

GET /<index>/_search
{
    "query": {
        "query_string" : {
            "fields" : ["content", "name.*^5"],
            "query" : "this AND that OR thus"
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

7. 全文搜索: simple_query_string

官网API:Simple query string query

simple_query_string 使用简单语法但有容错的解析器解析查询字符串。

此查询使用 简单的语法 来解析提供的查询字符串(simple_query_string.query)并将其分割为基于特殊运算符的词语。然后查询文档并在返回匹配文档之前独立分析每个词语。

虽然 query_string 查询语法丰富并严格,但 simple_query_string 查询不会因语法无效而返回错误。相反,它会忽略查询字符串的任何无效部分。

simple_query_string 查询格式:

GET /<index>/_search
{
    "query": {
        "simple_query_string" : {
            "query": "\"fried eggs\" + (eggplant | potato) -frittata",      // 查询语法 (查询字符串)
            "fields": ["title^5", "body"],  // (可选) 要搜索的字段, 支持 通配符 和 `^`运算符提升相关性
            "default_operator": "AND",      // (可选) 对分词结果匹配的默认逻辑, "OR"表示只需要匹配其中一个分词, "AND"表示必须匹配所有分词, 默认为"OR"
            "analyze_wildcard": false,      // (可选) 如果为true, 将尝试分析查询字符串中的通配符, 默认为false
            "analyzer": "standard",         // (可选) 使用的分析器, 默认使用创建索引时指定的搜索分析器
            "flags": "ALL",                 // (可选) 解析查询语法需要启用的运算符列表(多个运算符使用`|`分割)。默认为"ALL"表示所有运算符均有效。有关有效值, 见后面。
            "minimum_should_match", "-25%", // (可选) 至少需要匹配的分词数量, 可以是 正负整数、正负百分比、整数与百分比组合, 详见API文档
            // ...
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

simple_query_string.query 支持的查询语法(运算符):

  • + 表示 AND 运算。
  • | 表示 OR 运算。
  • - 表示 NOT 运算符,否定单个标记。
  • " 包含多个标记来表示要搜索的短语(需要不分割/连续匹配的短语)。
  • * 在词语末尾表示前缀查询(通配符查询)。
  • () 表示优先级(分组)。
  • ~N 放在词语后面,表示编辑距离(模糊查询)。
  • ~N 短语后在表示溢出量。

要按字面意思使用这些字符,需要使用前反斜杠(\)将其转义。这些运算符的行为可能会根据 default_operator 参数值的不同而有所不同。

flags 参数的枚举值,多个值之间使用|分割(如: "AND|OR|NOT"):

  • ALL:(默认)启用所有可选运算符。
  • AND:启用 + AND 运算符。
  • ESCAPE\ 作为转义字符启用。
  • FUZZY~N 在词语后启用运算符,其中 N 是表示允许匹配的编辑距离的整数。请参阅模糊性。
  • NEAR~N 在短语之后启用运算符,其中 N 是匹配标记之间允许的最大位置数。同义于 SLOP
  • NONE:禁用所有运算符。
  • NOT:启用 - NOT 运算符。
  • OR:启用 \| OR 运算符。
  • PHRASE:启用 " 用于搜索短语的引号运算符。
  • PRECEDENCE:启用 () 运算符来控制运算符优先级。
  • PREFIX:启用 * 前缀运算符。
  • SLOP~N 在短语后面启用运算符,其中 N 是匹配标记之间允许的最大位置数。同义于 NEAR
  • WHITESPACE:启用空格作为分割字符。

simple_query_string 查询示例:

GET /<index>/_search
{
    "query": {
        "simple_query_string": {
            "fields": [ "content" ],
            "query": "foo bar -baz"
        }
    }
}
// 上面的查询语法分为了 "foo"、"bar"、"-baz" 三个标记(分词), 由于 default_operator 参数默认值为 "OR",
// 因此这三个标记之间默认是 OR 的关系, 最终返回的是 包含"foo" 或 包含"bar" 或 不包含"baz"。
// 如果要返回 包含"foo"或"bar", 但不包含"baz", 可以使用: "(foo bar) + -baz" 或 "(foo | bar) + -baz"

GET /<index>/_search
{
    "query": {
        "simple_query_string": {
            "query": "foo | bar + baz*",    // 包含"foo" 或 包含"bar" 并且 以"baz"开头
            "flags": "OR|AND|PREFIX"        // 指定需要启用的运算符
        }
    }
}

GET /<index>/_search
{
    "query": {
        "simple_query_string" : {
            "query":    "Will Smith",
            "fields": [ "title", "*_name" ] // 字段可以使用通配符, 查询 title, first_name 和 last_name 字段
        }
    }
}

GET /<index>/_search
{
    "query": {
        "simple_query_string" : {
            "query" : "this is a test",
            "fields" : [ "subject^3", "message" ]       // 可以为某个字段使用 `^` 操作符提升相关性
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

8. 基于地理位置的查询

官网API:Geo-distance query

ES 支持地理位置坐标和形状区域数据类型的存储和搜索。其中地理坐标存储了经纬度,可以根据位置距离搜索数据。

geo_point 类型表示地理坐标类型,地理类型官网相关链接:

对于 geo_point 类型的字段,有 3 种查询方式,分别为 geo_distance查询、geo_bounding_box查询 和 geo_polygon查询。

其中 geo_distance 查询是根据指定中心坐标点,查询指定距离范围内的文档。

geo_distance 查询请求格式:

POST /<index>/_search
{
    "query": {
        "geo_distance": {
            "distance": "5km",          // 距离范围
            "<field>": {                // 需要查询的 geo_point 类型的字段, 提供一个中心点坐标值
                "lat": 22.5298891,      // 纬度, 正数表示北纬, 负数表示南纬
                "lon": 113.9449817      // 经度, 正数表示东经, 负数表示西经
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

geo_distance 查询请求示例,搜索 2km 内的酒店:

POST /hotel/_search
{
    "query": {
        "geo_distance": {               // geo_distance 查询
            "distance": "2km",          // 距离范围
            "location": {               // "location" 字段的中心点坐标
                "lat": 22.5298891,      // 纬度, 正数表示北纬, 负数表示南纬
                "lon": 113.9449817      // 经度, 正数表示东经, 负数表示西经
            }
        }
    }
}

// 返回
{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "hotel",
                "_id": "001",
                "_score": 1,
                "_source": {
                    "name": "龙门国际大酒店",
                    "price": 300,
                    "decoration_date": "2024-06-06",
                    "international": true,
                    "location": {
                        "lat": 22.5377084,
                        "lon": 113.9308322
                    },
                    "tag": ["WIFI", "停车场", "冰箱", "微波炉", "洗衣机"]
                }
            },
            {
                "_index": "hotel",
                "_id": "002",
                "_score": 1,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200,
                    "decoration_date": "2023-05-04",
                    "international": true,
                    "location": {
                        "lat": 22.531667,
                        "lon": 113.9497277
                    },
                    "tag": ["WIFI", "停车场", "微波炉", "洗衣机"]
                }
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

9. 搜索建议

官网API:Suggesters

搜索建议指的是用户在输入搜索词的过程中,系统根据已输入部分给出建议的搜索关键词,也就是自动补全,用户只需要点击其中一个搜索关键词直接进行搜索。要实现搜索建议,需要在搜索过程中,用户每输入一个字,就把已输入部分发到服务端查询匹配的搜索建议词列表,因此该场景需要服务端能快速响应请求。通过给出搜索建议,可以避免用户输入错误的关键词,或者引导用户使用更合适的关键词搜索,可以大大提升用户搜索体验和搜索结果的准确度。

ES 可以使用 Completion Suggester 实现搜索建议功能。如果要一个字段支持搜索建议,可以把字段定义为 completion 类型,搜索时使用 suggest 搜索。

为了方便演示,重新创建一个索引,写入一些文档:

// 创建索引
PUT /hotel_suggest
{
    "mappings": {
        "properties": {                 // 映射字段
            "name": {                   // 字段名称
                "type": "completion"    // 字段类型, 用于搜索建议
            }
        }
    }
}

// 写入文档
PUT /hotel_suggest/_doc/001
{"name": "龙门国际大酒店"}

PUT /hotel_suggest/_doc/002
{"name": "龙门精选假日酒店"}

PUT /hotel_suggest/_doc/003
{"name": "龙门客栈古风酒店"}

PUT /hotel_suggest/_doc/004
{"name": "悦来时尚宾馆"}

PUT /hotel_suggest/_doc/005
{"name": "悦来文雅大酒店"}

PUT /hotel_suggest/_doc/006
{"name": "烟雨楼文雅假日酒店"}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

suggest 搜索请求格式:

POST /<index>/_search
{
    "suggest": {                            // suggest 搜素
        "<suggest_name>": {                 // 自定义的搜索建议的名称, 可同时搜索多个
            "prefix": "<query_keyword>",    // 匹配前缀的关键词 (还有其他匹配方式)
            "completion": {
                "field": "<field_name>"     // 用于匹配前缀的 completion 类型的字段
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

suggest 搜索可以和 query 搜索一起使用,前者搜索结果封装在响应的 suggest 节点中,后者搜索结果封装在响应的 hits 节点中。

suggest 搜索请求示例:

POST /hotel_suggest/_search
{
    "suggest": {
        "name_suggest_1": {
            "prefix": "龙门",
            "completion": {
                "field": "name"
            }
        }
    }
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 0,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "suggest": {
        "name_suggest_1": [                             // 搜索请求中自定义的搜索建议名称
            {
                "text": "龙门",
                "offset": 0,
                "length": 2,
                "options": [
                    {
                        "text": "龙门国际大酒店",          // 搜索建议词
                        "_index": "hotel_suggest",
                        "_id": "001",
                        "_score": 1,
                        "_source": {                    // 原文档内容
                            "name": "龙门国际大酒店"
                        }
                    },
                    {
                        "text": "龙门客栈古风酒店",
                        "_index": "hotel_suggest",
                        "_id": "003",
                        "_score": 1,
                        "_source": {
                            "name": "龙门客栈古风酒店"
                        }
                    },
                    {
                        "text": "龙门精选假日酒店",
                        "_index": "hotel_suggest",
                        "_id": "002",
                        "_score": 1,
                        "_source": {
                            "name": "龙门精选假日酒店"
                        }
                    }
                ]
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

10. 按字段值排序

官网API:Sort search results

ES 搜索结果默认根据匹配程度降序排序,也可以在搜索请求时指定按照某些字段的值升序或降序排序。query 查询请求中,提供了 sort 子句用于根据指定的字段值排序。

搜索排序请求格式:

POST /<index>/_search
{
    "query": {
        // ... 搜索条件
    },
    "sort": [               // 排序, 可以按多个字段排序, 优先按数组靠前的元素字段排序
        {
            "<field>": {                    // 按某个字段的值排序
                "order": "asc|desc"         // 升序或降序
            }
        },
        {
            "_geo_distance": {              // 按地理距离排序
                "<field>": {                // 排序字段的中心点坐标 (数据类型为 geo_point 的字段)
                    "lat": 22.5298891,      // 纬度, 正数表示北纬, 负数表示南纬
                    "lon": 113.9449817      // 经度, 正数表示东经, 负数表示西经
                },
                "order": "asc|desc",        // 升序或降序,
                "unit": "km",               // 距离单位, 用于计算响应中 sort 字段的数值
                "distance_type": "plane"    // 距离计算算法
            }
        }
        // ...
    ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

10.1 按字段值排序

按字段值排序请求示例:

POST /hotel/_search
{
    "_source": ["name", "price", "decoration_date"],
    "query": {                         // 查询条件
        "match": {
            "name": "龙门"
        }
    },
    "sort": [                           // 排序条件
        {
            "price": {                  // 优先按价格升序排序
                "order": "asc"
            }
        },
        {
            "decoration_date": {        // 价格相同的, 再按装修日期降序排序
                "order": "desc"
            }
        }
    ]
}

// 返回
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": null,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": null,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "price": 200,
                    "decoration_date": "2023-05-04"
                },
                "sort": [ 200, 1683158400000 ]      // 排序字段值, 相当于排序分数
            },
            {
                "_index": "hotel",
                "_id": "001",
                "_score": null,
                "_source": {
                    "name": "龙门国际大酒店",
                    "price": 300,
                    "decoration_date": "2024-06-06"
                },
                "sort": [ 300, 1717632000000 ]
            },
            {
                "_index": "hotel",
                "_id": "003",
                "_score": null,
                "_source": {
                    "name": "龙门客栈古风酒店",
                    "price": 350,
                    "decoration_date": "2021-10-08"
                },
                "sort": [ 350, 1633651200000 ]
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

10.2 按地理距离排序

按地理距离排序请求示例:

POST /hotel/_search
{
    "_source": ["name", "location"],
    "query": {                              // 查询条件
        "match": {
            "name": "龙门"
        }
    },
    "sort": [                               // 排序条件
        {
            "_geo_distance": {              // 按地理距离排序
                "location": {               // "location" 字段的中心点坐标
                    "lat": 22.5298891,      // 纬度, 正数表示北纬, 负数表示南纬
                    "lon": 113.9449817      // 经度, 正数表示东经, 负数表示西经
                },
                "order": "asc",             // 升序排序
                "unit": "km",               // 排序距离单位为 km
                "distance_type": "plane"    // 平面算法
            }
        }
    ]
}

// 返回
{
    "took": 5,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": null,
        "hits": [
            {
                "_index": "hotel",
                "_id": "002",
                "_score": null,
                "_source": {
                    "name": "龙门精选假日酒店",
                    "location": {
                        "lat": 22.531667,
                        "lon": 113.9497277
                    }
                },
                "sort": [ 0.5260141384282283 ]      // 排序距离, 距离中心 0.526km
            },
            {
                "_index": "hotel",
                "_id": "001",
                "_score": null,
                "_source": {
                    "name": "龙门国际大酒店",
                    "location": {
                        "lat": 22.5377084,
                        "lon": 113.9308322
                    }
                },
                "sort": [ 1.693476752894645 ]
            },
            {
                "_index": "hotel",
                "_id": "003",
                "_score": null,
                "_source": {
                    "name": "龙门客栈古风酒店",
                    "location": {
                        "lat": 22.53396,
                        "lon": 114.0554156
                    }
                },
                "sort": [ 11.351370339175176 ]
            }
        ]
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/842277
推荐阅读
相关标签
  

闽ICP备14008679号