ES 基本使用与二次封装

概述

基本了解

Elasticsearch 是一个开源的分布式搜索和分析引擎,基于 Apache Lucene 构建。它提供了对海量数据的快速全文搜索、结构化搜索和分析功能,是目前流行的大数据处理工具之一。主要特点即高效搜索、分布式存储、拓展性强

核心功能

  • 全文搜索: 提供对文本数据的快速匹配和排名能力
  • 实时数据处理: 支持实时写入、更新、删除和搜索
  • 分布式存储: 能够将数据分片(shard)存储在多个节点中,具有高可用性和容错性
  • 聚合分析: 支持对大数据进行复杂的统计分析,如平均值、最大值、分组统计等

核心概念

索引

索引是数据的逻辑组织单位,可以类比为数据库中的“数据库”。每个索引由多个文档组成,类似于一本书的目录

索引是查询的入口点,比如当你要查“小说类书籍”,会直接到“小说书架”查找,而不是其他书架

文档

文档是数据存储的基本单元,相当于关系型数据库中的“行”。每个文档是以 JSON 格式存储的键值对集合(这一点注意和MySQL进行区分)

每本书可以看作一个文档。每本书有具体的属性,比如书名、作者、出版年份、ISBN 号等

{"title": "The Catcher in the Rye","author": "J.D. Salinger","year": 1951,"genre": "Fiction"
}

字段

文档中的属性或键值对,比如书的“标题”、“作者”、“出版年份”等;可以简单理解为一本书的详细信息

注意:字段的类型可以指定(例如 textkeywordinteger 等),并决定了如何处理这些数据。例如,“标题”是 text 类型,支持全文搜索,而“ISBN” 是 keyword 类型,只支持精确匹配

分片

分片是数据的物理存储单位。每个索引可以分成多个分片,分布在不同的节点上以提升性能

类似哈利波特系列的书很多,在图书馆中分别放在一楼和二楼,当寻找的时候同时派人去一楼和二楼寻找,这样就可以节省寻找的时间;这也就对应着分片的主要的作用:分片使得查询和存储可以并行处理,提高系统性能;分片还提供了冗余和容错能力(通过副本)

节点

节点是 Elasticsearch 集群中的一个实例。每个节点都有特定的角色,比如主节点、数据节点

例如在图书馆中,可以将每个楼层看作一个节点,可以存储数据,也可以帮助查询;比如,楼层 1 存储小说,楼层 2 存储科技书籍,但两层楼之间可以互相配合

不同的节点可以扮演不同角色

  • 主节点(Master Node):负责管理整个图书馆的目录(分片的分布情况)
  • 数据节点(Data Node):实际存储书籍和处理搜索请求
  • 协调节点(Coordinator Node):负责分发和合并查询结果

集群

集群是一组相互协作的节点,共同存储和处理数据

整个图书馆可以看作是一个 Elasticsearch 集群,包含多个楼层(节点),书架(索引)被分布在各个楼层(节点)上

  • 集群中的节点可以随时增加或减少,确保高扩展性
  • 当有大批人需要搜索数据时,集群可以通过多个节点的并发处理快速完成任务

ES高性能的原因

倒排索引

倒排索引是 Elasticsearch 搜索速度快的核心技术,它记录的是每个单词在文档中的位置,而不是逐个文档搜索所有内容。

例如有三个文档,记录的内容分别是:文档1: "猫喜欢鱼";文档2: "狗喜欢骨头";文档3: "鱼喜欢水",那么倒排索引就会建立如下索引

词语        文档ID
喜欢        1, 2, 3
猫          1
狗          2
鱼          1, 3
骨头        2
水          3

当查找喜欢这个单词的时候,Elasticsearch就不会扫描全部的文档,而是直接从倒排索引中找到包含该单词的文档列表即可

综上所述:ES使用倒排索引避免了逐个扫描文档,直接定位到包含目标关键词的文档,查询时间随文档总量的增长几乎不变。

分布式架构

Elasticsearch 将数据分成多个 分片 存储在不同的节点上,查询时,会并行搜索所有分片,最后合并结果返回。

场景还假设在上面文档的内容中,将索引分为3个分片中,分布在三个节点上

  • 分片1: 包含文档 1~100
  • 分片2: 包含文档 101~200
  • 分片3: 包含文档 201~300

当用户查询“鱼”,Elasticsearch 会同时向 3 个分片发出请求:

  • 分片1 返回包含“鱼”的文档 1
  • 分片2 无结果
  • 分片3 返回包含“鱼”的文档 3

最后将结果合并返回给用户;就像超时收银的,不会将所有的客户都在一个收银节点,通过设置多个收银节点完成最后的收银工作

缓存机制

Elasticsearch 使用内存缓存和文件系统缓存来存储常用的查询结果,如果相同的查询被多次请求,Elasticsearch 会直接从缓存中返回结果,而无需重新计算

查询优化和分析器

lasticsearch 会对查询请求进行优化,比如避免不必要的计算、合并多个相同的查询条件等

分析器(Analyzer)是对文本数据的处理器,通常会对字段的内容进行分词、去停用词(如 "the"、"is")、小写化等操作;借助分词器和索引机制让全文搜索更加精准和迅速

使用

添加与访问测试

 创建索引库

POST /user/_doc
{"settings": {"analysis": {"analyzer": {"ik": {"tokenizer": "ik_max_word"}}}},"mappings": {"dynamic": true,"properties": {"nickname": {"type": "text","analyzer": "ik_max_word"},"user_id": {"type": "keyword","analyzer": "standard"},"phone": {"type": "keyword","analyzer": "standard"},"description": {"type": "text","enabled": false},"avatar_id": {"type": "keyword","enabled": false}}}
}

新增数据

POST /user/_doc/_bulk
{ "index": { "_id": "1" } }
{ "user_id": "USER4b862aaa-2df8654a-7eb4bb65-e3507f66", "nickname": "昵称1", "phone": "手机号1", "description": "签名1", "avatar_id": "头像1" }
{ "index": { "_id": "2" } }
{ "user_id": "USER14eeea5-442771b9-0262e455-e46631d1", "nickname": "昵称2", "phone": "手机号2", "description": "签名2", "avatar_id": "头像2" }
{ "index": { "_id": "3" } }
{ "user_id": "USER484a6734-03a124f0-996c169d-d05c1869", "nickname": "昵称3", "phone": "手机号3", "description": "签名3", "avatar_id": "头像3" }
{ "index": { "_id": "4" } }
{ "user_id": "USER186ade83-4460d4a6-8c08068f-83127b5d", "nickname": "昵称4", "phone": "手机号4", "description": "签名4", "avatar_id": "头像4" }
{ "index": { "_id": "5" } }
{ "user_id": "USER6f19d074-c33891cf-23bf5a83-57189c19", "nickname": "昵称5", "phone": "手机号5", "description": "签名5", "avatar_id": "头像5" }
{ "index": { "_id": "6" } }
{ "user_id": "USER97605c64-9833ebb7-d0455353-35a59195", "nickname": "昵称6", "phone": "手机号6", "description": "签名6", "avatar_id": "头像6" }

 搜索所有数据

ES客户端接口

参数说明

  • indexName:指定 Elasticsearch 的索引名称
  • docType::指定文档的类型(在 Elasticsearch 7.x+ 中已被弃用)
  • id:文档的唯一标识符,用于获取、索引或删除
  • body:Elasticsearch 的请求体,通常为 JSON 格式
  • routing:路由参数,如果为空,则使用默认的路由规则

常用接口功能

搜索

  • 在 Elasticsearch 集群中搜索指定的索引,直到成功为止
  • 如果所有节点都未响应,则抛出 ConnectionException
cpr::Response search(const std::string &indexName,const std::string &docType,const std::string &body,const std::string &routing = std::string());

获取文档

  • 从集群中获取指定 ID 的文档
  • 如果所有节点都未响应,则抛出 ConnectionException
cpr::Response get(const std::string &indexName,const std::string &docType,const std::string &id = std::string(),const std::string &routing = std::string());

索引文档

  • 在集群中新增或更新文档
  • 如果 ID 未提供,Elasticsearch 将自动生成 ID
  • 如果所有节点都未响应,则抛出 ConnectionException
cpr::Response index(const std::string &indexName,const std::string &docType,const std::string &id,const std::string &body,const std::string &routing = std::string());

删除文档

  • 从集群中删除指定 ID 的文档
  • 如果所有节点都未响应,则抛出 ConnectionException
cpr::Response remove(const std::string &indexName,const std::string &docType,const std::string &id,const std::string &routing = std::string());

基本操作

预处理

创建索引然后插入数据 

搜索 

 通过客户端对指定内容进行搜索

二次封装

封装思路

索引创建

  • 动态设置索引名称、索引类型
  • 添加字段、设置字段类型及分词器设置
  • 构造的核心逻辑则通过Json::Value构造对应的请求正文

数据新增

  • 特定索引中插入文档
  • 文档格式以JSON构造,灵活支持动态字段和值

数据查询

  • 封装搜索语法 ,生成符合ES查询语法的JSON格式请求体
  • 支持复杂的查询条件,例如多条件组合 

数据删除

  • 封装删除请求接口,可以按索引或文档 ID 进行删除

具体实现

工具函数

Serialize:JSON数据序列化,将Value转换为字符串

    // 序列化bool Serialize(const Json::Value &val , std::string &dst){Json::StreamWriterBuilder swb;swb.settings_["emitUTF8"] = true;std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss;int ret = sw->write(val, &ss);if (ret != 0) {std::cout << "Json反序列化失败!\n";return false;}dst = ss.str();return true;}

UnSerialize:JSON数据反序列化,将字符串转换为Value

    // 反序列化bool UnSerialize(const std::string &src, Json::Value &val){Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err;bool ret = cr->parse(src.c_str(), src.c_str() + src.size(), &val, &err);if (ret == false) {std::cout << "json反序列化失败: " << err << std::endl;return false;}return true;}   

创建索引

append

  • 作用:给索引字段添加映射配置(字段名称,字段类型,分析器名称,是否启用该字段)
  • 实现逻辑:创建字段的JSON配置,然后将其添加到_properties中,然后字段映射以键值对的形式进行存储
ESIndex& append(const std::string &key, const std::string &type = "text", const std::string &analyzer = "ik_max_word", bool enabled = true) {Json::Value fields;fields["type"] = type;fields["analyzer"] = analyzer;if (enabled == false ) fields["enabled"] = enabled;_properties[key] = fields;return *this;}

create

  • 作用:根据settings和mappings配置,通过客户端发送HTTP请求创建Elasticsearch索引
  • 逻辑
    • 设置索引的mappins配置
    • 序列化索引配置为JSON字符串
    • 通过_client调用Elasticsearch的接口创建索引
bool create(const std::string &index_id = "default_index_id") {Json::Value mappings;mappings["dynamic"] = true;mappings["properties"] = _properties;_index["mappings"] = mappings;std::string body;bool ret = Serialize(_index, body);if (ret == false) {LOG_ERROR("索引序列化失败!");return false;}LOG_DEBUG("{}", body);//2. 发起搜索请求try {auto rsp = _client->index(_name, _type, index_id, body);if (rsp.status_code < 200 || rsp.status_code >= 300) {LOG_ERROR("创建ES索引 {} 失败,响应状态码异常: {}", _name, rsp.status_code);return false;}} catch(std::exception &e) {LOG_ERROR("创建ES索引 {} 失败: {}", _name, e.what());return false;}return true;}

新增数据

append

  • 作用:添加一条记录到_item中
  • 逻辑:将传入的键值对添加到_item对象中
template<typename T>ESInsert &append(const std::string &key, const T &val){_item[key] = val;return *this;}

insert

  • 作用:将_item序列化为JSON字符串
  • 逻辑
    • 通过序列化函数将_item转换为JSON格式的字符串body
    • 发起请求,将JSON数据发送到Elasticsearch中,然后检查是否发送成功
 bool insert(const std::string id = "") {std::string body;bool ret = Serialize(_item, body);if (ret == false) {LOG_ERROR("索引序列化失败!");return false;}LOG_DEBUG("{}", body);//2. 发起搜索请求try {auto rsp = _client->index(_name, _type, id, body);if (rsp.status_code < 200 || rsp.status_code >= 300) {LOG_ERROR("新增数据 {} 失败,响应状态码异常: {}", body, rsp.status_code);return false;}} catch(std::exception &e) {LOG_ERROR("新增数据 {} 失败: {}", body, e.what());return false;}return true;}

删除数据

remove

  • 作用:删除指定ID的文档
  • 逻辑:
    • 调用客户端的remove方法,向Elasticsearch发送删除请求
    • 判断是否成功,同时对错误进行捕捉,防止被其他错误中断
class ESRemove {public:ESRemove(std::shared_ptr<elasticlient::Client> &client, const std::string &name, const std::string &type = "_doc"):_name(name), _type(type), _client(client){}bool remove(const std::string &id) {try {auto rsp = _client->remove(_name, _type, id);if (rsp.status_code < 200 || rsp.status_code >= 300) {LOG_ERROR("删除数据 {} 失败,响应状态码异常: {}", id, rsp.status_code);return false;}} catch(std::exception &e) {LOG_ERROR("删除数据 {} 失败: {}", id, e.what());return false;}return true;}private:std::string _name;std::string _type;std::shared_ptr<elasticlient::Client> _client;
};

数据查询

  • 主要功能
    • 提供接口构建多条件的Elasticsearch布尔查询
    • 将查询条件序列化为JSON格式的请求体
    • 调用Elasticsearch客户端执行查询
    • 解析查询结果并返回文档数据
class ESSearch {public:ESSearch(std::shared_ptr<elasticlient::Client> &client, const std::string &name, const std::string &type = "_doc"):_name(name), _type(type), _client(client){}ESSearch& append_must_not_terms(const std::string &key, const std::vector<std::string> &vals) {Json::Value fields;for (const auto& val : vals){fields[key].append(val);}Json::Value terms;terms["terms"] = fields;_must_not.append(terms);return *this;}ESSearch& append_should_match(const std::string &key, const std::string &val) {Json::Value field;field[key] = val;Json::Value match;match["match"] = field;_should.append(match);return *this;}ESSearch& append_must_term(const std::string &key, const std::string &val) {Json::Value field;field[key] = val;Json::Value term;term["term"] = field;_must.append(term);return *this;}ESSearch& append_must_match(const std::string &key, const std::string &val){Json::Value field;field[key] = val;Json::Value match;match["match"] = field;_must.append(match);return *this;}Json::Value search(){Json::Value cond;if (_must_not.empty() == false) cond["must_not"] = _must_not;if (_should.empty() == false) cond["should"] = _should;if (_must.empty() == false) cond["must"] = _must;Json::Value query;query["bool"] = cond;Json::Value root;root["query"] = query;std::string body;bool ret = Serialize(root, body);if (ret == false) {LOG_ERROR("索引序列化失败!");return Json::Value();}LOG_DEBUG("{}", body);//2. 发起搜索请求cpr::Response rsp;try {rsp = _client->search(_name, _type, body);if (rsp.status_code < 200 || rsp.status_code >= 300) {LOG_ERROR("检索数据 {} 失败,响应状态码异常: {}", body, rsp.status_code);return Json::Value();}} catch(std::exception &e) {LOG_ERROR("检索数据 {} 失败: {}", body, e.what());return Json::Value();}//3. 需要对响应正文进行反序列化LOG_DEBUG("检索响应正文: [{}]", rsp.text);Json::Value json_res;ret = UnSerialize(rsp.text, json_res);if (ret == false) {LOG_ERROR("检索数据 {} 结果反序列化失败", rsp.text);return Json::Value();}return json_res["hits"]["hits"];}private:std::string _name;std::string _type;Json::Value _must_not;Json::Value _should;Json::Value _must;std::shared_ptr<elasticlient::Client> _client;
};
#pragma once
#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <json/json.h>
#include <iostream>
#include <memory>
#include "logger.hpp"namespace mag {/// 工具函数/*** @brief 将 Json::Value 数据序列化为 JSON 字符串* @param val 要序列化的 Json::Value 对象* @param dst 序列化后的 JSON 字符串* @return true 序列化成功* @return false 序列化失败*/bool Serialize(const Json::Value &val, std::string &dst) {Json::StreamWriterBuilder swb;swb.settings_["emitUTF8"] = true; // 输出为 UTF-8 编码std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss;int ret = sw->write(val, &ss); // 执行序列化if (ret != 0) {std::cout << "Json序列化失败!\n";return false;}dst = ss.str(); // 将结果写入目标字符串return true;}/*** @brief 将 JSON 字符串反序列化为 Json::Value 对象* @param src 待解析的 JSON 字符串* @param val 解析后的 Json::Value 对象* @return true 反序列化成功* @return false 反序列化失败*/bool UnSerialize(const std::string &src, Json::Value &val) {Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err;bool ret = cr->parse(src.c_str(), src.c_str() + src.size(), &val, &err); // 执行解析if (!ret) {std::cout << "Json反序列化失败: " << err << std::endl;return false;}return true;}/// 索引创建模块/*** @brief 索引创建类:用于定义和创建 Elasticsearch 索引*/class ESIndex {public:/*** @brief 构造函数:初始化索引创建模块* @param client Elasticsearch 客户端实例* @param name 索引名称* @param type 文档类型(默认 "_doc")*/ESIndex(std::shared_ptr<elasticlient::Client> &client, const std::string &name, const std::string &type = "_doc"):_name(name), _type(type), _client(client) {Json::Value analysis;Json::Value analyzer;Json::Value ik;Json::Value tokenizer;tokenizer["tokenizer"] = "ik_max_word"; // 设置分词器为 ik_max_wordik["ik"] = tokenizer;analyzer["analyzer"] = ik;analysis["analysis"] = analyzer;_index["settings"] = analysis; // 设置索引的 settings}/*** @brief 添加字段映射* @param key 字段名称* @param type 字段类型(默认 "text")* @param analyzer 分词器名称(默认 "ik_max_word")* @param enabled 是否启用字段(默认 true)* @return ESIndex& 支持链式调用*/ESIndex& append(const std::string &key, const std::string &type = "text", const std::string &analyzer = "ik_max_word", bool enabled = true) {Json::Value fields;fields["type"] = type;       // 设置字段类型fields["analyzer"] = analyzer; // 设置字段分词器if (!enabled) fields["enabled"] = enabled; // 如果禁用字段_properties[key] = fields;  // 添加字段到映射配置中return *this;}/*** @brief 创建索引* @param index_id 索引 ID(默认 "default_index_id")* @return true 创建成功* @return false 创建失败*/bool create(const std::string &index_id = "default_index_id") {Json::Value mappings;mappings["dynamic"] = true;          // 设置动态映射mappings["properties"] = _properties; // 添加字段映射_index["mappings"] = mappings;std::string body;bool ret = Serialize(_index, body); // 将索引配置序列化为 JSONif (!ret) {LOG_ERROR("索引序列化失败!");return false;}LOG_DEBUG("{}", body);try {auto rsp = _client->index(_name, _type, index_id, body); // 调用客户端创建索引if (rsp.status_code < 200 || rsp.status_code >= 300) {LOG_ERROR("创建ES索引 {} 失败,响应状态码异常: {}", _name, rsp.status_code);return false;}} catch (std::exception &e) {LOG_ERROR("创建ES索引 {} 失败: {}", _name, e.what());return false;}return true;}private:std::string _name;                  // 索引名称std::string _type;                  // 文档类型Json::Value _properties;            // 字段映射配置Json::Value _index;                 // 索引完整配置std::shared_ptr<elasticlient::Client> _client; // 客户端实例};/// 索引插入模块/*** @brief 索引插入类:支持动态数据插入到 Elasticsearch 索引*/class ESInsert {public:/*** @brief 构造函数:初始化插入模块* @param client Elasticsearch 客户端实例* @param name 索引名称* @param type 文档类型(默认 "_doc")*/ESInsert(std::shared_ptr<elasticlient::Client> &client, const std::string &name, const std::string &type = "_doc"):_name(name), _type(type), _client(client) {}/*** @brief 添加字段和数据* @tparam T 数据类型* @param key 字段名称* @param val 字段值* @return ESInsert& 支持链式调用*/template<typename T>ESInsert& append(const std::string &key, const T &val) {_item[key] = val; // 动态添加字段return *this;}/*** @brief 插入数据到 Elasticsearch* @param id 文档 ID(默认为自动生成)* @return true 插入成功* @return false 插入失败*/bool insert(const std::string id = "") {std::string body;bool ret = Serialize(_item, body); // 将数据序列化为 JSONif (!ret) {LOG_ERROR("数据序列化失败!");return false;}LOG_DEBUG("{}", body);try {auto rsp = _client->index(_name, _type, id, body); // 调用客户端插入数据if (rsp.status_code < 200 || rsp.status_code >= 300) {LOG_ERROR("插入数据 {} 失败,响应状态码异常: {}", body, rsp.status_code);return false;}} catch (std::exception &e) {LOG_ERROR("插入数据 {} 失败: {}", body, e.what());return false;}return true;}private:std::string _name;                  // 索引名称std::string _type;                  // 文档类型Json::Value _item;                  // 待插入的数据std::shared_ptr<elasticlient::Client> _client; // 客户端实例};/// 数据删除模块/*** @brief 删除模块:删除 Elasticsearch 中的文档*/class ESRemove {public:/*** @brief 构造函数:初始化删除模块* @param client Elasticsearch 客户端实例* @param name 索引名称* @param type 文档类型(默认 "_doc")*/ESRemove(std::shared_ptr<elasticlient::Client> &client, const std::string &name, const std::string &type = "_doc"):_name(name), _type(type), _client(client) {}/*** @brief 删除指定文档* @param id 文档 ID* @return true 删除成功* @return false 删除失败*/bool remove(const std::string &id) {try {auto rsp = _client->remove(_name, _type, id); // 调用客户端删除文档if (rsp.status_code < 200 || rsp.status_code >= 300) {LOG_ERROR("删除数据 {} 失败,响应状态码异常: {}", id, rsp.status_code);return false;}} catch (std::exception &e) {LOG_ERROR("删除数据 {} 失败: {}", id, e.what());return false;}return true;}private:std::string _name;                  // 索引名称std::string _type;                  // 文档类型std::shared_ptr<elasticlient::Client> _client; // 客户端实例};/// 数据查找模块/*** @brief 数据查找模块:用于执行复杂查询*/class ESSearch {public:/*** @brief 构造函数:初始化查询模块* @param client Elasticsearch 客户端实例* @param name 索引名称* @param type 文档类型(默认 "_doc")*/ESSearch(std::shared_ptr<elasticlient::Client> &client, const std::string &name, const std::string &type = "_doc"):_name(name), _type(type), _client(client) {}/*** @brief 添加 must_not 条件* @param key 字段名称* @param vals 排除的值列表* @return ESSearch& 支持链式调用*/ESSearch& append_must_not_terms(const std::string &key, const std::vector<std::string> &vals) {Json::Value fields;for (const auto &val : vals) {fields[key].append(val);}Json::Value terms;terms["terms"] = fields;_must_not.append(terms); // 添加到 must_not 条件return *this;}/*** @brief 添加 should 条件* @param key 字段名称* @param val 匹配的值* @return ESSearch& 支持链式调用*/ESSearch& append_should_match(const std::string &key, const std::string &val) {Json::Value field;field[key] = val;Json::Value match;match["match"] = field;_should.append(match); // 添加到 should 条件return *this;}/*** @brief 添加 must 条件(精确匹配)* @param key 字段名称* @param val 精确匹配的值* @return ESSearch& 支持链式调用*/ESSearch& append_must_term(const std::string &key, const std::string &val) {Json::Value field;field[key] = val;Json::Value term;term["term"] = field;_must.append(term); // 添加到 must 条件return *this;}/*** @brief 添加 must 条件(模糊匹配)* @param key 字段名称* @param val 模糊匹配的值* @return ESSearch& 支持链式调用*/ESSearch& append_must_match(const std::string &key, const std::string &val) {Json::Value field;field[key] = val;Json::Value match;match["match"] = field;_must.append(match); // 添加到 must 条件return *this;}/*** @brief 执行查询* @return Json::Value 查询结果*/Json::Value search() {Json::Value cond;if (!_must_not.empty()) cond["must_not"] = _must_not;if (!_should.empty()) cond["should"] = _should;if (!_must.empty()) cond["must"] = _must;Json::Value query;query["bool"] = cond;Json::Value root;root["query"] = query;std::string body;bool ret = Serialize(root, body); // 序列化查询条件if (!ret) {LOG_ERROR("查询条件序列化失败!");return Json::Value();}LOG_DEBUG("查询请求体: {}", body);cpr::Response rsp;try {rsp = _client->search(_name, _type, body); // 执行查询if (rsp.status_code < 200 || rsp.status_code >= 300) {LOG_ERROR("查询失败,响应状态码异常: {}", rsp.status_code);return Json::Value();}} catch (std::exception &e) {LOG_ERROR("查询失败: {}", e.what());return Json::Value();}LOG_DEBUG("查询响应: {}", rsp.text);Json::Value json_res;ret = UnSerialize(rsp.text, json_res); // 解析响应结果if (!ret) {LOG_ERROR("查询结果反序列化失败!");return Json::Value();}return json_res["hits"]["hits"]; // 返回查询结果}private:std::string _name;                  // 索引名称std::string _type;                  // 文档类型Json::Value _must_not;              // must_not 条件Json::Value _should;                // should 条件Json::Value _must;                  // must 条件std::shared_ptr<elasticlient::Client> _client; // 客户端实例};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/887683.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

矩阵的拼接

矩阵的拼接分为横向拼接和纵向拼接 注意&#xff1a;横向拼接要求两矩阵行数相同&#xff0c;纵向拼接要求两矩阵列数相同 h o r z c a t horzcat horzcat和 v e r t c a t vertcat vertcat函数 h o r z c a t ( a , b ) horzcat(a,b) horzcat(a,b)将 a a a和 b b b横向拼接&a…

【Qt】重写QComboBox下拉展示多列数据

需求 点击QComboBox时&#xff0c;下拉列表以多行多列的表格展示出来。 实现 直接上代码&#xff1a; #include <QComboBox> #include <QTableWidget> #include <QVBoxLayout> #include <QWidget> #include <QEvent> #include <QMouseEve…

七牛云AIGC内容安全方案助力企业合规创新

随着人工智能生成内容(AIGC)技术的飞速发展,内容审核的难度也随之急剧上升。在传统审核场景中,涉及色情、政治、恐怖主义等内容的标准相对清晰明确,但在AIGC的应用场景中,这些界限变得模糊且难以界定。用户可能通过交互性引导AI生成违规内容,为审核工作带来了前所未有的不可预测…

告别 Kafka,拥抱 Databend:构建高效低成本的用户行为分析体系

用户行为数据埋点指标是数据仓库中不可或缺的重要数据源之一&#xff0c;同时也是企业最宝贵的资产之一。通常情况下&#xff0c;用户行为数据分析包含两大数据源&#xff1a;用户行为分析日志和上游关系型数据库&#xff08;如 MySQL&#xff09;。基于这些数据&#xff0c;企…

数据结构 (8)线性表的应用——一元多项式的表示及应用

一、一元多项式的定义 一元多项式是代数学研究的基本对象之一&#xff0c;可以表示为&#xff1a; P_n(x) p_0 p_1x p_2xn 其中&#xff0c;p_0, p_1, ..., p_n 是数域 F 中的数&#xff0c;n 是非负整数&#xff0c;x 是变量。 二、一元多项式的线性表表示 在计算机中&…

如何安全高效地打开和管理动态链接库(DLL)?系统提示dll丢失问题的多种有效修复指南

动态链接库&#xff08;DLL&#xff09;文件是Windows操作系统中非常重要的一部分&#xff0c;它们包含了程序运行所需的代码和数据。当系统提示DLL文件丢失时&#xff0c;可能会导致应用程序无法正常运行。以下是一些安全高效地打开和管理DLL文件以及修复DLL丢失问题的方法&am…

基于微信小程序的平价药房管理系统+LW参考示例

1.项目介绍 系统角色&#xff1a;管理员、医生、普通用户功能模块&#xff1a;用户管理、医生管理、药品分类管理、药品信息管理、在线问诊管理、生活常识管理、日常提醒管理、过期处理、订单管理等技术选型&#xff1a;SpringBoot&#xff0c;Vue&#xff0c;uniapp等测试环境…

鸿蒙ArkUI-X已更新适配API13啦

ArkUI-X 5.0.1 Release版配套OpenHarmony 5.0.1 Rlease&#xff0c;API 13&#xff0c;新增适配部分API 13接口支持跨平台&#xff1b;框架能力进一步完善&#xff0c;支持Android应用非压缩模式&#xff0c;支持Android Fragment对接跨平台。ACE Tools工具易用性提升&#xff…

《生成式 AI》课程 第3講 CODE TASK执行文章摘要的机器人

课程 《生成式 AI》课程 第3講&#xff1a;訓練不了人工智慧嗎&#xff1f;你可以訓練你自己-CSDN博客 任务1:总结 1.我们希望你创建一个可以执行文章摘要的机器人。 2.设计一个提示符&#xff0c;使语言模型能够对文章进行总结。 model: gpt-4o-mini,#gpt-3.5-turbo, import…

【人工智能】深入解析GPT、BERT与Transformer模型|从原理到应用的完整教程

在当今人工智能迅猛发展的时代&#xff0c;自然语言处理&#xff08;NLP&#xff09;领域涌现出许多强大的模型&#xff0c;其中GPT、BERT与Transformer无疑是最受关注的三大巨头。这些模型不仅在学术界引起了广泛讨论&#xff0c;也在工业界得到了广泛应用。那么&#xff0c;G…

Python开发环境搭建+conda管理环境

下载Miniconda 推荐从清华镜像下载安装包 Index of /anaconda/miniconda/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 打开网页后&#xff0c;下拉到最后找到Miniconda3-latest前缀的文件&#xff0c;或者网页中直接搜索Miniconda3-latest&#xff0c;都可以找…

python控制鼠标,键盘,adb

python控制鼠标&#xff0c;键盘&#xff0c;adb 听说某系因为奖学金互相举报&#xff0c;好像拿不到要命一样。不禁想到几天前老墨偷走丁胖子的狗&#xff0c;被丁胖子逮到。他面对警察的问询面不改色坚持自我&#xff0c;反而是怒气冲冲的丁胖子被警察认为是偷狗贼。我觉得这…

使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件

使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件 文章目录 使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件什么是 exe4j准备工作打包 Spring Boot 项目为 EXE 文件1.启动 exe4j2. 选择项目类型3. 配置项目名称和输出目录4. 配置项目类型或可执行文件名称5. java配…

【操作文档】mysql分区操作步骤.docx

1、建立分区表 执行 tb_intercept_notice表-重建-添加分区.sql 文件&#xff1b; DROP TABLE IF EXISTS tb_intercept_notice_20241101_new; CREATE TABLE tb_intercept_notice_20241101_new (id char(32) NOT NULL COMMENT id,number varchar(30) NOT NULL COMMENT 号码,cre…

使用 pycharm 新建不使用 python 虚拟环境( venv、conda )的工程

有时候我们发现一个好玩的 demo&#xff0c;想赶快在电脑上 pip install 一下跑起来&#xff0c;发现因为 python 的 venv、conda 环境还挺费劲的&#xff0c;因为随着时间的发展&#xff0c;之前记得很清楚的 venv、conda 的用法&#xff0c;不经常使用&#xff0c;半天跑不起…

安全设备-日志审计-数据管理

7 日志审计系统数据管理 7.1 日志审计系统数据管理概述 日志审计系统数据管理系统自身数据存储管理&#xff0c;并对自身配置、采集的日志进行统一管理。 7.2 日志审计系统数据管理配置举例 7.2.1 用户场景 用户通过数据管理配置&#xff0c;实现对系统的备份、清理、转存…

简单的Activiti Modoler 流程在线编辑器

简单的Activiti Modoler 流程在线编辑器 1.需求 我们公司使用的流程是activiti5.22.0&#xff0c;版本有些老了&#xff0c;然后使用的编辑器都是eclipse的流程编辑器插件&#xff0c;每次编辑流程需要打开eclipse进行编辑&#xff0c;然后再导入到项目里面&#xff0c;不是特…

玩转 uni-app 静态资源 static 目录的条件编译

一. 前言 老生常谈&#xff0c;了解 uni-app 的开发都知道&#xff0c;uni-app 可以同时支持编译到多个平台&#xff0c;如小程序、H5、移动端 App 等。它的多端编译能力是 uni-app 的一大特点&#xff0c;让开发者可以使用同一套代码基于 Vue.js 的语法编写程序&#xff0c;然…

云技术-docker

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团…

【C++】从C语言到C++学习指南

如果你也是从C语言一路过来的&#xff0c;那么请一起看下去吧&#xff01; 文章目录 面型对象程序设计C基础C和C一些语法区别C在非对象方面对C语言的扩充C的一些标准&#xff08;兼容旧标准&#xff09; 首先&#xff0c;在C的学习中&#xff0c;我们要时刻清醒一点&#xff1…