ES 基本使用与二次封装
概述
基本了解
Elasticsearch 是一个开源的分布式搜索和分析引擎,基于 Apache Lucene 构建。它提供了对海量数据的快速全文搜索、结构化搜索和分析功能,是目前流行的大数据处理工具之一。主要特点即高效搜索、分布式存储、拓展性强
核心功能
- 全文搜索: 提供对文本数据的快速匹配和排名能力
- 实时数据处理: 支持实时写入、更新、删除和搜索
- 分布式存储: 能够将数据分片(shard)存储在多个节点中,具有高可用性和容错性
- 聚合分析: 支持对大数据进行复杂的统计分析,如平均值、最大值、分组统计等
核心概念
索引
索引是数据的逻辑组织单位,可以类比为数据库中的“数据库”。每个索引由多个文档组成,类似于一本书的目录
索引是查询的入口点,比如当你要查“小说类书籍”,会直接到“小说书架”查找,而不是其他书架
文档
文档是数据存储的基本单元,相当于关系型数据库中的“行”。每个文档是以 JSON 格式存储的键值对集合(这一点注意和MySQL进行区分)
每本书可以看作一个文档。每本书有具体的属性,比如书名、作者、出版年份、ISBN 号等
{"title": "The Catcher in the Rye","author": "J.D. Salinger","year": 1951,"genre": "Fiction"
}
字段
文档中的属性或键值对,比如书的“标题”、“作者”、“出版年份”等;可以简单理解为一本书的详细信息
注意:字段的类型可以指定(例如 text
、keyword
、integer
等),并决定了如何处理这些数据。例如,“标题”是 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; // 客户端实例};
}
相关文章:

ES 基本使用与二次封装
概述 基本了解 Elasticsearch 是一个开源的分布式搜索和分析引擎,基于 Apache Lucene 构建。它提供了对海量数据的快速全文搜索、结构化搜索和分析功能,是目前流行的大数据处理工具之一。主要特点即高效搜索、分布式存储、拓展性强 核心功能 全文搜索:…...

分割一切2.0,SAM2详解
🏡作者主页:点击! 🤖编程探索专栏:点击! ⏰️创作时间:2024年11月24日20点03分 神秘男子影, 秘而不宣藏。 泣意深不见, 男子自持重, 子夜独自沉。 论文链接 点击开启你的论文编程之旅…...

Spring AI Fluent API:与AI模型通信的流畅体验
引言 随着人工智能(AI)技术的飞速发展,越来越多的应用场景开始融入AI技术以提升用户体验和系统效率。在Java开发中,与AI模型通信成为了一个重要而常见的需求。为了满足这一需求,Spring AI引入了ChatClient,…...

基于python的长津湖评论数据分析与可视化,使用是svm情感分析建模
引言 研究背景及意义 上世纪初开始,中国电影就以自己独有的姿态登上了世界电影史的舞台。中国电影作为国家文化和思想观念的反映与延伸,能够增强文化自信,在文化输出方面有着极其重要的作用1[1]。 改革开放以来,随着生产力的提高…...

Lucene(2):Springboot整合全文检索引擎TermInSetQuery应用实例附源码
前言 本章代码已分享至Gitee: https://gitee.com/lengcz/springbootlucene01 接上文。Lucene(1):Springboot整合全文检索引擎Lucene常规入门附源码 如何在指定范围内查询。从lucene 7 开始,filter 被弃用,导致无法进行调节过滤。 TermInSetQuery 指定…...

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

【2024最新】基于Springboot+Vue的智慧食堂系统Lw+PPT
作者:计算机搬砖家 开发技术:SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等,“文末源码”。 专栏推荐:SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:Java精选实战项…...

NVR小程序接入平台EasyNVR多品牌NVR管理工具:高效管理分散视频资源的解决方案
在当今数字化、智能化的时代背景下,视频监控已成为各行各业不可或缺的一部分,从公共安全到企业运维,再到智慧城市建设,视频资源的管理与应用正面临着前所未有的挑战。如何高效整合、管理这些遍布各地的分散视频资源,成…...

排序算法(三)--插入排序
文章目录 一、插入排序的基本原理二、插入排序的C语言实现三、代码解析 插入排序 C语言实例 一、插入排序的基本原理 插入排序的基本思想是将数组中的元素逐一取出,然后将其插入到已经排好序的部分中的适当位置,直到整个数组排序完成。具体步骤如下&…...

YOLOv11融合[ECCV 2018]RCAN中的RCAB模块及相关改进思路
YOLOv11v10v8使用教程: YOLOv11入门到入土使用教程 YOLOv11改进汇总贴:YOLOv11及自研模型更新汇总 《Image Super-Resolution Using Very Deep Residual Channel Attention Networks》 一、 模块介绍 论文链接:https://arxiv.org/abs/1807…...

排序(Java数据结构)
1. 排序的概念及引用 1.1 排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。(所有的排序都是默认从小到大排序) 稳定性:假定在待排序的记录序列中ÿ…...

【Java 解释器模式】实现高扩展性的医学专家诊断规则引擎
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...

【超详细】卷积神经网络CNN基本架构以及工作原理详解
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...

Html前后端Ajax交互数据前端JavaScript脚本后台C#ashx服务
本示例使用设备:https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.52de2c1bU8Fdbo&ftt&id615391857885 前端以GET模式向后台请求数据 function MyGetAjax() {var xhr new XMLHttpRequest();xhr.open(GET, http://192.168.1.211/HttpReader.ash…...

问:Spring Boot应用监控组件工具,梳理一下?
在日常运维与开发过程中,Spring Boot 应用的监控是确保系统稳定性和性能的关键环节。本文将探讨 Spring Boot 常用的监控组件及工具的原理、适用场景,并针对不同场景下的运维监控方案进行介绍。 1. Spring Boot Actuator 原理: Spring Boo…...

利用Hooka开源的多种功能shellcode加载器实现快速免杀火绒,静态360+360杀毒,微步查杀1,vt查杀7(教程)
免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于…...

2025-2026财年美国CISA国际战略规划(下)
文章目录 前言四、加强综合网络防御(一)与合作伙伴共同实施网络防御,降低集体风险推动措施有效性衡量 (二)大规模推动标准和安全,以提高网络安全推动措施有效性衡量 (三)提高主要合作…...

iframe通过url方式来获传递的参数
iframe通过url方式来获传递的参数 一、src"http://xxxx/#/policyOverview?codeaaaa"二、 src"/static/iframePhone/html/main.html?codeaaaa" 一、src“http://xxxx/#/policyOverview?codeaaaa” <iframedata-v-47a50536""src"http:/…...

蓝桥杯不知道叫什么题目
小蓝有一个整数,初始值为1,他可以花费一些代价对这个整数进行变换。 小蓝可以花贵1的代价将教数增加1。 小蓝可以花费3的代价将整数增加一个值,这个值是整数的数位中最大的那个(1到9) .小蓝可以花费10的代价将整数变为原来的2倍, 例如,如果整…...

最多可收集的水果数目
三个小朋友收集水果问题:最大水果收集路径 问题描述 有一个游戏,游戏由 n x n 个房间网格状排布组成。给定一个大小为 n x n 的二维整数数组 fruits,其中 fruits[i][j] 表示房间 (i, j) 中的水果数目。 游戏开始时,三个小朋友分…...

戴尔 AI Factory 上的 Agentic RAG 搭载 NVIDIA 和 Elasticsearch 向量数据库
作者:来自 Elastic Hemant Malik, Dell Team 我们很高兴与戴尔合作撰写白皮书《戴尔 AI Factory with NVIDIA 上的 Agentic RAG》。白皮书是一份供开发人员参考的设计文档,概述了实施 Agentic 检索增强生成 (retrieval augmented generation - RAG) 应用…...

HarmonyOS4+NEXT星河版入门与项目实战(16)------ 状态管理 @State(页面数据刷新与渲染)
文章目录 1、@State装饰器2、视图渲染演示1、无嵌套的对象属性值变化时可以触发页面渲染2、嵌套对象的嵌套属性值变化时不能够触发页面刷新渲染3、数组中对象的属性值变化时不能触发页面刷新渲染3、总结1、@State装饰器 2、视图渲染演示 常规的 string、number 这里就不演示了…...

Origin教程003:数据导入(2)-从文件导入和导入矩阵数据
文章目录 3.3 从文件导入3.3.1 导入txt文件3.3.2 导入excel文件3.3.3 合并工作表3.4 导入矩阵数据3.3 从文件导入 所需数据 https://download.csdn.net/download/WwLK123/900267473.3.1 导入txt文件 选择【数据->从文件导入->导入向导】: 选择文件之后,点击完成即可…...

设计自己的网络通信协议
文章目录 一、为什么需要设计网络通信协议1. **标准化通信规则**2. **确保数据传输的可靠性**3. **支持网络的多样性和可扩展性**4. **分层设计,简化复杂性**5. **实现设备的互操作性**6. **支持多任务和多应用并发**7. **提供安全性**8. **支持不同的通信模式**总结…...

深入理解 Seata:分布式事务的最佳解决方案
随着微服务架构的广泛应用,分布式事务管理成为系统设计中一项重要且极具挑战的任务。在微服务架构下,服务之间通过网络调用,单个业务操作往往需要多个服务的协作来完成,这样分布式事务的问题就不可避免。Seata 是目前较为流行的一…...

JDK下载
jdk-8u421-windows-x64.exe : 阿里云盘 jdk-7u80-windows-x64.exe :阿里云盘...

如何使用 Python 开发一个简单的文本数据转换为 Excel 工具
目录 一、准备工作 二、理解文本数据格式 三、开发文本数据转换为Excel工具 读取CSV文件 将DataFrame写入Excel文件 处理其他格式的文本数据 读取纯文本文件: 读取TSV文件: 四、完整代码与工具封装 五、使用工具 六、总结 在数据分析和处理的日常工作中,我们经常…...

React(六)——Redux
文章目录 项目地址基本理解一、配置Redux store二、创建slice配置到store里并使用三、给Slice配置reducers,用来修改初始值 项目地址 教程作者:教程地址: 代码仓库地址: 所用到的框架和插件: dbt airflow基本理解 s…...

java抽奖系统(二)
3. 新建项目 3.1 选择相应的框架 pom文件配置如下: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:s…...

STM32F10x 定时器
使用定时器实现:B5 E5的开关 添加相关的.h路径文件 添加相关的.c配置文件 led.h文件 用于声明LED函数 #ifndef __LED_H //没有定义__LED_H #define __LED_H //就定义__LED_H #define LED1_ON GPIO_ResetBits(GPIOB,GPIO_Pin_5) #defi…...