ES 介绍与安装

Elasticsearch, 简称 ES,它是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful 风格接口,多数据源,自动搜索负载等。它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据。es 也使用 Java 开发并使用 Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单。

Elasticsearch 是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在 Elasticsearch 中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。

ES 安装

# 添加仓库秘钥
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
# 上边的添加方式会导致一个 apt-key 的警告,如果不想报警告使用下边这个
curl -s https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/icsearch.gpg --import
# 添加镜像源仓库
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elasticsearch.list
# 更新软件包列表
sudo apt update
# 安装 es
sudo apt-get install elasticsearch=7.17.21
# 启动 es
sudo systemctl start elasticsearch
# 安装 ik 分词器插件
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install https://get.infini.cloud/elasticsearch/analysis-ik/7.17.21

若 apt update 更新源报错:

安装 kibana

使用 apt 命令安装 Kibana。

sudo apt install kibana

sudo apt install kibana配置 Kibana(可选):

根据需要配置 Kibana。配置文件通常位于 /etc/kibana/kibana.yml。可能需要

设置如服务器地址、端口、Elasticsearch URL 等。

sudo vim /etc/kibana/kibana.yml 

例如,你可能需要设置 Elasticsearch 服务的 URL: 大概 32 行左右

elasticsearch.host: “http://localhost:9200”

启动 Kibana 服务:

安装完成后,启动 Kibana 服务。

sudo systemctl start kibana

设置开机自启(可选):

如果你希望 Kibana 在系统启动时自动启动,可以使用以下命令来启用自启动。

sudo systemctl enable kibana

验证安装:

使用以下命令检查 Kibana 服务的状态。

sudo systemctl status kibana

访问 Kibana:

在浏览器中访问 Kibana,通常是 http://:5601

ES 客户端的安装

代码:https://github.com/seznam/elasticlient

官网:https://seznam.github.io/elasticlient/index.html

ES C++的客户端选择并不多, 我们这里使用 elasticlient 库, 下面进行安装。

# 克隆代码
git clone https://github.com/seznam/elasticlient
# 切换目录
cd elasticlient
# 更新子模块
git submodule update --init --recursive
# 编译代码
mkdir build
cd build
cmake ..
make
# 安装
make install

cmake 生成 makefile 的过程会遇到一个问题

解决:需要安装 MicroHTTPD 库

sudo apt-get install libmicrohttpd-dev

make 的时候编译出错:这是子模块 googletest 没有编译安装

collect2: error: ld returned 1 exit status
make[2]: *** [external/httpmockserver/test/CMakeFiles/testserver.dir/build.make:105: bin/test-server] Error 1
make[1]: *** [CMakeFiles/Makefile2:675: 
external/httpmockserver/test/CMakeFiles/test-server.dir/all] Error 
2 
make: *** [Makefile:146: all] Error 2

解决:手动安装子模块

cd ../external/googletest/
mkdir cmake && cd cmake/
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make && sudo make install

安装好重新 cmake 即可。

ES 核心概念

索引(Index)

一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。

字段(Field)

字段相当于是数据表的字段,对文档数据根据不同属性进行的分类标识。

名称数值备注
enabledtrue(默认) | false是否仅作存储,不做搜索和分析
indextrue(默认) | false是否构建倒排索引(决定了是否分词,是否被索引
index_option
dynamictrue(缺省)| false控制 mapping 的自动更新
doc_valuetrue(默认) | false是否开启 doc_value,用户聚合和排序分析,分词字段不能使用
fielddatafielddata": {“format”:“disabled”}是否为 text 类型启动 fielddata,实现排序和聚合分析针对分词字段,参与排序或聚合时能提高性能,不分词字段统一建议使用 doc_value
storetrue | false(默认)是否单独设置此字段的是否存储而从_source 字段中分离,只能搜索,不能获取值
coercetrue(默认) | false是否开启自动数据类型转换功能,比如:字符串转数字,浮点转整型
analyzer“analyzer”: “ik”指定分词器,默认分词器为 standard analyzer
boost“boost”: 1.23字段级别的分数加权,默认值是 1.0
fields“fields”: {“raw”: {“type”:“text”,“index”:“not_analyzed”}}对一个字段提供多种索引模式,同一个字段的值,一个分词,一个不分词
data_detectiontrue(默认) | false是否自动识别日期类型

文档 (document)

一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档或者某个订单的一个文档。文档以 JSON(Javascript Object Notation)格式来表示,而 JSON 是一个到处存在的互联网数据交互格式。在一个index/type 里面,你可以存储任意多的文档。一个文档必须被索引或者赋予一个索引的 type。

Elasticsearch与传统关系型数据库相比如下:

DBDatabaseTableRowColumn
ESIndexTypeDocumentField

Kibana 访问 es 进行测试

通过网页访问 kibana:

创建索引库

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" : "USER14eeeaa5-442771b9-0262e455-e4663d1d","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-57189a19","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"}

查看并搜索数据

GET /user/_doc/_search?pretty
{"query": {"bool": {"must_not": [{"terms": {"user_id.keyword": ["USER4b862aaa-2df8654a-7eb4bb65-e3507f66","USER14eeeaa5-442771b9-0262e455-e4663d1d","USER484a6734-03a124f0-996c169d-d05c1869"]}}],"should": [{"match": {"user_id": "昵称"}},{"match": {"nickname": "昵称"}},{"match": {"phone": "昵称"}}]}}
}

删除索引:

DELETE /user

检索全部数据:

GET /user/_search
{"query": {"match_all": {}}
}

ES 客户端接口介绍

// 创建客户端对象
explicit Client(const std::vector < std::string >> &hostUrlList,std::int32_t timeout = 6000);// 应用于索引创建,以及新增数据
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());// 检索数据
cpr::Response search(const std::string &indexName,const std::string &docType,const std::string &body,const std::string &routing = std::string());// 删除数据
cpr::Response remove(const std::string &indexName,const std::string &docType,const std::string &id,const std::string &routing = std::string());

使用案例,数据为上面的数据:

#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <iostream>int main()
{// 构造ES客户端elasticlient::Client client({"http://127.0.0.1:9200/"});// 发起搜索请求try{auto rsp = client.search("user", "_doc", "{\"query\":{\"match_all\":{} }}");std::cout << rsp.status_code << std::endl;std::cout << rsp.text << std::endl;}catch (std::exception &e){std::cout << "请求失败: " << e.what() << std::endl;return -1;}return 0;
}

测试结果:

image-20250216183726619

二次封装

使用jsoncpp库实现数据的序列化和反序列化

Json::Value:用于进行中间数据存储

将多个字段数据进行序列化,需要先将数据存储到Value对象中

若要对一个json格式字符串进行解析,解析结果也是存放在Value中

常用接口:

#include <json/json.h>Value &operator=(Value &other);
Value &operator[](const char *key);  // Value["name"] = "张三";
Value &append(const Value &value);   // 数组数据的新增,Value["score"].append(100);
std::string asString() const;        // Value["name"].asString();
ArrayIndex size() const;             // 获取数组元素的个数
Value &operator[](ArrayIndex index); // 通过下标获取数组元素,Value["socre"][0].asFloat();// Write类
class Json_API StreamWriter
{virtual write(Value const &root, JSONCPP_OSTREAM *sout) = 0; // 序列化接口
};
class Json_API StreamWriterBuilder
{StreamWriter *newStreamWriter(); // StreamWriter 对象生产接口
};// Reader类
class JSON_API CharReader
{virtual bool parse(char const *beginDoc, char const *endDoc, Value *root, JSONCPP_STRING *errs) = 0;
};class JSON_API CharReaderBuilder
{CharReader *newCharReader(); // 创建CharReader对象接口
};

Jsoncpp使用案例:

#include <json/json.h>
#include <iostream>
#include <string>
#include <sstream>
#include <memory>bool serialize(const Json::Value &root, std::string &str)
{Json::StreamWriterBuilder swb;std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss;int ret = sw->write(root, &ss);if (ret < 0){std::cout << "json serialize failed" << std::endl;return false;}str = ss.str();return true;
}bool deserialize(const std::string &body, Json::Value &val)
{Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err;bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), &val, &err);if (ret == false){std::cout << "json deserialize failed " << err << std::endl;return false;}return true;
}int main()
{std::string name = "小明";int age = 18;float score[3] = {91, 99, 100};Json::Value stu;stu["name"] = name;stu["age"] = age;stu["score"].append(score[0]);stu["score"].append(score[1]);stu["score"].append(score[2]);std::string json_str;bool ret = serialize(stu, json_str);if (ret == false){std::cout << "json serialize failed" << std::endl;return -1;}std::cout << json_str << std::endl;Json::Value root;ret = deserialize(json_str, root);if (ret == false){std::cout << "json deserialize failed" << std::endl;return -1;}std::cout << "姓名:" << root["name"].asString() << std::endl;std::cout << "年龄:" << root["age"].asInt() << std::endl;std::cout << "成绩分别是: ";int sz = root["score"].size();for (int i = 0; i < sz; i++){std::cout << root["score"][i].asFloat() << " ";}std::cout << std::endl;return 0;
}

image-20250216190238780

ES客户端API二次封装

封装四个操作:索引创建,数据新增,数据查询,数据删除

封装最主要完成的是请求正文的构造过程,Json::Value对象数据新增过程

索引创建:

1.能够动态设定索引名称,索引类型

2.能够动态的添加字段,并设置字段类型,设置分词器类型,是否构造索引

构造思想:根据固定的Json格式构造Value对象即可

数据新增:

1.提供用户一个新增字段及数据的接口即可

2.提供一个发起请求的接口

封装代码实现:

#pragma once
#include <json/json.h>
#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include "logger.hpp"namespace hdp
{bool serialize(const Json::Value &root, std::string &str){Json::StreamWriterBuilder swb;std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());std::stringstream ss;int ret = sw->write(root, &ss);if (ret < 0){LOG_ERROR("json serialize failed");return false;}str = ss.str();return true;}bool deserialize(const std::string &body, Json::Value &val){Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string err;bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), &val, &err);if (ret == false){LOG_ERROR("json deserialize failed: {} ", err);return false;}return true;}class ESIndex{public:ESIndex(const 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["ik"] = tokenizer;analyzer["analyzer"] = ik;analysis["analysis"] = analyzer;_index["settings"] = analysis;}ESIndex &append(const std::string &key, const std::string &type = "text",const std::string &analyzer = "ik_max_word",bool enabled = true){Json::Value field;field["type"] = type;field["analyzer"] = analyzer;if (enabled == false)field["enabled"] = enabled;_properties[key] = field;return *this;}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;}try{cpr::Response 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 _index;Json::Value _properties;std::shared_ptr<elasticlient::Client> _client;};class ESInsert{public:ESInsert(const std::shared_ptr<elasticlient::Client> client,const std::string &name,const std::string &type = "_doc"): _name(name),_type(type), _client(client) {}template <class T>ESInsert &append(const std::string &key, const T &val){_item[key] = val;return *this;}bool insert(const std::string &id = ""){std::string body;bool ret = serialize(_item, body);if (ret == false){LOG_ERROR("索引序列化失败");return false;}try{cpr::Response 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;};class ESRemove{public:ESRemove(const 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{cpr::Response rsp = _client->remove(_name, _type, id);if (rsp.status_code < 200 || rsp.status_code >= 300){LOG_ERROR("删除数据{}失败:响应状态码异常: {}", 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;};class ESSearch{public:ESSearch(const 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();}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();}// 需要对响应正文进行反序列化Json::Value json_res;ret = deserialize(rsp.text, json_res);if (ret == false){LOG_ERROR("检索数据 {} 结果反序列化失败", rsp.text);return Json::Value();}serialize(json_res, body);LOG_DEBUG("检索响应正文: [{}]", body);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;};
}

二次封装测试代码:

#include "../../../common/icsearch.hpp"
#include <gflags/gflags.h>DEFINE_int32(run_mode, 0, "程序的运行模式,0-调试,1-发布");
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志的输出等级");int main(int argc, char *argv[])
{google::ParseCommandLineFlags(&argc, &argv, true);hdp::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);std::vector<std::string> host_list = {"http://127.0.0.1:9200/"};std::shared_ptr<elasticlient::Client> client = std::make_shared<elasticlient::Client>(host_list);// 创建索引bool ret = hdp::ESIndex(client, "test_user").append("nickname").append("phone", "keyword", "standard", true).create();if (ret == false){LOG_ERROR("创建索引失败");return -1;}else{LOG_DEBUG("创建索引成功");}// 新增数据ret = hdp::ESInsert(client, "test_user").append("nickname", "张三").append("phone", "123456").insert("0001");if (ret == false){LOG_ERROR("新增数据失败");return -1;}else{LOG_DEBUG("新增数据成功");}ret = hdp::ESInsert(client, "test_user").append("nickname", "李四").append("phone", "112233").insert("0002");if (ret == false){LOG_ERROR("新增数据失败");return -1;}else{LOG_DEBUG("新增数据成功");}std::this_thread::sleep_for(std::chrono::seconds(1));// 检索数据Json::Value user = hdp::ESSearch(client, "test_user").append_should_match("nickname", "李四").search();if (user.empty() || user.isArray() == false){LOG_ERROR("检索结果为空,或者结果不是数组类型");return -1;}else{LOG_DEBUG("数据检索成功");}int size = user.size();for (int i = 0; i < size; ++i){LOG_INFO("nickname: {}", user[i]["_source"]["nickname"].asString());LOG_INFO("phone: {}", user[i]["_source"]["phone"].asString());}// 更新数据ret = hdp::ESInsert(client, "test_user").append("nickname", "李四").append("phone", "123456789").insert("0002");if (ret == false){LOG_ERROR("更新数据失败");return -1;}else{LOG_DEBUG("更新数据成功");}std::this_thread::sleep_for(std::chrono::seconds(1));user = hdp::ESSearch(client, "test_user").append_should_match("phone.keyword", "123456789").search();if (user.empty() || user.isArray() == false){LOG_ERROR("检索结果为空,或者结果不是数组类型");return -1;}else{LOG_DEBUG("数据检索成功");}size = user.size();for (int i = 0; i < size; ++i){LOG_INFO("nickname: {}", user[i]["_source"]["nickname"].asString());LOG_INFO("phone: {}", user[i]["_source"]["phone"].asString());}// 删除数据ret = hdp::ESRemove(client, "test_user").remove("0002");if (ret == false){LOG_ERROR("删除数据失败");return -1;}else{LOG_DEBUG("删除数据成功");}std::this_thread::sleep_for(std::chrono::seconds(1));user = hdp::ESSearch(client, "test_user").append_should_match("phone.keyword", "123456789").search();if (user.empty() || user.isArray() == false){LOG_ERROR("检索结果为空,或者结果不是数组类型");return -1;}else{LOG_DEBUG("数据检索成功");}size = user.size();for (int i = 0; i < size; ++i){LOG_INFO("nickname: {}", user[i]["_source"]["nickname"].asString());LOG_INFO("phone: {}", user[i]["_source"]["phone"].asString());}return 0;
}

测试结果:

image-20250216181820079

ES客户端API使用注意事项:

1.地址后面不要忘了相对根目录: http://127.0.0.1:9200/

tring());
}

// 删除数据
ret = hdp::ESRemove(client, "test_user").remove("0002");
if (ret == false)
{LOG_ERROR("删除数据失败");return -1;
}
else
{LOG_DEBUG("删除数据成功");
}std::this_thread::sleep_for(std::chrono::seconds(1));
user = hdp::ESSearch(client, "test_user").append_should_match("phone.keyword", "123456789").search();
if (user.empty() || user.isArray() == false)
{LOG_ERROR("检索结果为空,或者结果不是数组类型");return -1;
}
else
{LOG_DEBUG("数据检索成功");
}
size = user.size();
for (int i = 0; i < size; ++i)
{LOG_INFO("nickname: {}", user[i]["_source"]["nickname"].asString());LOG_INFO("phone: {}", user[i]["_source"]["phone"].asString());
}
return 0;

}


测试结果:[外链图片转存中...(img-v3LOnflV-1753877627079)]ES客户端API使用注意事项:1.地址后面不要忘了相对根目录:  http://127.0.0.1:9200/2.ES客户端API使用,要进行异常捕捉,否则操作失败会导致程序异常退出

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

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

相关文章

【13】C# 窗体应用WinForm——.NET Framework、WinForm、工程创建、工具箱简介、窗体属性及创建

文章目录1. WinForm工程创建 及 界面介绍1.1 WinForm工程创建1.2 窗体 Form1.cs “查看代码”1.3 打开窗体设计器2. 工具箱3. 窗体属性及创建3.1 窗体属性3.2 实例&#xff1a;创建一个新窗体3.2.1 添加新Windows窗体3.2.2 窗体属性配置3.2.3 设置该窗体为启动窗体WinForm 是 W…

论文阅读-IGEV

文章目录1 概述2 模块2.1 总体说明2.2 特征抽取器2.3 CGEV2.4 基于Conv-GRU的更新算子2.5 空间上采样2.6 损失函数3 效果参考文献1 概述 在双目深度估计中&#xff0c;有一类是基于3D卷积的方法&#xff0c;代表就是PSMNet&#xff0c;它应用 3D 卷积编码器-解码器来聚合和正则…

[2025CVPR-图象分类方向]SPARC:用于视觉语言模型中零样本多标签识别的分数提示和自适应融合

1. ​背景与问题定义​ 视觉语言模型&#xff08;如CLIP&#xff09;在单标签识别中表现出色&#xff0c;但在零样本多标签识别&#xff08;MLR&#xff09;任务中表现不佳。MLR要求模型识别图像中多个对象&#xff08;例如&#xff0c;图像包含“猫”和“沙发”&#xff09;&…

2025创始人IP如何破局?

内容持续更新却无人点赞&#xff0c;课程精心打磨却无人报名&#xff0c;直播卖力讲解却无人停留 —— 明明有内容、有经验、有成果&#xff0c;却始终难以打动用户。问题的核心&#xff0c;或许在于你尚未打造出真正的 “创始人IP”。‌一、创始人IP&#xff1a;不止标签&…

告别配置混乱!Spring Boot 中 Properties 与 YAML 的深度解析与最佳实践

一、Spring配置文件 1.1、什么是Spring配置 Spring配置指的是在Spring框架中定义和管理应用程序组件&#xff08;如Bean&#xff09;及其依赖关系的过程 作用&#xff1a; 配置文件主要用于解决硬编码问题&#xff0c;它将可能变更的信息集中存放。程序启动时&#xff0c;会从…

无人机喷洒系统技术要点与难点解析

一、 模块运行方式1. 任务规划与加载模块&#xff1a;输入&#xff1a;农田边界、障碍物信息、作物类型、病虫害信息、所需喷洒量、天气条件。运行&#xff1a;利用地面站软件或移动APP&#xff0c;规划最优飞行路径&#xff0c;设定飞行高度、速度、喷洒参数、作业区域。将规…

mongodb源代码分析createCollection命令创建Collection流程分析

MongoDB 提供两种方式创建集合&#xff1a;隐式创建 和 显式创建。方式 1&#xff1a;隐式创建&#xff08;推荐&#xff09;当你向不存在的集合中插入文档时&#xff0c;MongoDB 会自动创建该集合。示例在 db中隐式创建 users 集合&#xff1a;javascriptdb.users.insertOne({…

c++注意点(13)----设计模式(抽象工厂)

创建型模式抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供一个接口&#xff0c;用于创建一系列相关或相互依赖的对象&#xff0c;而无需指定它们具体的类。简单说&#xff0c;它就像一个 "超级工厂"&#xff…

【大语言模型入门】—— Transformer 如何工作:Transformer 架构的详细探索

Transformer 如何工作&#xff1a;Transformer 架构的详细探索Transformer 如何工作&#xff1a;Transformer 架构的详细探索什么是 Transformer&#xff1f;什么是 Transformer 模型&#xff1f;历史背景从 RNN 模型&#xff08;如 LSTM&#xff09;到 Transformer 模型在 NLP…

iOS安全和逆向系列教程 第20篇:Objective-C运行时机制深度解析与Hook技术

iOS安全和逆向系列教程 第20篇:Objective-C运行时机制深度解析与Hook技术 引言 在上一篇文章中,我们深入学习了ARM64汇编语言的基础知识,掌握了从寄存器操作到指令分析的完整技能体系。现在,我们将把这些底层知识与iOS应用的高层逻辑联系起来,深入探讨Objective-C运行时…

IDEA中全局搜索快捷键Ctrl+Shift+F为何失灵?探寻原因与修复指南

在软件开发中&#xff0c;高效地查找和管理代码是提升生产力的关键。IntelliJ IDEA&#xff0c;作为一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;提供了丰富的搜索功能&#xff0c;帮助开发者迅速定位代码、资源、甚至是IDE功能本身。 在 IntelliJ IDE…

【学习笔记】Lean4 定理证明 ing

文章目录概述Lean4 定理证明初探示例&#xff1a;证明 1 1 2示例&#xff1a;证明 2 * (x y) 2 * x 2 * yLean4 定理证明基础命题与定理命题&#xff08;Proposition&#xff09;定理&#xff08;Theorem&#xff09;量词策略概述 Lean证明是指在Lean环境中&#xff0c;通…

墨者:SQL注入漏洞测试(HTTP头注入)

墨者学院&#xff1a;SQL注入漏洞测试(HTTP头注入)&#x1f680; 1. 什么是HTTP头注入&#xff1f;&#x1f50d; HTTP头注入是指攻击者通过篡改HTTP请求头部的字段&#xff08;如User-Agent、Referer、Cookie、Host等&#xff09;&#xff0c;将恶意SQL代码插入到后端数据库查…

linux_前台,后台进程

*在用户访问端口时&#xff0c;操作系统会形成对应的session,在其的内部进一步形成bash等进程 *一个会话只有一个前台进程&#xff0c;可以有多个后台进程&#xff0c;前台与后台进程的区别在于谁拥有键盘的使用权*前台与后台进程都可以访问显示器但是后台无法访问标准输入获取…

spring data mongodb 入门使用手册

<!--pom.xml引入依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency>文档映射类Student.java import lombok.Data; import lombok.NoArgsCons…

Fastjson2常用操作大全:对象、字符串、集合、数组、Map与JSON互转实战

高性能&#xff1a; 核心解析器和生成器经过深度优化&#xff0c;性能远超许多同类库。 功能丰富&#xff1a; 支持标准JSON、JSONPath查询、泛型处理、日期格式化、自定义序列化/反序列化等。 易用性&#xff1a; API 设计简洁直观&#xff0c;JSON 工具类提供了最常用的 toJS…

大模型——字节Coze重磅开源!Dify何去何从

大模型——字节Coze重磅开源!Dify何去何从 想必很多人盼了很久,就在昨晚,字节Coze终于开源了!Coze Studio 是字节跳动新一代 AI Agent 开发平台扣子(Coze)的开源版本。 提供 AI Agent 开发所需的全部核心技术:Prompt、RAG、Plugin、Workflow,使得开发者可以聚焦创造 A…

NaVid——基于单目RGB捕获的视频让VLM规划「连续环境中VLN」的下一步:无需地图/里程计/深度信息(含MP3D/R2R/RxR,及VLN-CE的详解)

前言 因为我司「七月在线」准备于25年7月底复现下NaVILA&#xff0c;而在研究NaVILA的过程中&#xff0c;注意到了这个NaVid 虽然NaVid目前已经不是VLN sota了&#xff0c;但其首次展示了VLM在无需地图、里程计或深度输入的情况下&#xff0c;能够实现优秀的导航性能且对后来…

【Vue2】结合chrome与element-ui的网页端条码打印

所有文章都是免费查看的&#xff0c;如果有无法查看的情况&#xff0c;烦请联系我修改哈~ 序言 为什么要做这个呢&#xff1f;因为所需要的条码打印功能比较简单&#xff0c;符合需要即可&#xff0c;但是呢网上查看了发现并没有合适的开源项&#xff0c;其他成熟的软件收费又超…

循环神经网络——动手学深度学习7

环境&#xff1a;PyCharm python3.8 &#x1f449;【循环神经网络】(recurrent neural network&#xff0c;RNN) RNN通过 引入状态变量存储过去的信息和当前的输入&#xff0c;从而可以确定当前的输出。状态变量捕捉序列的时序依赖&#xff0c;是处理文本、时间序列等数据的…