Elasticsearch实战应用:五大业务场景深入解析

2024-12-17 16:26 更新

大家好,我是 V 哥。Elasticsearch 是一个强大的全文搜索和分析引擎,广泛应用于各种场景。以下是五个常见业务场景中的 Elasticsearch 实战应用案例及其详细分析。

1. 全文搜索与高亮显示

业务场景: 某电商平台需要为用户提供高效的商品搜索功能,要求在海量数据中快速返回匹配结果,并高亮显示关键字,提升用户体验。

解决方案:

  • 索引设计: 对商品名称、描述、品牌等字段进行全文索引,使用 Elasticsearch 的分词器(如 Standard Analyzer)处理数据,确保用户输入的关键字可以正确匹配商品信息。
  • 搜索功能: 使用 match 查询类型,配合 multi_match 进行多个字段的搜索,确保用户查询能匹配到商品名称、描述等相关字段。
  • 高亮显示: 使用 highlight 功能,在返回的结果中对匹配的关键字进行高亮处理,提升用户可读性。

详细分析: Elasticsearch 提供了强大的倒排索引机制,使得全文搜索非常高效。通过灵活的查询组合,用户可以精确匹配多种字段的搜索条件,同时高亮功能可以让用户直观地看到匹配位置。此方案提升了用户的搜索体验,并能迅速处理电商平台的大量商品数据。

要实现全文搜索与高亮显示的功能,主要分为以下几个步骤,包括 Elasticsearch 环境的设置、数据的索引、查询的编写,以及高亮显示的处理。具体如下:

1. 环境准备

确保 Elasticsearch 已经安装并运行。如果尚未安装,可以通过 Docker 快速启动一个 Elasticsearch 实例:

docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" elasticsearch:8.0.0

然后可以通过以下 URL 访问 Elasticsearch API:

http://localhost:9200

2. 创建索引与映射

在实际场景中,可能需要为商品(或者其他实体)创建一个索引。首先为该索引配置字段和分词器,确保字段能够支持全文检索。

创建索引和映射

我们为商品信息创建一个索引,定义商品名称和描述的字段类型为 text,并指定使用默认的分词器。

PUT /products
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "description": {
        "type": "text"
      },
      "price": {
        "type": "float"
      }
    }
  }
}

3. 添加商品数据

在创建好索引后,可以开始向索引中插入一些商品数据。以下是一些商品的示例数据:

POST /products/_doc/1
{
  "name": "huawei mate 70",
  "description": "mate 70 手机是搭载纯血鸿蒙NEXT 系统的第一款旗舰机",
  "price": 6500
}


POST /products/_doc/2
{
  "name": "huawei Mate XT非凡大师",
  "description": "非凡大师 16GB+1TB玄黑 ULTIMATE DESIGN",
  "price": 23999
}


POST /products/_doc/3
{
  "name": "huawei Mate X5",
  "description": "huawei mate x5 12GB+512GB 羽砂黑 超轻薄四曲折叠,超高清高分辨率临境双屏,超智慧灵犀通信",
  "price": 12499
}

4. 实现搜索功能

使用 match 查询来实现对商品名称和描述字段的全文搜索。为了能高效地搜索到多个字段中的内容,我们可以使用 multi_match 查询。比如用户在搜索框中输入了“iPhone”时,我们希望在商品名称和描述中都查找匹配项。

基本查询

GET /products/_search
{
  "query": {
    "multi_match": {
      "query": "X5",
      "fields": ["name", "description"]
    }
  }
}

这个查询会返回所有匹配“iPhone”关键字的商品。

5. 添加高亮显示

为了增强用户体验,可以使用 Elasticsearch 的高亮功能,显示搜索结果中的匹配词语。通过在查询中添加 highlight,我们可以让关键字在返回结果中高亮显示。

带高亮显示的查询

GET /products/_search
{
  "query": {
    "multi_match": {
      "query": "X5",
      "fields": ["name", "description"]
    }
  },
  "highlight": {
    "fields": {
      "name": {},
      "description": {}
    }
  }
}

6. 结果解析

Elasticsearch 返回的结果中会包含高亮字段。例如,假设用户搜索“iPhone”,以下是一个可能的响应结果:

{
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "hits": [
      {
        "_index": "products",
        "_id": "1",
        "_source": {
          "name": "huawei Mate X5",
          "description": "huawei mate x5 12GB+512GB 羽砂黑 超轻薄四曲折叠,超高清高分辨率临境双屏,超智慧灵犀通信",
          "price": 12499
        },
        "highlight": {
          "name": ["huawei <em>x5</em> 14"],
          "description": ["huawei mate <em>x5</em> 12GB+512GB 羽砂黑 超轻薄四曲折叠,超高清高分辨率临境双屏,超智慧灵犀通信"]
        }
      }
    ]
  }
}

从结果中可以看到,高亮部分会以 <em> 标签包裹,我们可以根据需求在前端使用 CSS 或 HTML 标签来调整高亮显示的样式。

7. 前端展示

在前端页面中,搜索结果可以通过解析响应中的 _sourcehighlight 字段,将高亮部分以更明显的方式展示给用户。假设我们使用 JavaScript 进行结果展示,代码可能如下:

<ul id="search-results"></ul>


<script>
  const results = [
    {
      "_source": {
        "name": "huawei Mate X5",
        "description": "huawei mate x5 12GB+512GB 羽砂黑 超轻薄四曲折叠,超高清高分辨率临境双屏,超智慧灵犀通信"
      },
      "highlight": {
        "name": ["huawei Mate <em>X5</em>"],
        "description": ["huawei mate <em>X5</em> 12GB+512GB 羽砂黑 超轻薄四曲折叠,超高清高分辨率临境双屏,超智慧灵犀通信"]
      }
    }
  ];


  const resultsContainer = document.getElementById('search-results');
  results.forEach(result => {
    const listItem = document.createElement('li');
    listItem.innerHTML = `
      <h2>${result.highlight.name ? result.highlight.name[0] : result._source.name}</h2>
      <p>${result.highlight.description ? result.highlight.description[0] : result._source.description}</p>
    `;
    resultsContainer.appendChild(listItem);
  });
</script>

在上面的例子中,前端会展示包含 <em> 标签的文本,该标签会将搜索到的关键字(如“x5”)高亮显示。

8. 扩展:自定义分词器和同义词

如果搜索场景中需要更复杂的匹配,比如同义词搜索、拼写纠错等,可以进一步定制分词器或通过同义词字典进行扩展。

使用自定义分词器

PUT /products
{
  "settings": {
    "analysis": {
      "analyzer": {
        "synonym_analyzer": {
          "tokenizer": "whitespace",
          "filter": ["synonym_filter"]
        }
      },
      "filter": {
        "synonym_filter": {
          "type": "synonym",
          "synonyms": [
            "x5, mate x5",
            "mate70, 非凡大师"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "synonym_analyzer"
      },
      "description": {
        "type": "text",
        "analyzer": "synonym_analyzer"
      }
    }
  }
}

通过同义词分析器,用户搜索“ huawei mate”时也可以匹配到包含“mate”的文档,从而进一步提升搜索的准确性。

小结一下

通过上述步骤,我们可以实现一个完整的 Elasticsearch 全文搜索与高亮显示的功能。这个功能适用于电商平台、博客搜索、文档系统等多种业务场景,提供快速、高效、用户友好的搜索体验。

2. 日志收集与分析

业务场景: 某 SaaS 公司需要对其分布式系统中的应用日志进行集中管理、实时监控与分析,要求快速定位系统错误和性能瓶颈。使用 Elasticsearch 可以集中存储和分析分布式系统中的日志,快速查询和监控日志数据。

解决方案:

  • 日志收集: 使用 Logstash 或 Filebeat 作为数据采集工具,将各个应用的日志发送到 Elasticsearch 进行存储和索引。
  • 日志分析: 使用 Elasticsearch 的 aggregations 聚合功能进行日志的统计分析,如错误分类、按时间段的访问量统计等。
  • 实时监控: 配合 Kibana,构建实时的日志监控和告警系统,通过可视化的方式展示日志数据,快速发现异常。

详细分析: Elasticsearch 是 ELK(Elasticsearch, Logstash, Kibana)技术栈中的核心组件,它不仅支持大规模日志数据的存储,还能通过内置的聚合和搜索功能,实现实时分析与可视化。对于分布式系统中的日志分析场景,Elasticsearch 通过分片和复制机制提供了高可用性和扩展性,保证了海量日志数据的快速查询和处理。

以下是一个完整的实现案例,包括从日志的收集、传输、存储到实时分析的步骤。

1. 环境准备

在实际应用中,日志的收集与分析一般采用 ELK(Elasticsearch, Logstash, Kibana)或 EFK(Elasticsearch, Filebeat, Kibana)技术栈。这里我们选择 Filebeat 作为日志采集工具,Elasticsearch 作为数据存储和查询引擎,Kibana 作为可视化和监控工具。

启动 Elasticsearch 和 Kibana(Docker 方式)

## 启动 Elasticsearch
docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" elasticsearch:8.0.0


## 启动 Kibana
docker run -d --name kibana -p 5601:5601 --link elasticsearch:elasticsearch kibana:8.0.0

访问 Kibana: http://localhost:5601,确保 Elasticsearch 和 Kibana 正常启动并连接成功。

2. 安装并配置 Filebeat

Filebeat 是一个轻量级的日志收集工具,能够监控文件变化,并将日志数据发送到 Elasticsearch 或 Logstash。

安装 Filebeat

可以通过以下方式在系统中安装 Filebeat:

## 在 Linux 中安装 Filebeat
sudo apt-get install filebeat

配置 Filebeat

Filebeat 的配置文件 filebeat.yml 是日志收集的核心,它定义了从哪里收集日志,日志如何处理,并发送到哪里。以下是一个典型的配置文件,用于将日志发送到 Elasticsearch。

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/myapp/*.log  # 日志文件路径


output.elasticsearch:
  hosts: ["localhost:9200"]  # 指定 Elasticsearch 地址
  username: "elastic"
  password: "changeme"  # 设置 Elasticsearch 认证


setup.kibana:
  host: "localhost:5601"  # Kibana 的地址

这段配置定义了 Filebeat 从 /var/log/myapp/*.log 目录下读取日志文件,并将其传送到 Elasticsearch 中进行存储。同时,还配置了 Kibana 的连接,便于后续可视化分析。

启动 Filebeat

sudo filebeat modules enable system
sudo filebeat setup
sudo service filebeat start

3. Elasticsearch 索引和映射

在日志数据发送到 Elasticsearch 之前,需要为日志数据创建索引,并为其指定字段映射。可以使用 Elasticsearch 动态映射的功能来自动创建索引,但为了更好地处理日志中的日期、字符串等数据类型,建议手动创建索引映射。

创建索引映射

PUT /logs-system
{
  "mappings": {
    "properties": {
      "timestamp": {
        "type": "date"
      },
      "log.level": {
        "type": "keyword"
      },
      "message": {
        "type": "text"
      },
      "service.name": {
        "type": "keyword"
      },
      "host.name": {
        "type": "keyword"
      },
      "process.pid": {
        "type": "integer"
      }
    }
  }
}

在这个映射中,我们为系统日志定义了几个重要的字段:

  • timestamp: 日志的时间戳,数据类型为 date
  • log.level: 日志级别,如 INFO, ERROR 等,数据类型为 keyword
  • message: 日志的内容,使用 text 类型进行全文索引。
  • service.name: 服务名称,用于区分不同的服务。
  • host.name: 记录日志的主机名,用于定位具体的机器。
  • process.pid: 进程 ID,用于进一步跟踪问题。

4. 日志采集与发送

当 Filebeat 启动后,它会监控配置文件中的日志文件路径(例如 /var/log/myapp/*.log),并将新生成的日志行发送到 Elasticsearch 中。

日志文件示例

假设应用程序生成了如下格式的日志文件 /var/log/myapp/app.log

2024-10-14T12:30:00Z INFO [my-service] Service started
2024-10-14T12:31:00Z ERROR [my-service] Failed to connect to database
2024-10-14T12:32:00Z WARN [my-service] Low disk space on /dev/sda1

这些日志文件会被 Filebeat 采集并自动传送到 Elasticsearch 中,按配置的索引存储。

5. 实时日志查询与分析

一旦日志数据进入 Elasticsearch,我们可以使用 Kibana 进行实时查询和分析。

在 Kibana 中创建索引模式

  1. 访问 Kibana 管理界面 http://localhost:5601
  2. 点击 Management > Index Patterns,创建一个新的索引模式 logs-system-*,这将匹配 Elasticsearch 中的日志索引。
  3. timestamp 字段设置为时间过滤字段,用于时间范围筛选。

在 Kibana 中进行搜索

Kibana 提供了一个非常强大的查询语言——KQL(Kibana Query Language),可以在 Kibana 中对日志数据进行各种查询和过滤。例如:

  1. 查询所有 ERROR 级别的日志:

   log.level: "ERROR"

  1. 查询特定服务的日志:

   service.name: "my-service"

  1. 结合日志级别和时间范围查询:

   log.level: "ERROR" AND @timestamp > "2024-10-14T12:00:00Z"

6. 聚合分析

Elasticsearch 提供了强大的 aggregations 聚合功能,可以用于统计和分析日志数据中的各种模式。以下是几个常用的聚合查询示例:

统计不同日志级别的日志数量

GET /logs-system/_search
{
  "size": 0,
  "aggs": {
    "by_log_level": {
      "terms": {
        "field": "log.level"
      }
    }
  }
}

按时间段统计每分钟的错误日志数量

GET /logs-system/_search
{
  "size": 0,
  "query": {
    "match": {
      "log.level": "ERROR"
    }
  },
  "aggs": {
    "logs_over_time": {
      "date_histogram": {
        "field": "timestamp",
        "interval": "minute"
      }
    }
  }
}

7. 创建告警(Alerting)

为了实现实时监控和错误告警,我们可以使用 Kibana 的告警功能,设置触发条件和告警机制。

配置告警步骤

  1. 打开 Kibana,进入 Alerts and Actions 页面。
  2. 创建新的告警规则,例如:
    • 当错误日志超过某个阈值时发送通知(如邮件、Slack 消息等)。
  3. 配置触发条件,例如每分钟的错误日志超过 10 条时触发告警。

8. 结果可视化

Kibana 提供了丰富的可视化功能,你可以通过以下方式展示和分析日志数据:

  • 折线图:展示一段时间内日志数量的变化趋势。
  • 柱状图:展示不同服务或主机生成的日志数量。
  • 饼图:展示不同日志级别(INFO, ERROR, WARN)的比例。

可视化示例

  • 按服务统计日志级别分布:在 Kibana 中创建柱状图,X 轴为 service.name,Y 轴为日志数量,按 log.level 进行分组。
  • 实时监控仪表盘:创建一个仪表盘,展示不同服务的实时日志流量、错误数量等,便于运维人员实时监控系统健康状态。

9. 小结一下

通过 Elasticsearch、Filebeat 和 Kibana 的配合,我们可以快速搭建一个集中式日志收集与分析系统,实现对分布式系统日志的实时监控和告警。步骤包括:

  • 使用 Filebeat 收集各服务的日志。
  • 使用 Elasticsearch 存储和聚合日志数据。
  • 通过 Kibana 实现可视化分析和告警通知。

这种架构能够帮助 SaaS 公司快速定位系统错误、分析性能瓶颈,并为系统运维提供实时的可视化支持。

3. 个性化推荐系统

业务场景: 某在线视频平台希望通过构建个性化推荐系统,根据用户的历史观看记录、兴趣偏好和行为数据,为用户推荐相关视频内容。这类推荐系统有助于提升用户的粘性和转化率,进一步推动平台的商业化。为了实现该目标,可以采用基于 Elasticsearch 的内容推荐模型,结合协同过滤(Collaborative Filtering)、内容过滤(Content-Based Filtering)以及基于行为的数据分析。

解决方案:

  • 用户行为数据建模: 将用户的浏览历史、点赞、评论等行为数据记录到 Elasticsearch 中,并构建倒排索引以便快速查询。
  • 推荐算法: 使用 More Like This 查询,根据用户历史观看的视频,推荐相似的视频。结合 function_score 查询,基于用户行为频次加权,个性化推荐排序。
  • 动态调整推荐结果: 使用 Elasticsearch 的聚合分析功能,定期统计受欢迎的视频,并结合流行度(如播放量、点赞数)来调整推荐策略。

详细分析: Elasticsearch 的 More Like This 查询非常适合用于相似内容推荐的场景,能够根据用户的兴趣偏好进行相关视频的推荐。其高效的索引和查询机制,加上支持复杂的查询组合,使得推荐系统既能保持较高的实时性,又能根据动态数据调整推荐结果。

实现步骤

  1. 数据准备与索引设计
  2. 用户行为数据的存储与分析
  3. 基于内容的推荐算法
  4. 基于协同过滤的推荐算法
  5. 综合推荐与实时推荐
  6. 结果展示与优化

1. 数据准备与索引设计

推荐系统的核心是数据,首先我们需要创建 Elasticsearch 索引来存储用户和视频的数据。

视频内容索引

视频索引中包含视频的基础信息,如标题、描述、标签、类别等,这些信息可以用来计算视频的相似度。

PUT /videos
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "description": {
        "type": "text"
      },
      "tags": {
        "type": "keyword"
      },
      "category": {
        "type": "keyword"
      },
      "release_date": {
        "type": "date"
      }
    }
  }
}

用户行为索引

我们还需要存储用户的行为数据,比如他们看过哪些视频,搜索过哪些关键词等。这些数据将用于个性化推荐。

PUT /user_actions
{
  "mappings": {
    "properties": {
      "user_id": {
        "type": "keyword"
      },
      "video_id": {
        "type": "keyword"
      },
      "action_type": {
        "type": "keyword"   # 例如: view, like, search, etc.
      },
      "timestamp": {
        "type": "date"
      }
    }
  }
}

2. 用户行为数据的存储与分析

用户行为数据是个性化推荐的核心依据。我们可以通过 Filebeat 等工具实时采集用户的观看行为数据,并将其存储到 Elasticsearch 中。

例如,用户观看了某个视频,行为数据可能如下:

POST /user_actions/_doc
{
  "user_id": "weige",
  "video_id": "video789",
  "action_type": "view",
  "timestamp": "2024-10-14T12:30:00Z"
}

为了分析用户的兴趣,我们可以使用 Elasticsearch 的聚合功能。比如,统计用户观看最多的类别和标签,以此作为兴趣偏好的基础。

聚合查询:统计某用户观看最多的视频类别

GET /user_actions/_search
{
  "size": 0,
  "query": {
    "term": {
      "user_id": "weige"
    }
  },
  "aggs": {
    "favorite_categories": {
      "terms": {
        "field": "category.keyword",
        "size": 5
      }
    }
  }
}

该查询可以帮助我们了解用户最常观看的前 5 个视频类别。

3. 基于内容的推荐算法

基于内容的推荐算法通过分析用户观看过的视频内容(标题、描述、标签等),为用户推荐相似的视频。我们可以使用 Elasticsearch 的 more_like_this 查询来找到和用户已观看内容相似的视频。

使用 more_like_this 进行基于内容的推荐

假设用户刚刚观看了视频 video789,我们希望找到与该视频内容相似的其他视频。

GET /videos/_search
{
  "query": {
    "more_like_this": {
      "fields": ["title", "description", "tags"],
      "like": [
        {
          "_id": "video789"
        }
      ],
      "min_term_freq": 1,
      "max_query_terms": 12
    }
  }
}

这个查询会根据视频 video789 的标题、描述和标签,推荐相似的视频。min_term_freqmax_query_terms 可以用来调整推荐的相似度。

4. 基于协同过滤的推荐算法

协同过滤(Collaborative Filtering)是另一种常用的推荐算法,它通过分析不同用户的行为数据,寻找用户之间的相似性,从而推荐其他用户喜欢的视频。

查询与用户行为相似的用户

我们可以通过 Elasticsearch 的聚合来查找与当前用户行为相似的其他用户,比如查找同样观看过某个视频的用户。

GET /user_actions/_search
{
  "size": 0,
  "query": {
    "term": {
      "video_id": "video789"
    }
  },
  "aggs": {
    "similar_users": {
      "terms": {
        "field": "user_id.keyword",
        "size": 10
      }
    }
  }
}

通过这个查询,我们找到了所有观看过 video789 的用户列表。接下来,我们可以根据这些用户的观看历史,推荐他们喜欢的视频给当前用户。

基于协同过滤推荐其他用户喜欢的视频

找到与当前用户相似的其他用户后,我们可以查询他们共同观看的视频,并为当前用户推荐这些视频。

GET /user_actions/_search
{
  "size": 10,
  "query": {
    "terms": {
      "user_id": ["weige123", "weige456"]  # 与当前用户相似的用户
    }
  },
  "aggs": {
    "recommended_videos": {
      "terms": {
        "field": "video_id.keyword",
        "size": 5
      }
    }
  }
}

通过这个查询,可以推荐其他用户看过且当前用户还没有观看过的视频。

5. 综合推荐与实时推荐

为了提高推荐的准确性,可以将基于内容的推荐和协同过滤结合在一起,综合考虑用户的兴趣和行为数据。

结合用户兴趣和行为的推荐

首先,我们可以获取用户最喜欢的类别和标签,然后结合用户历史行为推荐符合这些兴趣的热门视频。

GET /videos/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "category": "user_favorite_category" } },
        { "match": { "tags": "user_favorite_tags" } }
      ]
    }
  },
  "sort": [
    { "release_date": { "order": "desc" } }
  ]
}

这种推荐方式综合了用户的兴趣和最新视频内容,有助于提升用户体验。

6. 结果展示与优化

在前端展示推荐结果

推荐结果可以通过前端 API 展示给用户,假设通过 JavaScript 请求 Elasticsearch 来获取推荐内容,代码示例如下:

fetch('http://localhost:9200/videos/_search', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    "query": {
      "more_like_this": {
        "fields": ["title", "description", "tags"],
        "like": [
          {
            "_id": "video789"
          }
        ],
        "min_term_freq": 1,
        "max_query_terms": 12
      }
    }
  })
})
.then(response => response.json())
.then(data => {
  console.log("Recommended videos:", data.hits.hits);
  // 展示推荐视频内容
});

不断优化推荐算法

  • 用户反馈:通过用户对推荐视频的反馈(如点赞、点击等),进一步优化推荐算法。
  • 实时推荐:通过 Kafka 等流处理工具,实时监控用户行为数据,并调整推荐结果。

7. 小结一下

构建个性化推荐系统的关键在于对用户兴趣和行为的深刻理解。通过 Elasticsearch,可以轻松实现以下功能:

  • 内容推荐:基于视频内容相似性为用户推荐视频。
  • 协同过滤:基于相似用户行为推荐视频。
  • 实时推荐:结合用户实时行为和兴趣,提供最新的个性化推荐。

这种推荐系统不仅能提升用户粘性,还能增加视频播放量和广告转化率,为在线视频平台带来更多的商业收益。

4. 商品价格区间统计与筛选

业务场景: 在在线商城中,用户经常根据价格来筛选商品。商城需要提供按价格区间筛选商品的功能,并实时统计每个价格区间内的商品数量,以便用户快速选择符合其预算的商品。这类功能可以通过 Elasticsearch 的聚合查询来高效实现。

解决方案:

  • 数据存储: 将商品的价格字段建模为 numeric 类型,并存储到 Elasticsearch 中。
  • 聚合分析: 使用 Elasticsearch 的 range 聚合功能,将商品按价格区间分类,统计各区间内商品数量。例如:0-100 元,100-500 元,500-1000 元等。
  • 筛选与排序: 配合 filter 查询,支持用户在前端选择价格区间进行筛选,展示符合条件的商品,并按价格升序或降序排列。

详细分析: Elasticsearch 的聚合功能特别适合用于统计类场景。在价格筛选应用中,range 聚合能够实时计算各个价格区间的商品数量,配合过滤查询实现快速筛选。这种机制不仅响应速度快,还能动态适应不断变化的数据规模,保持较高的用户体验。

该方案的实现步骤如下:

  1. 创建商品索引,存储商品信息
  2. 使用 range 查询进行价格区间筛选
  3. 使用聚合统计每个价格区间内的商品数量
  4. 实现实时筛选和动态更新

实现步骤

1. 商品索引创建

首先,我们需要创建一个 Elasticsearch 索引来存储商品数据。商品数据通常包括商品名称、描述、分类、价格等信息,其中价格字段将用于价格区间筛选。

PUT /products
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "description": {
        "type": "text"
      },
      "category": {
        "type": "keyword"
      },
      "price": {
        "type": "float"
      },
      "in_stock": {
        "type": "boolean"
      }
    }
  }
}

2. 插入商品数据

接下来,我们插入一些商品数据以供后续使用。

POST /products/_bulk
{ "index": { "_id": "1" } }
{ "name": "Smartphone A", "description": "A high-end smartphone", "category": "electronics", "price": 499.99, "in_stock": true }
{ "index": { "_id": "2" } }
{ "name": "Laptop B", "description": "A powerful laptop", "category": "electronics", "price": 899.99, "in_stock": true }
{ "index": { "_id": "3" } }
{ "name": "Tablet C", "description": "A mid-range tablet", "category": "electronics", "price": 299.99, "in_stock": true }
{ "index": { "_id": "4" } }
{ "name": "Headphones D", "description": "Noise-cancelling headphones", "category": "accessories", "price": 199.99, "in_stock": true }
{ "index": { "_id": "5" } }
{ "name": "Smartwatch E", "description": "A fitness-oriented smartwatch", "category": "accessories", "price": 149.99, "in_stock": false }

3. 按价格区间筛选商品

用户在商城中可以按照价格区间来筛选商品。例如,用户希望查找价格在 200 到 500 之间的商品。我们可以使用 range 查询来实现这一需求。

价格区间筛选查询示例:

GET /products/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 200,
        "lte": 500
      }
    }
  }
}

该查询返回价格在 200 到 500 之间的商品。用户可以通过调整 gte(大于等于)和 lte(小于等于)参数来修改筛选的价格区间。

4. 统计各价格区间内的商品数量

为了提供用户选择不同价格区间的选项,我们需要统计每个价格区间内的商品数量。可以通过 histogramrange 聚合实现这一功能。

使用 range 聚合统计价格区间商品数量:

GET /products/_search
{
  "size": 0,
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 200 },            # 价格在200以下
          { "from": 200, "to": 500 }, # 价格200到500之间
          { "from": 500, "to": 1000 },# 价格500到1000之间
          { "from": 1000 }            # 价格1000以上
        ]
      }
    }
  }
}

该查询返回每个价格区间内的商品数量。查询结果中的 price_ranges 聚合部分展示了每个区间内的商品数。

5. 结果解析

查询结果会显示每个价格区间内的商品数量:

{
  "aggregations": {
    "price_ranges": {
      "buckets": [
        {
          "key": "*-200.0",
          "doc_count": 2  # 价格在200以下的商品数量
        },
        {
          "key": "200.0-500.0",
          "doc_count": 2  # 价格在200到500之间的商品数量
        },
        {
          "key": "500.0-1000.0",
          "doc_count": 1  # 价格在500到1000之间的商品数量
        },
        {
          "key": "1000.0-*",
          "doc_count": 0  # 价格在1000以上的商品数量
        }
      ]
    }
  }
}

结果中 doc_count 表示每个价格区间内的商品数量。

6. 结合筛选与统计

在实际应用中,用户希望先查看商品的价格区间分布,再选择合适的区间进行进一步筛选。我们可以结合上述步骤,先返回各价格区间的统计数据,再根据用户选择执行相应的 range 查询。

例如,用户在看到价格区间统计后,选择查看 200 到 500 的商品。此时可以执行以下查询:

GET /products/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 200,
        "lte": 500
      }
    }
  }
}

7. 实时筛选与动态更新

通过结合 Elasticsearch 的实时索引更新功能,当商品信息(如价格或库存状态)发生变化时,索引也会动态更新。例如,某个商品的价格调整或库存状态变化时,我们可以通过实时更新索引来反映这些变化。

实时更新商品信息:

假设我们要更新某个商品的价格和库存状态:

POST /products/_update/1
{
  "doc": {
    "price": 479.99,
    "in_stock": false
  }
}

8. 小结一下

通过 Elasticsearch 的 range 查询和聚合功能,能够高效地实现在线商城的商品价格区间筛选和统计功能,具体实现包括:

  • 价格区间的筛选:用户可以根据价格范围筛选商品。
  • 价格区间内商品数量的实时统计:系统可以快速统计每个价格区间内的商品数量,供用户进一步筛选。
  • 实时更新与动态调整:当商品价格或库存发生变化时,系统可以实时反映这些变化,确保数据的准确性。

通过这种方式,用户可以更直观、更方便地根据价格来筛选商品,提升购物体验。

5. 地理位置搜索

业务场景:

某外卖平台希望根据用户的地理位置,推荐附近的餐馆,并根据与用户的距离进行排序。为了实现这一需求,可以利用 Elasticsearch 的地理位置查询(geo-location query)和距离排序功能。通过地理坐标信息(经纬度)存储餐馆的位置,并结合用户的当前位置进行距离计算,快速查询附近的餐馆。

解决方案:

  • 地理位置数据存储: 将餐馆的经纬度信息存储为 geo_point 类型,创建相应的索引。
  • 地理位置查询: 使用 Elasticsearch 的 geo_distance 查询,根据用户当前的地理位置,搜索附近一定距离内的餐馆(如 5 公里内)。
  • 距离排序: 使用 geo_distancesort 功能,根据距离远近对餐馆进行排序,优先展示距离较近的餐馆。
  • 精细化筛选: 结合其他查询条件(如评分、菜系等)进行进一步筛选,提供个性化的餐馆推荐。

详细分析: Elasticsearch 内置的 geo_point 类型和相关的地理位置查询功能,非常适合用于位置相关的业务场景。通过 geo_distance 查询,平台可以迅速筛选出与用户距离较近的餐馆,并进行距离排序,提升用户体验。此外,Elasticsearch 能够轻松扩展到全球范围内的位置数据应用,具有极高的灵活性和扩展性。

实现步骤

  1. 餐馆信息的地理位置索引创建
  2. 插入餐馆数据
  3. 用户当前位置的餐馆搜索
  4. 根据距离排序
  5. 设置搜索范围(限制半径)
  6. 实时更新与扩展

1. 餐馆信息的地理位置索引创建

首先,为了存储餐馆的地理位置,我们需要为餐馆数据创建一个包含 geo_point 类型的索引,geo_point 用于存储地理坐标信息(经纬度)。

创建餐馆索引:

PUT /restaurants
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "description": {
        "type": "text"
      },
      "location": {
        "type": "geo_point"  # 存储餐馆的经纬度信息
      },
      "rating": {
        "type": "float"
      },
      "category": {
        "type": "keyword"
      }
    }
  }
}

2. 插入餐馆数据

接下来,我们将插入一些带有地理位置的餐馆数据。这些数据中包含餐馆的名称、描述、评分、类别以及位置的经纬度信息。

插入示例数据:

POST /restaurants/_bulk
{ "index": { "_id": "1" } }
{ "name": "韭菜鸡蛋", "description": "男人的加油站", "location": { "lat": 40.730610, "lon": -73.935242 }, "rating": 4.5, "category": "Italian" }
{ "index": { "_id": "2" } }
{ "name": "Sushi World", "description": "Authentic china sushi", "location": { "lat": 40.742610, "lon": -73.945242 }, "rating": 4.7, "category": " china" }
{ "index": { "_id": "3" } }
{ "name": "Burger Town", "description": "Best burgers in town", "location": { "lat": 40.729510, "lon": -73.914342 }, "rating": 4.3, "category": "china" }
{ "index": { "_id": "4" } }
{ "name": "Vegan Delight", "description": "Healthy and delicious vegan food", "location": { "lat": 40.715610, "lon": -73.935142 }, "rating": 4.6, "category": "china" }

在这些数据中,每个餐馆的 location 字段存储了其经纬度信息。

3. 用户当前位置的餐馆搜索

为了根据用户的当前位置搜索附近的餐馆,可以使用 Elasticsearch 的 geo_distance 查询来实现。假设用户当前位于某个位置(经纬度:40.730610, -73.935242),我们希望查找这个位置附近的餐馆。

按距离搜索附近餐馆:

GET /restaurants/_search
{
  "query": {
    "geo_distance": {
      "distance": "5km",  # 搜索5公里范围内的餐馆
      "location": {
        "lat": 40.730610,
        "lon": -73.935242
      }
    }
  }
}

这个查询会返回距离用户当前位置 5 公里以内的所有餐馆。

4. 根据距离排序

为了让用户能够优先看到离自己最近的餐馆,我们可以在查询中添加基于距离的排序功能。Elasticsearch 提供了 geo_distance 排序方法,可以按距离升序排列餐馆。

按距离排序的查询:

GET /restaurants/_search
{
  "query": {
    "geo_distance": {
      "distance": "5km",
      "location": {
        "lat": 40.730610,
        "lon": -73.935242
      }
    }
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 40.730610,
          "lon": -73.935242
        },
        "order": "asc",  # 按距离升序排序
        "unit": "km"
      }
    }
  ]
}

这个查询不仅返回了 5 公里范围内的餐馆,还根据距离从近到远进行排序。

5. 设置搜索范围(限制半径)

为了控制搜索的范围,比如用户希望只查找特定半径范围内的餐馆(如 3 公里以内),我们可以通过调整 distance 参数来实现。

搜索 3 公里以内的餐馆:

GET /restaurants/_search
{
  "query": {
    "geo_distance": {
      "distance": "3km",  # 搜索3公里范围内的餐馆
      "location": {
        "lat": 40.730610,
        "lon": -73.935242
      }
    }
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 40.730610,
          "lon": -73.935242
        },
        "order": "asc",  # 按距离排序
        "unit": "km"
      }
    }
  ]
}

6. 实时更新与扩展

餐馆的位置和营业状态可能会随着时间发生变化,例如某家餐馆关闭或新餐馆开张。因此,我们需要支持餐馆数据的实时更新。Elasticsearch 提供了实时索引更新功能,可以方便地更新餐馆的位置信息。

实时更新餐馆的地理位置:

如果某家餐馆位置发生了变化(比如迁址),我们可以通过以下命令更新其位置信息:

POST /restaurants/_update/1
{
  "doc": {
    "location": {
      "lat": 40.735610,
      "lon": -73.930242  # 更新后的新位置
    }
  }
}

餐馆信息实时更新后,新的查询结果将自动反映变化。

扩展功能:根据餐馆评分进行筛选

除了按距离筛选外,用户还可能希望按餐馆评分来过滤结果。我们可以将评分筛选条件添加到查询中,确保返回的餐馆不仅距离较近,还符合评分要求。

添加评分过滤的查询:

GET /restaurants/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "geo_distance": {
            "distance": "5km",
            "location": {
              "lat": 40.730610,
              "lon": -73.935242
            }
          }
        },
        {
          "range": {
            "rating": {
              "gte": 4.5  # 筛选评分大于或等于4.5的餐馆
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 40.730610,
          "lon": -73.935242
        },
        "order": "asc",  # 按距离排序
        "unit": "km"
      }
    }
  ]
}

这个查询将返回距离用户 5 公里以内且评分不低于 4.5 分的餐馆。

7. 小结一下

通过 Elasticsearch 的地理位置查询功能,我们可以高效地实现外卖平台的附近餐馆搜索和排序功能,具体实现包括:

  • 地理位置存储:使用 geo_point 字段存储餐馆的经纬度信息。
  • 距离查询:根据用户的地理位置查找附近的餐馆。
  • 按距离排序:确保用户优先看到距离最近的餐馆。
  • 评分筛选:结合餐馆评分进行筛选,提升用户体验。
  • 实时更新:支持餐馆信息的动态更新,确保查询结果实时准确。

通过这些功能,用户可以方便地找到附近的优质餐馆,提升了外卖平台的用户体验和服务效率。

总结

这五个案例涵盖了 Elasticsearch 在全文搜索、日志分析、推荐系统、数据聚合与筛选、地理位置搜索等典型业务场景中的应用。通过合理的索引设计、灵活的查询与聚合功能,Elasticsearch 能够满足多种复杂场景下的高效数据检索与分析需求。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号