Elasticsearch初识与 index+mapping+document 基操

news2024/11/25 8:40:42

前言

在21年多少有使用过es 当时是在艺术赛道的一个教育公司,大概流程就是 将mysql中的各种课程数据通过logstash汇总到es 然后提供rest接口出去。由于在职时间较短(很不幸赶上了教育双减),所以对es的了解其实仅仅是些皮毛,当然elk在我的任职经历中基本上每家都有使用,不得不说elk真的是查询日志的好工具。不管是elk 还是当年的课程数据亦或者其他使用到es的场景,我们可以发现他们都有一个共同点就是数据量很大,并且搜索需求很高也比较复杂,而这类场景在实际开发中是很常见的,因此掌握Elasticsearch这门技术就成了程序员必不可少的一项技能,一言概之:学好es,可以让我们在面对 大数据+高频+复杂 的搜索场景时显得游刃有余。而不是让你的数据沉睡在数据库中!

一些说明: - Elasticsearch 版本: 7.17.5 , kibana版本:7.17.5 - 由于 Elasticsearch 这个词比较长为了方便我们在日常中一般都称为es,当然本文也不例外 - Elasticsearch 和 kibana 的安装不在本文,因为之前我写过一篇安装文章,见:Centos7安装Elasticsearch和Kibana 记录(无坑版)

核心概念

ps: 为了便于理解,我们会拿es中的索引(index),类型(type),文档(doc),字段(filed)去类比mysql(但其实本质上不那么合理,只能说我们初学es时为了方便而进行的一种思维映射,在深入理解es后,最好能抛弃此类比概念或者知晓其不一样的地方),由于es6开始,逐步移除了type的概念,所以类比时会有两个版本,如下是未移出type时的类比关系图: | ES |mysql || | --- | --- |---| | Index | Database|库 | Type | Table |表 | Document | Row |行 | Filed | Field |字段

移除type后,我更喜欢这么类比:

| ES |mysql || | --- | --- |---| | Index | Table |表 | Document | Row |行 | Filed | Field |字段

索引(index):

索引是一类相似文档的集合,当然了他也是es存储数据的地方。(这里我们要区别与mysql中的索引叫法虽一样但是作用却大不相同)。 - 如果是7.x以及之前 的ES,则可以类比为关系型数据库中的 库 (database)。 - 如果是7.x以及之后 的ES,我愿将其类比为关系型数据库中的 表 (table)

类型(type)【7.x及之后版本将逐步废弃】:

从6.0开始官方将逐步移除type这个属性,计划如下: - type在6.x中只能有一个,可自定义。 - type在7.x中只能有一个,且只能是_doc。 - type在8.x(未来版本)彻底取消,API会大变,以前的 INDEX -> TYPE -> DOC 的方式会变成 INDEX -> DOC,如我们新增个文档时,将会从 POST indextest/_doc/1 变为 POST indextest/1 。以下是官方的说明: image.png

在7.x版本之前,type可以类比一个关系型数据库的表(table)。但在ES 7.x以后,由于将逐步移除type这个概念,所以我们如果使用的是7.x版本的ES 那么无需关注此属性,也不再将其类比为表了,

文档(document):

Elasticsearch中的最小数据单元,一般以Json格式显示。一个document相当于关系型数据库中的一行(row)数据。

字段(Field):

一条文档数据由一个或多个字段(Field)组成, 这里的字段类似mysql中表的字段(filed)。当然es中字段也有类型的,下面是常用的字段类型: - 数值类型(包括: long、integer、short、byte、double、float) - text : 会分词,支持全文搜索 - keyword : 不会分词,将全部内容作为一个词条,不支持全文搜索。如:email、电话这些数据,作为一个整体进行匹配就可以,不需要分词处理。 - date : 日期类型 - boolean : 布尔类型 - 范围类型 : integerrange, floatrange, longrange, doublerange, date_range - 数组 []: 数组类型的json对象 - 对象 {}: 单个json对象

映射(mapping):

mapping定义了每个字段的类型、字段所使用的分词器等。相当于关系型数据库中的表结构定义

倒排索引:

关于倒排索引我们这里费点笔墨来说说他,因为他是es底层 lucene的非常重要的一个数据结构也是es搜索之快的一个重要原因。当然关于倒排索引里边的东西实在太多了,我们这里只是简单介绍,后续有时间的话写个更详细点的倒排索引文章。

ps: 倒排索引这个名字其实有点不恰当,从实现上来说的话其实叫它反向索引更加生动形象。但是为了方便理解我们在下文还是叫它倒排索引,只是你应该知道,他叫反向索引更合适。别着急,理解了他的工作机制,就知道为什么说叫它反向索引更合适了。

说倒排索引前,我们先看下与之相反的,正向索引是什么。

常规的索引(也就是正向索引)是从文档中找关键词:文档——>关键词,但是这样检索有个很大的弊端就是需要将所有文档(即行)遍历一遍,比如你在mysql中的查询就是根据给定条件来找匹配的行记录,如下: image.png 小提示:(注意上边我们假设就是全表遍历,如果索引类型是全文索引 fulltext index,那么mysql其实也是使用了倒排),看下官方的说明: image.png 这个知道就好。

倒排索引简易图

区别于正向索引,倒排索引是根据关键词找文档: 关键词——>文档。也就是说es会在内部维护一个 词条索引,每一个词条都 有一个包含它的文档列表 ,如下【简易图】所示: image.png

下边我们来简单看下创建倒排索引与搜索的过程。注意:真实的es内部实现远比我下边说的要复杂的多,我只是介绍点皮毛主要我们理解他的大概思路就行,细节不去扣,扣的话得上源码了现在还不是时候。

创建倒排索引,分为以下几步:

  1. 创建文档列表:首先对原始文档数据进行编号(DocID),形成一个文档列表
  2. 创建倒排索引列表:然后对文档中数据进行分词,得到词条并对词条进行编号,以词条创建倒排索引。然后记录下包含该词条的所有文档编号(及其频率和位置信息)

第一步: :创建文档列表 | 文档编号 | 文档内容 | | :-: | --- | | 1| 你今天吃什么 | | 2| 什么都不想吃昨天吃多了|

第二步: 分词 & 创建倒排索引列表(注:下边分词可能不严谨不过没关系主要是体会这个意思)

step1:对 "你今天吃什么" 分词,得到如下词条:

| 词条: |你|今天|吃|什么| | :-: |:-:|:-:|:-:|:-:| | 下标: | 1 |2|3|4|

step2:对 "什么都不想吃昨天吃多了" 分词,得到如下结果:

|词条:|什么|都|不想|吃|昨天|吃|多了 |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-: | 下标: | 1 |2|3|4|5|6|7|

step3:根据分词后得到的词条,创建 倒排索引列表

(注意:实际es内部会分为三个文件分别是 词典文件(Term Dictionary)、频率文件(frequencies)、位置文件 (positions)。其中词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息(通过频率信息可以计算匹配度也就是分值)和位置信息)如下图所示:

| 词条 | 文档编号 | 出现频率 | 出现位置 | | :-: | :-: | :-: | --- | | 你 | 1 | 1|1| | 今天 | 1,2 |1|1| | 吃 | 1,2|1,2|3,[4,6] 说明:文档1的第3个和文档2的第4、第6个位置| | 什么 | 1,2 |1,1|4,1| | 都 | 2 |1|2| | 不想 | 2 |1|3| | 昨天 | 2 |1|5| | 多了 | 2 |1|6|

搜索过程

当用户输入的搜索条件为 "吃" 这个词条时,大致搜索逻辑如下: 1. 首先对用户输入的数据进行分词,得到用户要搜索的所有词条(分词后的词条也是 )。 2. 然后拿着这个词条去倒排索引列表中进行匹配。找到这些词条就能找到包含这些词条的所有文档的编号以及频率&位置信息。命中的倒排索引就是这个:image.png 找到包含他的文档有:文档1和文档2,以及频率信息(在文档1出现了1次在文档2出现了2次以及位置信息)和位置信息。 4. 最后返回命中的文档信息以及分值(默认会根据分值倒序),如下:image.png

当然这只是精简后的过程概要,真正的搜索过程还是很复杂的尤其是涉及到集群、分词方式等等,我们暂时先不展开,主要是体会一下倒排的大致工作原理。

ps:由于此时我们只是介绍es的基本功能,集群暂时还未涉及,所以先不讲集群相关的概念,到后边我们对es熟练了之后再讲解&搭建es集群.

下边我们好好说说es的语法,先学会用在去探究原理。

索引+映射+文档 (管理)

索引管理:index

我们都知道在使用mysql时,想要管理数据,第一步得先有个库以及表,而对于es来说管理数据首先得有个索引吧?不过在新建索引前,我们使用以下命令先来看下当前都有哪些索引:

查看索引

dsl GET /_cat/indices?v image.png 可以看到除了 hzztest以及invertedindex这俩索引是我之前建的之外其他几个索引都是以.开头的,这类一般是系统默认的我们暂时不用管它,接下来我们看看如何管理索引。

创建索引

Elasticsearch使用PUT方式来实现索引的新增,你可以在创建索引的时候不添加任何参数,当然你可以添加附加一些配置信息,如下: image.png 查询刚刚创建的索引: image.png 这里返回的很多个字段,我们列个表解释一下:

| 字段 | 解释 | | --- | --- | | aliases | 别名 | | mappings | 映射,也就是这个索引的定义,我们只创建了索引没有定义所以上边返回是空的 | | settings | 此索引的一些配置,具体见下边 | settings.index.routing | 路由设置,不指定的话es会默认将索引分片自动分配给内容层:Content tier ,这个分层机制其实可以理解成冷热机制此设计在很多数据库中都有涉及目的就是为了更快的查询到热数据,只不过es分的更细分了4层分别是:Content tier内容层,Hot tier热层,Warm tier 温层,Cold tier 冷层| | settings.index.creationdate | 此索引的创建时间 | | settings.index.numberofshards | 数据分片数,索引要做多少个分片,只能在创建索引时指定,后期无法修改 | | settings.index.numberofreplicas | 数据备份数,每个分片有多少个副本,后期可以动态修改 | | settings.index.uuid | 此索引的唯一id | | settings.index.providedname | 此索引的名称 |

索引是否存在

使用 HEAD 索引名称 可以查看该索引是否存在,如下: image.png

关闭&打开 索引

在某些场景下,我们可能需要禁掉某些索引的访问功能(读+写),但是又不想删除这个索引,那么此时关闭索引就是一种方式,如下: dsl POST indexstudy/_close image.png 当然关闭后你可以随时打开,打开后就用可以继续读写索引里的数据了,打开方式如下: dsl POST indexstudy/_open image.png 当你暂不需要那些数据,但是也不想删除它们。关闭索引将变得比较有意义。关闭它们会比较稳妥,因为随时可以重新打开被关闭的索引来重新读写,而不是删除

删除索引

当然确定某些索引确实不用了那么就删除他吧 不然留着他还干啥: dsl DELETE indexstudy image.png

映射管理:mapping

映射(mapping)的创建是基于索引的,你必须要先创建索引才能创建映射,es中的映射相当于传统数据库中的表结构,数据存储的格式就是通过映射来规定的。值的注意的是:es会自动根据数据格式识别它的类型,当然在一些情况下你可能对es给你生成的mapping类型不放心或者和你的期望不一致,那么这时就应该手动给他指定映射(mapping)啦!

创建映射

可以在创建索引时指定映射,其中 mappings.properties 为固定结构,使用映射可定义字段的类型,我们刚开始也说了,可以把他类比理解为关系型数据库的ddl -> 表结构定义;

下边我创建索引并设置映射,即:给 username(姓名)设置为keyword类型 ,age(年龄)设置为integer, introduction(简介) 设置为text类型。 dsl PUT mappingstudy { "mappings": { "properties": { "username": { "type": "keyword" }, "age": { "type": "integer" }, "introduction": { "type": "text" } } } } 创建索引+设置映射 成功后再次查询索引信息可以看到我们对该索引设置的映射信息 image.png

查看映射

查看映射很简单你可以直接查询此索引也可以指定就只查映射信息,这样更清爽些,如下: ```dsl //索引全部信息都会返回 GET mappingstudy

or

//只返回映射信息 更清爽一些 GET mappingstudy/_mapping ``` image.png

修改映射(不能直接修改,需使用reindex方式)

如果你想修改某个字段的属性比如你想将 username 字段的类型从 keyword 改为 text 那对不起,不能(像mysql那样)直接修改。如果是mysql的可能你一个sql就搞定了,如下: sql alter table mappingstudy modify column username text NOT NULL; 但是在es中想直接修改字段的类型,会毫不留情给你报个错: image.png 那我们确实就是有这个需要呢?其实es也不是那么轴非不让你修改,而是提供了一种新的方式,即使用: reindex,步骤大致如下: 1. 先新建个空的,你的目标索引,并将其mapping设置为我们最新需要的 3. 将数据从旧的索引中迁移到新的索引使用 reindex并指定 目标索引名称和来源索引名称 dsl //新建目标索引 PUT mapping_update_target { "mappings": { "properties": { "username": { "type": "text" }, "age": { "type": "integer"}, "introduction": {"type": "text" } } } } //执行迁移 POST _reindex { "source": { "index": "mappingstudy" }, "dest": { "index": "mapping_update_target" } } image.png 看下效果:可以看到新的索引的username字段已经变为text并且数据也迁移过来了。 image.png reindex更多参见官网:reindex

文档管理:document

首先有必要说一下POST和PUT,这俩和ES没什么关系,他们属于HTTP请求方式,在http1.1中 一共有9种(GET、POST、HEAD、OPTIONS、PUT、PATCH、DELETE、TRACE、CONNECT),日常开发我一般都是使用post和get一把撸,当然如果遵循rest 规范的话最好还是在不同的动作下使用不同的请求方式, 我日常put用的不多,但是为了全面我们还是要讲一下:

第一:POST不用加具体的id,它是作用在一个集合资源之上的(/uri),而PUT操作是作用在一个具体资源之上的(/uri/xxx)。

第二:POST与PUT都可以用于更新和新增操作,但是新增时POST是不用指定ID的,会自动分配;而PUT是需要指定ID的,也就是说PUT是具有幂等性的,无论你操作多少次结果都是一样的。而POST是操作一遍就新增一条,如果你多次POST 新增同一条数据那么es将会将原数据删除再新增

创建文档

我们上边早就说过文档可以类比关系型数据库的一行数据,所以创建文档其实就是新增一条数据(但是格式是json),也就是说可以理解为一个insert语句。 创建文档时可以手动指定文档的唯一id, 也可以不指定这样的话es会帮我们生成一个唯一的文档id。

小提示:一般不推荐手动指定文档id,因为会对ES插入性能造成影响。如果手动指定id,es为了保证id不冲突,会先查询一次文档库,如果不存在则进行插入,手动插入多了一次查询操作,性能会有损失。而es自动生成的id是一个不会重复的随机数,使用GUID算法,可以保证在分布式环境下,不同节点同一时间创建的 _id 一定是不冲突的

创建文档一共有好几种方式,如下介绍:

使用Index API (_doc)来创建文档

语法:POST 目标索引名称/_doc/ image.png 语法:POST 目标索引名称/_doc/文档id image.png 语法:PUT 目标索引名称/_doc/文档id image.png

使用Create API (_create)来创建文档

PUT /目标索引名称/_create/文档id image.png POST /目标索引名称/_create/文档id image.png

错误的创建文档 示范

使用 POST 目标索引名称/_create/ 则报错 ”Invalid type: expecting doc but got _create" 原因很明确:使用了错误的操作类型,说白了 你不能使用 POST create 不指定id 的方式创建文档,想不指定id创建文档, 请使用上边介绍过的:POST 目标索引名称/_doc/ 方式 image.png

使用 PUT 目标索引名称/_create/ 则报错405,原因我们上边也说了PUT操作是作用于具体的资源之上的,不指定具体的资源id,那肯定是不行。报错已经很明显了不指定资源id 让我们使用POST方式 image.png

在上边不管哪种方式创建的文档,都会返回一堆数据,下边简单解释一下含义: dsl { "_index" : "document_study",#所属索引 "_type" : "_doc", #所属type "_id" : "1", #文档id "_version" : 1, #文档版本 "result" : "created", #创建结果 "_shards" : { "total" : 2, #所在分片有两个分片 "successful" : 1, #只有一个副本成功写入,因为我是部署的单机es "failed" : 0 #失败副本数 }, "_seq_no" : 0, #第几次操作该文档 "_primary_term" : 1 #词项数 } 更多关于创建文档的操作详见官网:创建文档

更新文档

在更新文档前,我们先看下现在的数据: //只查source和id字段,其他字段过滤掉 GET document_study/_search?filter_path=hits.hits._source,hits.hits._id image.png

更新文档使用POST 和put都行,不过都需要指定文档id了因为更新肯定是针对某一或多个资源的更新,另外值的注意的是如果通过 update(update Api) 则可以部分更新,也就是只更新某些字段其余不动,但是如果使用 _doc (Index Api)则会全部更新,即你想更新a字段,那么使用doc (Index Api)方式提交后则目标文档中只有a字段了其他字段被覆盖,造成这样的原因就是:此机制是先删除,再新增。而update才是真正的你更新a字段就指给你更新a字段,而不会影响其他的字段和数据。

使用 update api 更新文档(更新指定的字段)

POST 目标索引名称/_update/文档id ```json //只更新username为 ”小琴“ //注意,使用update方式更新时,需要外边报一个doc,doc是个对象,要修改的字段值必须放到doc对象中

POST documentstudy/update/3 { "doc": { "username": "小琴" } } `` ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1cadd6a96ea8485cbc08aa7414ad11f2~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=3434&h=1544&s=649599&e=png&b=f4f5f9) PUT documentstudy/update/3` 将报错405 ,也就是说不能使用put +update 方式更新。得使用post方式 image.png

使用 index api 更新文档(先删除,再新增)

PUT 目标索引名称/_doc/文档id image.png POST 目标索引名称/_doc/文档id image.png

使用脚本更新

脚本比较灵活,举例几个:

追加内容

给文档1中的字段introduction追加(使用+=代表追加) 内容,如下: dsl POST document_study/_update/1 { "script" : { "source": "ctx._source.introduction += params.introduction", "lang": "painless", "params" : { "introduction" : "-我的目标是寻找第3颗和平星" } } } image.png

更新内容

修改文档1中的age为30(=则直接使用新值覆盖旧值) dsl POST document_study/_update/1 { "script" : { "source": "ctx._source.age = params.age", "lang": "painless", "params" : { "age" : "30" } } } image.png

添加字段

添加字段(该字段是数组类型) dsl POST document_study/_update/1 { "script": { "source": "ctx._source.hobby=params.hobby", "lang": "painless", "params": { "hobby": [ "java", "python", "linux", "es" ] } } } image.png

当然 添加字段你也可以不定义params直接这样加: dsl POST document_study/_update/1 { "script" : "ctx._source.address='北京市朝阳区百子湾'" } image.png

追加内容到数组

dsl POST document_study/_update/1 { "script": { "source": "ctx._source.hobby.add(params.tag)", "lang": "painless", "params": { "tag": "go" } } } image.png

从数组移除元素

脚本是支持条件的,比如下边我们在移除元素时,会判断hobby数组中是否包含 ”linux“这个元素,存在再移除,如下: dsl POST document_study/_update/1 { "script": { "source": "if (ctx._source.hobby.contains(params.tag)) { ctx._source.hobby.remove(ctx._source.hobby.indexOf(params.tag)) }", "lang": "painless", "params": { "tag": "linux" } } } image.png

移除文档中的字段

POST test/_update/1 { "script" : "ctx._source.remove('new_field')" }

删除文档如果符合条件

dsl POST document_study/_update/1 { "script": { "source": "if (ctx._source.hobby.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'noop' }", "lang": "painless", "params": { "tag": "go" } } } image.png 如果username包含周芷若,则删除文档5: image.png

删除字段

dsl POST document_study/_update/6 { "script" : "ctx._source.remove('age')" } image.png

使用upsert

当你操作一个不存在的文档时(我这里准备操作文档id是7的文档这个文档目前不存在),则会报错,如下: image.png 如果你使用upsert后,如果文档不存在则会新建,文档存在则会更新。如下文档不存在时(则新增): dsl POST document_study/_update/7 { "script": { "source": "ctx._source.username = params.tag", "lang": "painless", "params": { "tag": "金毛狮王" } }, "upsert": { "username": "金花婆婆" } } image.png 使用upsert时,文档存在(则更新): image.png

根据查询条件更新

dsl POST document_study/_update_by_query { "query": { "match": { "username": "赵敏" } }, "script": { "source": "ctx._source.age = 20 ", "lang": "painless" } } image.png

根据条件更新,可能在实际场景中比较实用至少我个人感觉是这样,但是有个问题就是可能符合条件的数据很多,那么这个请求就可能超时错误,合理的解决方案就是使用异步方式,告诉es 这个更新操作你异步来处理,这样的话我们发出的更新请求就会秒返回了。es内部会有个异步任务,来处理这个更新请求,当然我们也可以查看这个异步任务的状态,如下演示:

ps:updatebyquery 后边可以跟的参数很多,具体见 docsupdatebyquery ```dsl POST documentstudy/updatebyquery?waitforcompletion=false { "query": { "match": { "username": "赵敏" } }, "script": { "source": "ctx.source.age = 21 ", "lang": "painless" } }

//查询任务状态 GET /_tasks/任务id

//当然在某些任务执行过程中,可能我们不想更新了, //那么也可以取消某任务 POST tasks/任务id/cancel ``` image.png

查询文档

查询文档是否存在:HEAD 索引id/_doc/文档id image.png

查询文档

查某索引下的全部文档数据: GET document_study/_search

查指定文档数据: GET document_study/_doc/3 指定返回字段: GET document_study/_search?filter_path=hits.hits._source,hits.hits._id

image.png

删除文档

删除文档很简单

DELETE 索引名/_doc/文档id image.png 当然你也可以根据条件删除,如下: dsl POST document_study/_delete_by_query { "query":{ "match":{ "username":"阿牛" } } } image.png 当然和根据条件更新一样,当符合的条件的数据很多时,你也可以设置为异步。当然有很多参数

批量操作 _bulk

批量操作的话使用 _bulk,他支持4种类型的操作: - create,delete,index(新增),和 update,如下描述: image.png

即允许一批操作中有不同类型(新增 删除 修改) 不同索引的操作,但是他这个对写法比较严格,必须使用换行符来分割JSON结构(我们不能格式化json串了),第一行指定操作类型和元数据(索引、文档id等),紧接着的一行是这个操作的内容(如果有的话你比如新增修改就有 删除就不存在),批量操作示例如下: dsl POST _bulk {"index":{"_index":"document_study_2","_id":"1"}} {"username": "黄壮壮","age":29,"introduction" : "我是蝎子莱莱爱打怪"} {"create":{"_index":"document_study_3","_id":"2"}} {"username": "赵奕欢","age":17,"introduction" : "我是奕欢姐姐"} {"update":{"_index":"document_study_2","_id":"1"}} {"doc":{"age":30}} {"delete":{"_index":"document_study","_id":"7"}} image.png 上边这个批操作我是新建了索引 documentstudy2,documentstudy3 并且插入了数据,之后修改了documentstudy2索引中的文档1的age值为30 ,并且删除了索引 document_study中id=7的文档。这个功能在我们造测试数据时其实比较好用。不用多次请求了。只要脚本写好请求一次就完事了,也挺爽的。


说明:

本来我是准备将es的索引 映射 文档以及查询语法、聚合语法和Java Api 以及es集群都放一篇搞定,但是遇到了几个问题

  1. 首先 时间和精力不够且放到一起估计文章会超长。
  2. 最近在忙其他事情,es先放一放,后续的事情可能和es有深度的交互 ,到时候再说查询、聚合以及Java Api和集群和es原理这些事情吧。

上边的问题导致这篇文章在草稿箱挂了好久,为了不一口吃个胖子,所以 先将此文发表出来,后续有时间,会一点点更新Elasticsearch相关的文章。

es,未完待续。

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

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

相关文章

推荐一款免费的GIF编辑器——【ScreenToGif编辑器】

读者大大们好呀!!!☀️☀️☀️ 👀期待大大的关注哦❗️❗️❗️ 🚀欢迎收看我的主页文章➡️木道寻的主页 文章目录 🔥前言🚀素材准备🚀逐帧制作🚀保存图片⭐️⭐️⭐️总结 &#…

使用Jetpack Compose实现具有多选功能的图片网格

使用Jetpack Compose实现具有多选功能的图片网格 在现代应用中,多选功能是一项常见且重要的需求。例如,Google Photos允许用户轻松选择多个照片进行分享、添加到相册或删除。在本文中,我们将展示如何使用Jetpack Compose实现类似的多选行为,最终效果如下: 主要步骤 实现…

深度学习笔记: 最详尽解释R 平方 (R²)

欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家! 理解 R 平方 (R) 什么是相关性 R? 相关性测量两个定量变量(例如,重量和尺寸&a…

iOS shouldRecognizeSimultaneouslyWithGestureRecognizer 调用机制探索

shouldRecognizeSimultaneouslyWithGestureRecognizer 经常会看到,但是一直没有弄清楚其中的原理和运行机制,今天专门研究下 其运行规律 我们准备三个视图,如下,红色的是绿色视图的父视图,绿色视图 是蓝色视图的父视图…

layui在表格中嵌入上传按钮,并修改上传进度条

当需要在表格中添加上传文件按钮,并不需要弹出填写表单的框的时候,需要在layui中,用按钮触发文件选择 有一点需要说明的是,layui定义table并不是在定义的标签中渲染,而是在紧接着的标签中渲染,所以要获取实…

Unity WebGL项目问题记录

一、资源优化 可通过转换工具配套提供的资源优化工具,将游戏内纹理资源针对webgl导出做优化。 工具入口: 工具介绍 Texture 搜索规则介绍 已开启MipMap: 搜索已开启了MipMap的纹理。 NPOT: 搜索非POT图片。 isReadable: 搜索已开启readable纹理。 …

为什么有的手机卡没有语音功能呢?

大家好,今天这篇文章为大家介绍一下,无通话功能的手机卡, 在网上申请过手机卡的朋友应该都知道,现在有这么一种手机卡,虽然是运营商推出的正规号卡,但是却屏蔽了通话功能,你知道这是为什么吗&am…

第六节:如何解决@ComponentScan只能扫描当前包及子包(自学Spring boot 3.x的第一天)

大家好,我是网创有方,继上节咱们使用了Component和ComponentScan的方法实现了获取IOC容器中的Bean,但是存在一个问题,就是必须把AppConfig和要扫描的bean类放在同一个目录下,这样就导致了AppConfig类和bean类在同一个目…

T4打卡 学习笔记

所用环境 ● 语言环境:Python3.11 ● 编译器:jupyter notebook ● 深度学习框架:TensorFlow2.16.1 ● 显卡(GPU):NVIDIA GeForce RTX 2070 设置GPU from tensorflow import keras from tensorflow.keras…

Dahlia Hart: Stylized Casual Character(休闲角色模型)

此包包含两个发型和两个服装,每个都有多种颜色选择。每个发型都适合与物理资源一起使用,并包含各种表情和音素混合形状。 下载:​​Unity资源商店链接资源下载链接 效果图:

自适应IT互联网营销企业网站pbootcms模板

模板介绍 一款蓝色自适应IT互联网营销企业网站pbootcms模板,该模板采用响应式设计,可自适应手机端,适合一切网络技术公司、互联网IT行业,源码下载,为您提供了便捷哦。 模板截图 源码下载 自适应IT互联网营销企业网站…

C++精解【9】

文章目录 大整数GMP概述GMP安装 [cygwin](https://cygwin.com/install.html)安装 gmpexample Eigen基本属性和运算 大整数GMP 概述 GMP GMP是一个用于任意精度算术的免费库,可对有符号整数、有理数和浮点数进行操作。除了运行GMP的机器的可用内存所暗示的精度外&…

【建设方案】工单系统建设方案(Word原件)

工单管理系统解决方案 1、工单创建:根据告警信息创建工单。 2、工单管理:列表形式展示所有工单信息及进度状态。 3、工单处理:对接收的工单进行处理反馈。 4、工单催办:根据工单时效自动发送工单催办消息通知。 5、工单归档&#…

鲲鹏arm服务器部署paddleOCR

1. 部署环境信息查看 1.1 操作系统 $ cat /etc/os-release PRETTY_NAME"UnionTech OS Server 20" NAME"UnionTech OS Server 20" VERSION_ID"20" VERSION"20" ID"uos" PLATFORM_ID"platform:uel20" HOME_URL&q…

数据结构与算法笔记:高级篇 - 搜索:如何用 A* 搜索算法实现游戏中的寻路功能?

概述 魔兽世界、仙剑奇侠传这类 MMRPG 游戏,不知道你玩过没有?在这些游戏中,有一个非常重要的功能,那就是任务角色自动寻路。当任务处于游戏地图中的某个位置时,我们用鼠标点击另外一个相对较远的位置,任务…

Java学习 - 布隆过滤器

前置需求 需求 已经有50亿个电话号码,现在给出10万个电话号码,如何快速准确地判断这些电话号码是否已经存在? 参考方案 通过数据库查询:比如MySQL,性能不行,速度太慢将数据先放进内存:50亿*8字…

用pycharm进行python爬虫的步骤

使用 pycharm 进行 python 爬虫的步骤:下载并安装 pycharm。创建一个新项目。安装 requests 和 beautifulsoup 库。编写爬虫脚本,包括获取页面内容、解析 html 和提取数据的代码。运行爬虫脚本。保存和处理提取到的数据。 用 PyCharm 进行 Python 爬虫的…

机器人控制系列教程之Simulink中模型搭建(1)

机器人模型获取 接上期:机器人控制系列教程之控制理论概述,文中详细讲解了如何通过Solidworks软件导出URDF格式的文件。文末提到了若需要将其导入到Simulink中可在命令行中输入smimport(urdf/S_Robot_urdf.urdf),MATLAB将自动打开Simulink以…

TCP单进程循环服务器程序与单进程客户端程序

实验目的 理解并掌握以下内容: 网络进程标识(即套接字地址)在Linux中的数据结构与地址转换函数。网络字节序与主机字节序的定义、转换以及相关函数在网络编程中的应用。数据结构内存对齐的基本规则,以及基于数据结构构建PDU的基本方法。TCP单进程循环服务器与单进程客户端的…

【ai】ubuntu18.04 找不到 nvcc --version问题

nvcc --version显示command not found问题 这个是cuda 库: windows安装了12.5 : 参考大神:解决nvcc --version显示command not found问题 原文链接:https://blog.csdn.net/Flying_sfeng/article/details/103343813 /usr/local/cuda/lib64 与 /usr/local/cuda-11.3/lib64 完…