Elasticsearch 提供了多种查询类型,每种查询类型适用于不同的搜索场景。以下是八种常见的 Elasticsearch 查询类型及其详细说明和示例。
1. Match Query
用途:用于全文搜索,会对输入的文本进行分词,并在索引中的字段中查找这些分词。 特点:支持模糊匹配、短语匹配等。
示例
json
GET /my_index/_search
{
"query": {
"match": {
"content": "quick brown fox"
}
}
}
2. Term Query
用途:用于精确匹配,不会对输入的文本进行分词。 特点:适用于关键词、ID 等不需要分词的情况。
示例
json
GET /my_index/_search
{
"query": {
"term": {
"status": "active"
}
}
}
3. Terms Query
用途:用于在一个字段上匹配多个值。 特点:类似于 SQL 中的 IN 操作符。
示例
json
GET /my_index/_search
{
"query": {
"terms": {
"tags": ["news", "sports"]
}
}
}
4. Range Query
用途:用于范围查询,可以指定数值或日期范围。 特点:适用于数值型字段和日期字段。
示例
json
GET /my_index/_search
{
"query": {
"range": {
"age": {
"gte": 18,
"lte": 30
}
}
}
}
5. Bool Query
用途:组合多个查询条件,支持 must(必须满足)、should(应该满足)、must_not(必须不满足)和 filter(过滤)子句。 特点:灵活性高,可以构建复杂的查询逻辑。
示例
json
GET /my_index/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Elasticsearch" } },
{ "range": { "date": { "gte": "now-1y/d" }}}
],
"filter": [
{ "term": { "status": "published" }}
]
}
}
}
6. Wildcard Query
用途:使用通配符进行匹配。 特点:支持 *
和 ?
通配符,但性能较差,应谨慎使用。
示例
json
GET /my_index/_search
{
"query": {
"wildcard": {
"name": "joh*"
}
}
}
7. Fuzzy Query
用途:用于近似匹配,允许一定数量的拼写错误。 特点:适用于拼写错误较多的搜索场景。
示例
GET /my_index/_search
{
"query": {
"fuzzy": {
"username": {
"value": "kenneth",
"fuzziness": "AUTO"
}
}
}
}
8. Match Phrase Query
用途:用于精确匹配整个短语。 特点:不会对短语进行分词,要求短语完全一致。
示例
json
GET /my_index/_search
{
"query": {
"match_phrase": {
"description": "Elasticsearch search engine"
}
}
}
总结
每种查询类型都有其特定的应用场景和优缺点:
- Match Query:适合全文搜索。
- Term Query:适合精确匹配。
- Terms Query:适合多值匹配。
- Range Query:适合范围查询。
- Bool Query:适合复杂查询逻辑。
- Wildcard Query:适合通配符匹配,性能较低。
- Fuzzy Query:适合拼写错误匹配。
- Match Phrase Query:适合精确短语匹配。
通过合理选择和组合这些查询类型,可以实现高效且灵活的搜索功能。
参考资料
这些资源提供了更多关于 Elasticsearch 查询类型的详细信息和示例,帮助你更好地理解和使用这些查询
为了实现八种 Elasticsearch 查询类型并在 Web 前端展示结果,我们可以使用 Spring Boot 构建后端服务,并使用 Thymeleaf 作为模板引擎来构建前端页面。以下是一个完整的示例,展示如何实现这八种查询类型并将结果显示在 Web 前端。
项目结构
spring-boot-elasticsearch-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── controller/
│ │ │ │ └── SearchController.java
│ │ │ ├── model/
│ │ │ │ └── Document.java
│ │ │ ├── repository/
│ │ │ │ └── DocumentRepository.java
│ │ │ ├── service/
│ │ │ │ └── SearchService.java
│ │ │ └── SpringBootElasticsearchDemoApplication.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── static/
│ │ └── templates/
│ │ └── search.html
├── pom.xml
1. 添加依赖
在 pom.xml
中添加必要的依赖:
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
</dependency>
</dependencies>
2. 配置文件
在 src/main/resources/application.yml
中配置 Elasticsearch 连接信息:
yaml
spring:
elasticsearch:
rest:
uris: http://localhost:9200
server:
port: 8080
3. 实体类
创建一个实体类 Document.java
表示索引中的文档:
java
package com.example.model;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
@Document(indexName = "documents")
public class Document {
@Id
private String id;
@Field(type = FieldType.Text)
private String title;
@Field(type = FieldType.Text)
private String content;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Date)
private String date;
@Field(type = FieldType.Keyword)
private String status;
@Field(type = FieldType.Keyword)
private String[] tags;
@Field(type = FieldType.Keyword)
private String username;
}
4. Repository 接口
创建一个 Repository 接口 DocumentRepository.java
:
java
package com.example.repository;
import com.example.model.Document;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface DocumentRepository extends ElasticsearchRepository<Document, String> {
}
5. Service 层
创建一个 Service 类 SearchService.java
来处理各种查询逻辑:
java
package com.example.service;
import com.example.model.Document;
import com.example.repository.DocumentRepository;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class SearchService {
@Autowired
private RestHighLevelClient client;
public List<Document> matchQuery(String field, String query) throws IOException {
SearchRequest searchRequest = new SearchRequest("documents");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(field, query);
sourceBuilder.query(matchQueryBuilder);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getDocumentsFromResponse(searchResponse);
}
public List<Document> termQuery(String field, String value) throws IOException {
SearchRequest searchRequest = new SearchRequest("documents");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(field, value);
sourceBuilder.query(termQueryBuilder);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getDocumentsFromResponse(searchResponse);
}
public List<Document> termsQuery(String field, List<String> values) throws IOException {
SearchRequest searchRequest = new SearchRequest("documents");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery(field, values);
sourceBuilder.query(termsQueryBuilder);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getDocumentsFromResponse(searchResponse);
}
public List<Document> rangeQuery(String field, Object gte, Object lte) throws IOException {
SearchRequest searchRequest = new SearchRequest("documents");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(field).gte(gte).lte(lte);
sourceBuilder.query(rangeQueryBuilder);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getDocumentsFromResponse(searchResponse);
}
public List<Document> boolQuery(Map<String, Object> mustQueries, Map<String, Object> shouldQueries, Map<String, Object> mustNotQueries) throws IOException {
SearchRequest searchRequest = new SearchRequest("documents");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
if (mustQueries != null) {
for (Map.Entry<String, Object> entry : mustQueries.entrySet()) {
boolQueryBuilder.must(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()));
}
}
if (shouldQueries != null) {
for (Map.Entry<String, Object> entry : shouldQueries.entrySet()) {
boolQueryBuilder.should(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()));
}
}
if (mustNotQueries != null) {
for (Map.Entry<String, Object> entry : mustNotQueries.entrySet()) {
boolQueryBuilder.mustNot(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()));
}
}
sourceBuilder.query(boolQueryBuilder);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getDocumentsFromResponse(searchResponse);
}
public List<Document> wildcardQuery(String field, String value) throws IOException {
SearchRequest searchRequest = new SearchRequest("documents");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery(field, value);
sourceBuilder.query(wildcardQueryBuilder);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getDocumentsFromResponse(searchResponse);
}
public List<Document> fuzzyQuery(String field, String value) throws IOException {
SearchRequest searchRequest = new SearchRequest("documents");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery(field, value);
sourceBuilder.query(fuzzyQueryBuilder);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getDocumentsFromResponse(searchResponse);
}
public List<Document> matchPhraseQuery(String field, String phrase) throws IOException {
SearchRequest searchRequest = new SearchRequest("documents");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery(field, phrase);
sourceBuilder.query(matchPhraseQueryBuilder);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getDocumentsFromResponse(searchResponse);
}
private List<Document> getDocumentsFromResponse(SearchResponse searchResponse) {
List<Document> documents = new ArrayList<>();
searchResponse.getHits().forEach(hit -> {
Document document = new Document();
document.setId(hit.getId());
document.setTitle((String) hit.getSourceAsMap().get("title"));
document.setContent((String) hit.getSourceAsMap().get("content"));
document.setAge((Integer) hit.getSourceAsMap().get("age"));
document.setDate((String) hit.getSourceAsMap().get("date"));
document.setStatus((String) hit.getSourceAsMap().get("status"));
document.setTags((String[]) hit.getSourceAsMap().get("tags"));
document.setUsername((String) hit.getSourceAsMap().get("username"));
documents.add(document);
});
return documents;
}
}
6. Controller 层
创建一个 Controller 类 SearchController.java
来处理 HTTP 请求并返回结果:
java
package com.example.controller;
import com.example.model.Document;
import com.example.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class SearchController {
@Autowired
private SearchService searchService;
@GetMapping("/search")
public String search(
@RequestParam(required = false) String queryType,
@RequestParam(required = false) String field,
@RequestParam(required = false) String query,
@RequestParam(required = false) String value,
@RequestParam(required = false) String gte,
@RequestParam(required = false) String lte,
@RequestParam(required = false) String[] values,
Model model) throws IOException {
List<Document> results = null;
switch (queryType) {
case "match":
results = searchService.matchQuery(field, query);
break;
case "term":
results = searchService.termQuery(field, value);
break;
case "terms":
results = searchService.termsQuery(field, List.of(values));
break;
case "range":
results = searchService.rangeQuery(field, gte, lte);
break;
case "bool":
Map<String, Object> mustQueries = new HashMap<>();
Map<String, Object> shouldQueries = new HashMap<>();
Map<String, Object> mustNotQueries = new HashMap<>();
if (field != null && query != null) {
mustQueries.put(field, query);
}
results = searchService.boolQuery(mustQueries, shouldQueries, mustNotQueries);
break;
case "wildcard":
results = searchService.wildcardQuery(field, value);
break;
case "fuzzy":
results = searchService.fuzzyQuery(field, value);
break;
case "match_phrase":
results = searchService.matchPhraseQuery(field, query);
break;
default:
// Handle default case or throw an exception
break;
}
model.addAttribute("results", results);
return "search";
}
}
7. 前端页面
创建一个 Thymeleaf 模板 search.html
来展示搜索结果:
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Elasticsearch Queries</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-5">
<h1>Elasticsearch Queries</h1>
<form method="get" action="/search">
<div class="form-group">
<label for="queryType">Query Type:</label>
<select class="form-control" id="queryType" name="queryType">
<option value="match">Match</option>
<option value="term">Term</option>
<option value="terms">Terms</option>
<option value="range">Range</option>
<option value="bool">Bool</option>
<option value="wildcard">Wildcard</option>
<option value="fuzzy">Fuzzy</option>
<option value="match_phrase">Match Phrase</option>
</select>
</div>
<div class="form-group">
<label for="field">Field:</label>
<input type="text" class="form-control" id="field" name="field">
</div>
<div class="form-group">
<label for="query">Query:</label>
<input type="text" class="form-control" id="query" name="query">
</div>
<div class="form-group">
<label for="value">Value:</label>
<input type="text" class="form-control" id="value" name="value">
</div>
<div class="form-group">
<label for="gte">GTE:</label>
<input type="text" class="form-control" id="gte" name="gte">
</div>
<div class="form-group">
<label for="lte">LTE:</label>
<input type="text" class="form-control" id="lte" name="lte">
</div>
<div class="form-group">
<label for="values">Values (comma separated):</label>
<input type="text" class="form-control" id="values" name="values">
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
<hr>
<h2>Results:</h2>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Content</th>
<th>Age</th>
<th>Date</th>
<th>Status</th>
<th>Tags</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr th:each="document : ${results}">
<td th:text="${document.id}"></td>
<td th:text="${document.title}"></td>
<td th:text="${document.content}"></td>
<td th:text="${document.age}"></td>
<td th:text="${document.date}"></td>
<td th:text="${document.status}"></td>
<td th:text="${#strings.arrayJoin(document.tags, ', ')}"></td>
<td th:text="${document.username}"></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
8. 启动类
创建启动类 SpringBootElasticsearchDemoApplication.java
:
java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootElasticsearchDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootElasticsearchDemoApplication.class, args);
}
}
9. 测试数据
确保 Elasticsearch 中有一些测试数据以便进行查询。你可以使用 Kibana 或 curl 命令插入一些文档到 documents
索引中。
示例文档
json
POST /documents/_doc/1
{
"title": "Introduction to Elasticsearch",
"content": "Elasticsearch is a distributed, RESTful search and analytics engine.",
"age": 5,
"date": "2023-01-01",
"status": "published",
"tags": ["elasticsearch", "search"],
"username": "john_doe"
}
POST /documents/_doc/2
{
"title": "Advanced Elasticsearch Techniques",
"content": "Learn advanced techniques in Elasticsearch for better performance.",
"age": 3,
"date": "2023-06-15",
"status": "draft",
"tags": ["elasticsearch", "advanced"],
"username": "jane_smith"
}
10. 运行项目
- 启动 Elasticsearch:确保 Elasticsearch 服务器正在运行。
- 插入测试数据:使用上述示例文档插入测试数据。
- 启动 Spring Boot 应用:
bash
./mvnw spring-boot:run
4.访问应用:打开浏览器,访问 http://localhost:8080/search
。
使用说明
在前端页面中,可以选择不同的查询类型并输入相应的参数,点击“Search”按钮即可查看查询结果。以下是每种查询类型的示例参数:
-
Match Query:
- Query Type:
match
- Field:
content
- Query:
introduction
- Query Type:
-
Term Query:
- Query Type:
term
- Field:
status
- Value:
published
- Query Type:
-
Terms Query:
- Query Type:
terms
- Field:
tags
- Values:
elasticsearch,search
- Query Type:
-
Range Query:
- Query Type:
range
- Field:
age
- GTE:
1
- LTE:
5
- Query Type:
-
Bool Query:
- Query Type:
bool
- Field:
status
- Query:
published
- Query Type:
-
Wildcard Query:
- Query Type:
wildcard
- Field:
username
- Value:
john*
- Query Type:
-
Fuzzy Query:
- Query Type:
fuzzy
- Field:
username
- Value:
jon_doe
- Query Type:
-
Match Phrase Query:
- Query Type:
match_phrase
- Field:
title
- Query:
Introduction to Elasticsearch
- Query Type:
通过这种方式,你可以在 Web 前端展示和测试八种常见的 Elasticsearch 查询类型。