首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

Python开发环境之pyenv环境搭建

首先到Github上下载Pyenv相应的一键安装脚本, $curl-Lhttps://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer|bash 安装完毕,设置环境变量,设置环境变量,可以添加到个人家目录的.bashrc或.bash_profile及系统的全 局的/etc/profile, #Loadpyenvautomaticallybyadding #thefollowingto~/.bash_profile: exportPATH="/home/lavenliu/.pyenv/bin:$PATH" eval"$(pyenvinit-)" eval"$(pyenvvirtualenv-init-)" 设置完毕之后,可以在命令行进行验证, [lavenliu@VM_113_230_centos~]$.~/.bash_profile [lavenliu@VM_113_230_centos~]$pyenv pyenv20160726 Usage:pyenv<command>[<args>] Someusefulpyenvcommandsare: commandsListallavailablepyenvcommands localSetorshowthelocalapplication-specificPythonversion globalSetorshowtheglobalPythonversion shellSetorshowtheshell-specificPythonversion installInstallaPythonversionusingpython-build uninstallUninstallaspecificPythonversion rehashRehashpyenvshims(runthisafterinstallingexecutables) versionShowthecurrentPythonversionanditsorigin versionsListallPythonversionsavailabletopyenv whichDisplaythefullpathtoanexecutable whenceListallPythonversionsthatcontainthegivenexecutable See`pyenvhelp<command>'forinformationonaspecificcommand. Forfulldocumentation,see:https://github.com/yyuu/pyenv#readme 以上都没有问题。比如,安装一个3.5.2的版本Python, pyenvinstall3.5.2 如果下载速度慢的话,可以事先下载之,放到~/.pyenv/cache目录即可。修改~/.pyenv/plugins/python-build/share/python-build/3.5.2文件, cat~/.pyenv/plugins/python-build/share/python-build/3.5.2 #require_gcc install_package"openssl-1.0.2g""https://www.openssl.org/source/openssl-1.0.2g.tar.gz#b784b1b3907ce39abf4098702dade6365522a253ad1552e267a9a0e89594aa33"mac_openssl--ifhas_broken_mac_openssl install_package"readline-6.3""http://ftpmirror.gnu.org/readline/readline-6.3.tar.gz#56ba6071b9462f980c5a72ab0023893b65ba6debb4eeb475d7a563dc65cafd43"standard--ifhas_broken_mac_readline ifhas_tar_xz_support;then install_package"Python-3.5.2""~/.pyenv/cache/Python-3.5.2.tar.gz"ldflags_dirsstandardverify_py35ensurepip else install_package"Python-3.5.2""~/.pyenv/cache/Python-3.5.2.tar.gz"ldflags_dirsstandardverify_py35ensurepip fi 由于没有~/.pyenv/cache目录,进行手工创建, $mkdir~/.pyenv/cache 如果使用手工安装,则需要安装一些依赖, #yuminstall-ygccmakepatchgdbm-developenssl-develsqlite-develzlib-develbzip2-develreadline-devel 需要事先准备好Python-3.5.2.tar.gz的安装包,放到~/.pyenv/cache目录下。然后,在命令行直接使用pyenv install 3.5.2即可, $pyenvinstall3.5.2 安装完毕,使用version命令进行查看, pyenvversion 3.5.2(setby/home/lavenliu/.python-version) 至此,已经安装完毕。

优秀的个人博客,低调大师

搭建hadoop1.2集群

环境准备 我使用的是vmware workstation,首先安装ubuntu 12.04,安装完成后通过vmware的clone,clone出两个虚机,设置的IP分别是: 192.168.74.130 master 192.168.74.132 node1 192.168.74.133 node2 然后修改各个主机的/etc/hosts中的内容。 使用vi或者gedit,将上边的内容编缉进去。 创建用户 先创建hadoop用户组: sudo addgroup hadoop 然后创建用户hadoop: sudo adduser -ingroup hadoop hadoop 注:在centos 和 redhat下直接创建用户就行,会自动生成相关的用户组和相关文件,而ubuntu下直接创建用户,创建的用户没有根目录。 给hadoop用户添加权限,打开/etc/sudoers文件; sudo gedit /etc/sudoers 按回车键后就会打开/etc/sudoers文件了,给hadoop用户赋予root用户同样的权限。 在root ALL=(ALL:ALL) ALL下添加hadoop ALL=(ALL:ALL) ALL, hadoop ALL=(ALL:ALL) ALL 为本机(master)和子节点(node..)安装JDK环境。 其实网上挺多的,参考http://blog.csdn.net/klov001/article/details/8075237,这里不详细描述了。 修改本机(master)和子节点(node..)机器名 打开/etc/hostname文件; sudo gedit /etc/hostname 分别改为master、node1和node2。 本机(master)和子节点(son..)安装ssh服务 主要为ubuntu安装,cents和redhat系统自带。 ubuntu下: sudo apt-get install ssh openssh-server 建立ssh无密码登录环境 做这一步之前首先建议所有的机子全部转换为hadoop用户,以防出现权限问题的干扰。 ssh生成密钥有rsa和dsa两种生成方式,默认情况下采用rsa方式。 创建ssh-key,这里我们采用rsa方式; ssh-keygen -t rsa -P "" (注:回车后会在~/.ssh/下生成两个文件:id_rsa和id_rsa.pub这两个文件是成对出现的) 进入~/.ssh/目录下,将id_rsa.pub追加到authorized_keys授权文件中,开始是没有authorized_keys文件的; cd ~/.ssh cat id_rsa.pub >> authorized_keys 可以使用ssh 主机名测试一下是否成功。 为mater安装hadoop 在hadoop用户下建立hadoop文件夹,然后将hadoop-1.2.0.tar.gz上传到这个目录下。 tar -zxvf hadoop-1.2.0.tar.gz 解压缩。然后到hadoop目录下conf下找到hadoop-env.sh 配置JAVA_HOME为你上面配置的JAVA_HOME。 找到core-site.xml,配置信息如下: <configuration> <property> <name>hadoop.tmp.dir</name> <value>/home/hadoop/tmp/hadoop-${user.name}</value> <description>A base for other temporarydirectories.</description> </property> <property> <name>fs.default.name</name> <value>hdfs://master:9000</value> <description>The name of the default file system. A URI whose scheme and authority determine the FileSystem implementation. The uri's scheme determines the config property (fs.SCHEME.impl) naming the FileSystem implementation class. The uri's authority is used to determine the host, port, etc. for a filesystem. </description> </property> </configuration> 修改hdfs-site.xml: <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <!-- Put site-specific property overrides in this file. --> <configuration> <property> <name>dfs.replication</name> <value>2</value> <description>Default block replication. The actual number of replications can be specified when the file iscreated. The default is used if replication is not specified in create time. </description> </property> </configuration> 修改mapred-site.xml: <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <!-- Put site-specific property overrides in this file. --> <configuration> <property> <name>mapred.job.tracker</name> <value>master:9001</value> <description>The host and port that the MapReduce job trackerruns at. If "local", then jobs are run in-process as a singlemap and reduce task. </description> </property> </configuration> 修改masters: master 修改slaves: node1 node2 启动hadoop 在master主机上的hadoop安装目录下的bin目录下,执行格式化 ./hadoop namenode -format 正常情况下会出现如下提示: 说明格式化成功。 启动所有结点: ./start-all.sh 会按先后顺序启动,启动完成后,分别到主机和两个node上使用jps查看。 master上显示如下: node1和node2上显示: 在操作的过程中遇到了DataNode不能启动的问题,经过查看node1的hadoop的日志,发现提示错误信息: org.apache.hadoop.hdfs.server.datanode.DataNode: All directories in dfs.data.dir are invalid. 经过查找是因为权限的问题,于是 sudo chmod 755 “你配置的data目录” 问题解决。 运行示例 在根目录下新建文件a,并且向a中随意添加字符串信息。 然后在hdfs上创建目录: ./hadoop dfs -mkdir test1 把刚才创建的文件a上传到test1下: ./hadoop dfs -put ~/a test1 然后查看文件中的内容: ./hadoop dfs -cat test1/a 显示结果如下:

优秀的个人博客,低调大师

Go 语言读写 Excel 基础

Excelize 是 Go 语言编写的一个用来操作 Office Excel 文档类库,基于 ECMA-376 Office OpenXML 标准。可以使用它来读取、写入 XLSX 文件。相比较其他的开源类库,Excelize 支持写入原本带有图片(表)的文档,还支持向 Excel 中插入图片,并且在保存后不会丢失图表样式。 安装 go get github.com/xuri/excelize/v2 创建 XLSX package main import ( "fmt" "github.com/xuri/excelize/v2" ) func main() { f := excelize.NewFile() // Create a new sheet. index := f.NewSheet("Sheet2") // Set value of a cell. f.SetCellValue("Sheet2", "A2", "Hello world.") f.SetCellValue("Sheet1", "B2", 100) // Set active sheet of the workbook. f.SetActiveSheet(index) // Save xlsx file by the given path. err := f.SaveAs("./Book1.xlsx") if err != nil { fmt.Println(err) } } 读取已有文档 package main import ( "fmt" "github.com/xuri/excelize/v2" ) func main() { f, err := excelize.OpenFile("./Book1.xlsx") if err != nil { fmt.Println(err) return } // Get value from cell by given worksheet name and axis. cell, err := f.GetCellValue("Sheet1", "B2") if err != nil { fmt.Println(err) return } fmt.Println(cell) // Get all the rows in the Sheet1. rows, err := f.GetRows("Sheet1") for _, row := range rows { for _, colCell := range row { fmt.Print(colCell, "\t") } fmt.Println() } } 向 Excel 中插入图表 package main import ( "fmt" "github.com/xuri/excelize/v2" ) func main() { categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"} values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8} f := excelize.NewFile() for k, v := range categories { f.SetCellValue("Sheet1", k, v) } for k, v := range values { f.SetCellValue("Sheet1", k, v) } err := f.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`) if err != nil { fmt.Println(err) return } // Save xlsx file by the given path. err = f.SaveAs("./Book1.xlsx") if err != nil { fmt.Println(err) } } 向 Excel 中插入图片 package main import ( "fmt" _ "image/gif" _ "image/jpeg" _ "image/png" "github.com/xuri/excelize/v2" ) func main() { f, err := excelize.OpenFile("./Book1.xlsx") if err != nil { fmt.Println(err) return } // Insert a picture. err = f.AddPicture("Sheet1", "A2", "./image1.png", "") if err != nil { fmt.Println(err) } // Insert a picture to worksheet with scaling. err = f.AddPicture("Sheet1", "D2", "./image2.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`) if err != nil { fmt.Println(err) } // Insert a picture offset in the cell with printing support. err = f.AddPicture("Sheet1", "H2", "./image3.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`) if err != nil { fmt.Println(err) } // Save the xlsx file with the origin path. err = f.Save() if err != nil { fmt.Println(err) } } 还有其他一些功能,在这里就不一一列举了,详细使用文档以及获取后期的维护更新可以从项目的主页获取 github.com/xuri/excelize

优秀的个人博客,低调大师

GaussDB SQL基础语法-变量&常量

一、前言 SQL是用于访问和处理数据库的标准计算机语言。GaussDB支持SQL标准(默认支持SQL2、SQL3和SQL4的主要特性)。 本系列将以《云数据库GaussDB—SQL参考》在线文档为主线进行介绍。 二、GaussDB数据库中的常量和变量的基本概述及语法定义 数据库中的变量和常量是两种重要的数据使用类型。变量是可以变化和被修改的,而常量则是固定不变,不能被修改的。 1、变量定义 在GaussDB中,变量是用于存储可变值的数据类型。变量通常在程序中定义,并在执行期间可以更改其值。在GaussDB中,可以使用以下语法来定义变量: DECLARE variable_name data_type; 其中,variable_name 是变量的名称,data_type 是变量的数据类型。例如,要定义一个名为 my_variable 的整数变量,可以使用以下语句: DECLARE my_variable INT; 2、常量定义 在GaussDB中,常量用于存储固定值的数据类型。常量在程序中定义后,其值不能被修改。在GaussDB中,可以使用以下语法来定义常量: DECLARE constant_name data_type = constant_value; 其中,constant_name 是常量的名称,data_type 是常量的数据类型,constant_value 是常量的值。例如,要定义一个名为 my_constant 的整数常量,可以使用以下语句: DECLARE my_constant INT = 10; 这将定义一个名为 my_constant 的整数常量,其值为 10。 请注意,这只是GaussDB中定义变量和常量的基本语法。具体建议参考GaussDB的官方文档或相关资料以获取更详细信息。 3、其他(%TYPE、%ROWTYPE属性) 变量类型除了支持基本类型,还可使用%TYPE和%ROWTYPE去声明一些与其他表字段或表结构本身相关的变量。 %TYPE属性: %TYPE主要用于声明某个与其他变量类型(例如,表中某列的类型)相同的变量。假如我们想定义一个my_name变量,它的变量类型与employee的firstname类型相同,我们可以通过如下定义: `--使用某列的属性声明 DECLARE my_name employee.firstname%TYPE --使用其他变量的属性声明 DECLARE name VARCHAR(10) NOT NULL := 'ZhangSan'; surname name%TYPE := 'LiSi';` 这样定义可以带来两个好处,首先,我们不用预先知道employee 表的firstname类型具体是什么。其次,即使之后firstname类型有了变化,我们也不需要再次修改my_name的类型。 %ROWTYPE属性: %ROWTYPE属性主要用于对一组数据的类型声明,用于存储表中的一行数据,或从游标匹配的结果。假如,我们需要一组数据,该组数据的字段名称与字段类型都与employee表相同。我们可以通过如下定义: --根据表employee表结构定义变量类型 DECLARE my_employee employee%ROWTYPE 三、在GaussDB数据库中如何使用变量&常量(示例) 变量&常量一般在数据库中不能直接应用到简单的SQL语句中,而是常常用于自定义的函数、存储过程中。下文简单举一列子(可参考前面FUNCTION/PROCEDURE等相关文章): 示例一,定义常量&变量(创建动态语句) 常量作为WHERE 条件之一;变量存储查询的结果值。 示例二,定义变量(创建游标) 定义变量,存储游标结果值 示例三,定义变量(创建package属性重载函数) 定义TEXT类型变量、赋值SQL语句 四、小结 在GaussDB数据库中,变量和常量是两种不同的数据存储方式。变量是用于存储可变值的数据类型,可以在程序执行期间更改其值。而常量则是用于存储固定值的数据类型,其值在定义后不能被修改。 在GaussDB中,可以使用DECLARE语句来定义变量和常量,并通过使用“:=”运算符来为它们赋值。正确地定义和使用变量和常量可以提高程序的灵活性和可维护性,并确保数据的准确性和完整性。 ——结束

优秀的个人博客,低调大师

ElasticSearch必知必会-基础

京东物流 康睿 姚再毅 李振 刘斌 王北永 说明:以下全部均基于eslaticsearch 8.1 版本 一.索引的定义 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/indices.html 索引的全局认知 ElasticSearch Mysql Index Table Type废弃 Table废弃 Document Row Field Column Mapping Schema Everything is indexed Index Query DSL SQL GET http://... select * from POST http://... update table set ... Aggregations group by\sum\sum cardinality 去重 distinct reindex 数据迁移 索引的定义 定义: 相同文档结构(Mapping)文档的结合 由唯一索引名称标定 一个集群中有多个索引 不同的索引代表不同的业务类型数据 注意事项: 索引名称不支持大写 索引名称最大支持255个字符长度 字段的名称,支持大写,不过建议全部统一小写 索引的创建 ​ index-settings 参数解析 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/index-modules.html 注意: 静态参数索引创建后,不再可以修改,动态参数可以修改 思考: 一、为什么主分片创建后不可修改? A document is routed to a particular shard in an index using the following formula: <shard_num = hash(_routing) % num_primary_shards> the defalue value userd for _routing is the document`s _id es中写入数据,是根据上述的公式计算文档应该存储在哪个分片中,后续的文档读取也是根据这个公式,一旦分片数改变,数据也就找不到了 简单理解 根据ID做Hash 然后再 除以 主分片数 取余,被除数改变,结果就不一样了 二、如果业务层面根据数据情况,确实需要扩展主分片数,那怎么办? reindex 迁移数据到另外一个索引 https://www.elastic.co/guide/en/elasticsearch/reference/8.1/docs-reindex.html ​ 索引的基本操作 ​ 二.Mapping-Param之dynamic 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/dynamic.html 核心功能 自动检测字段类型后添加字段 也就是哪怕你没有在es的mapping中定义该字段,es也会动态的帮你检测字段类型 初识dynamic // 删除test01索引,保证这个索引现在是干净的 DELETE test01 // 不定义mapping,直接一条插入数据试试看, POST test01/_doc/1 { "name":"kangrui10" } // 然后我们查看test01该索引的mapping结构 看看name这个字段被定义成了什么类型 // 由此可以看出,name一级为text类型,二级定义为keyword,但其实这并不是我们想要的结果, // 我们业务查询中name字段并不会被分词查询,一般都是全匹配(and name = xxx) // 以下的这种结果,我们想要实现全匹配 就需要 name.keyword = xxx 反而麻烦 GET test01/_mapping { "test01" : { "mappings" : { "properties" : { "name" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } } } } } } dynamic的可选值 可选值 说明 解释 true New fields are added to the mapping (default). 创建mapping时,如果不指定dynamic的值,默认true,即如果你的字段没有收到指定类型,就会es帮你动态匹配字段类型 false New fields are ignored. These fields will not be indexed or searchable, but will still appear in the _source field of returned hits. These fields will not be added to the mapping, and new fields must be added explicitly. 若设置为false,如果你的字段没有在es的mapping中创建,那么新的字段,一样可以写入,但是不能被查询,mapping中也不会有这个字段,也就是被写入的字段,不会被创建索引 strict If new fields are detected, an exception is thrown and the document is rejected. New fields must be explicitly added to the mapping. 若设置为strict,如果新的字段,没有在mapping中创建字段,添加会直接报错,生产环境推荐,更加严谨。示例如下,如要新增字段,就必须手动的新增字段 动态映射的弊端 字段匹配相对准确,但不一定是用户期望的 比如现在有一个text字段,es只会给你设置为默认的standard分词器,但我们一般需要的是ik中文分词器 占用多余的存储空间 string类型匹配为text和keyword两种类型,意味着会占用更多的存储空间 mapping爆炸 如果不小心写错了查询语句,get用成了put误操作,就会错误创建很多字段 三.Mapping-Param之doc_values 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/doc-values.html 核心功能 DocValue其实是Lucene在构建倒排索引时,会额外建立一个有序的正排索引(基于document => field value的映射列表) DocValue本质上是一个序列化的 列式存储,这个结构非常适用于聚合(aggregations)、排序(Sorting)、脚本(scripts access to field)等操作。而且,这种存储方式也非常便于压缩,特别是数字类型。这样可以减少磁盘空间并且提高访问速度。 几乎所有字段类型都支持DocValue,除了text和annotated_text字段。 何为正排索引 正排索引其实就是类似于数据库表,通过id和数据进行关联,通过搜索文档id,来获取对应的数据 doc_values可选值 true:默认值,默认开启 false:需手动指定,设置为false后,sort、aggregate、access the field from script将会无法使用,但会节省磁盘空间 真题演练 // 创建一个索引,test03,字段满足以下条件 // 1. speaker: keyword // 2. line_id: keyword and not aggregateable // 3. speech_number: integer PUT test03 { "mappings": { "properties": { "speaker": { "type": "keyword" }, "line_id":{ "type": "keyword", "doc_values": false }, "speech_number":{ "type": "integer" } } } } 四.分词器analyzers ik中文分词器安装 https://github.com/medcl/elasticsearch-analysis-ik 何为倒排索引 ​ 数据索引化的过程 ​ 分词器的分类 官网地址: https://www.elastic.co/guide/en/elasticsearch/reference/8.1/analysis-analyzers.html ​ 五.自定义分词 自定义分词器三段论 1.Character filters 字符过滤 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/analysis-charfilters.html 可配置0个或多个 HTML Strip Character Filter:用途:删除HTML元素,如 <b>,并解 码HTML实体,如&amp Mapping Character Filter:用途:替换指定字符 Pattern Replace Character Filter:用途:基于正则表达式替换指定字符 2.Tokenizer 文本切为分词 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/analysis-tokenizers.html#_word_oriented_tokenizers 只能配置一个 用分词器对文本进行分词 3.Token filters 分词后再过滤 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/analysis-tokenfilters.html 可配置0个或多个 分词后再加工,比如转小写、删除某些特殊的停用词、增加同义词等 真题演练 有一个文档,内容类似 dag & cat, 要求索引这个文档,并且使用match_parase_query, 查询dag & cat 或者 dag and cat,都能够查到 题目分析: 1.何为match_parase_query:match_phrase 会将检索关键词分词。match_phrase的分词结果必须在被检索字段的分词中都包含,而且顺序必须相同,而且默认必须都是连续的。 2.要实现 & 和 and 查询结果要等价,那么就需要自定义分词器来实现了,定制化的需求 3.如何自定义一个分词器:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/analysis-custom-analyzer.html 4.解法1核心使用功能点,Mapping Character Filter 5.解法2核心使用功能点,https://www.elastic.co/guide/en/elasticsearch/reference/8.1/analysis-synonym-tokenfilter.html 解法1 # 新建索引 PUT /test01 { "settings": { "analysis": { "analyzer": { "my_analyzer": { "char_filter": [ "my_mappings_char_filter" ], "tokenizer": "standard", } }, "char_filter": { "my_mappings_char_filter": { "type": "mapping", "mappings": [ "& => and" ] } } } }, "mappings": { "properties": { "content":{ "type": "text", "analyzer": "my_analyzer" } } } } // 说明 // 三段论之Character filters,使用char_filter进行文本替换 // 三段论之Token filters,使用默认分词器 // 三段论之Token filters,未设定 // 字段content 使用自定义分词器my_analyzer # 填充测试数据 PUT test01/_bulk {"index":{"_id":1}} {"content":"doc & cat"} {"index":{"_id":2}} {"content":"doc and cat"} # 执行测试,doc & cat || oc and cat 结果输出都为两条 POST test01/_search { "query": { "bool": { "must": [ { "match_phrase": { "content": "doc & cat" } } ] } } } 解法2 # 解题思路,将& 和 and 设定为同义词,使用Token filters # 创建索引 PUT /test02 { "settings": { "analysis": { "analyzer": { "my_synonym_analyzer": { "tokenizer": "whitespace", "filter": [ "my_synonym" ] } }, "filter": { "my_synonym": { "type": "synonym", "lenient": true, "synonyms": [ "& => and" ] } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "my_synonym_analyzer" } } } } // 说明 // 三段论之Character filters,未设定 // 三段论之Token filters,使用whitespace空格分词器,为什么不用默认分词器?因为默认分词器会把&分词后剔除了,就无法在去做分词后的过滤操作了 // 三段论之Token filters,使用synony分词后过滤器,对&和and做同义词 // 字段content 使用自定义分词器my_synonym_analyzer # 填充测试数据 PUT test02/_bulk {"index":{"_id":1}} {"content":"doc & cat"} {"index":{"_id":2}} {"content":"doc and cat"} # 执行测试 POST test02/_search { "query": { "bool": { "must": [ { "match_phrase": { "content": "doc & cat" } } ] } } } 六.multi-fields 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/multi-fields.html // 单字段多类型,比如一个字段我想设置两种分词器 PUT my-index-000001 { "mappings": { "properties": { "city": { "type": "text", "analyzer":"standard", "fields": { "fieldText": { "type": "text", "analyzer":"ik_smart", } } } } } } 七.runtime_field 运行时字段 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/runtime.html 产生背景 假如业务中需要根据某两个数字类型字段的差值来排序,也就是我需要一个不存在的字段, 那么此时应该怎么办? 当然你可以刷数,新增一个差值结果字段来实现,假如此时不允许你刷数新增字段怎么办? 解决方案 ​ 应用场景 在不重新建立索引的情况下,向现有文档新增字段 在不了解数据结构的情况下处理数据 在查询时覆盖从原索引字段返回的值 为特定用途定义字段而不修改底层架构 功能特性 Lucene完全无感知,因没有被索引化,没有doc_values 不支持评分,因为没有倒排索引 打破传统先定义后使用的方式 能阻止mapping爆炸 增加了API的灵活性 注意,会使得搜索变慢 实际使用 运行时检索指定,即检索环节可使用(也就是哪怕mapping中没有这个字段,我也可以查询) 动态或静态mapping指定,即mapping环节可使用(也就是在mapping中添加一个运行时的字段) 真题演练1 # 假定有以下索引和数据 PUT test03 { "mappings": { "properties": { "emotion": { "type": "integer" } } } } POST test03/_bulk {"index":{"_id":1}} {"emotion":2} {"index":{"_id":2}} {"emotion":5} {"index":{"_id":3}} {"emotion":10} {"index":{"_id":4}} {"emotion":3} # 要求:emotion > 5, 返回emotion_falg = '1', # 要求:emotion < 5, 返回emotion_falg = '-1', # 要求:emotion = 5, 返回emotion_falg = '0', 解法1 检索时指定运行时字段: https://www.elastic.co/guide/en/elasticsearch/reference/8.1/runtime-search-request.html 该字段本质上是不存在的,所以需要检索时要加上 fields * GET test03/_search { "fields": [ "*" ], "runtime_mappings": { "emotion_falg": { "type": "keyword", "script": { "source": """ if(doc['emotion'].value>5)emit('1'); if(doc['emotion'].value<5)emit('-1'); if(doc['emotion'].value==5)emit('0'); """ } } } } 解法2 创建索引时指定运行时字段:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/runtime-mapping-fields.html 该方式支持通过运行时字段做检索 # 创建索引并指定运行时字段 PUT test03_01 { "mappings": { "runtime": { "emotion_falg": { "type": "keyword", "script": { "source": """ if(doc['emotion'].value>5)emit('1'); if(doc['emotion'].value<5)emit('-1'); if(doc['emotion'].value==5)emit('0'); """ } } }, "properties": { "emotion": { "type": "integer" } } } } # 导入测试数据 POST test03_01/_bulk {"index":{"_id":1}} {"emotion":2} {"index":{"_id":2}} {"emotion":5} {"index":{"_id":3}} {"emotion":10} {"index":{"_id":4}} {"emotion":3} # 查询测试 GET test03_01/_search { "fields": [ "*" ] } 真题演练2 # 有以下索引和数据 PUT test04 { "mappings": { "properties": { "A":{ "type": "long" }, "B":{ "type": "long" } } } } PUT task04/_bulk {"index":{"_id":1}} {"A":100,"B":2} {"index":{"_id":2}} {"A":120,"B":2} {"index":{"_id":3}} {"A":120,"B":25} {"index":{"_id":4}} {"A":21,"B":25} # 需求:在task04索引里,创建一个runtime字段,其值是A-B,名称为A_B; 创建一个range聚合,分为三级:小于0,0-100,100以上;返回文档数 // 使用知识点: // 1.检索时指定运行时字段: https://www.elastic.co/guide/en/elasticsearch/reference/8.1/runtime-search-request.html // 2.范围聚合 https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-bucket-range-aggregation.html 解法 # 结果测试 GET task04/_search { "fields": [ "*" ], "size": 0, "runtime_mappings": { "A_B": { "type": "long", "script": { "source": """ emit(doc['A'].value - doc['B'].value); """ } } }, "aggs": { "price_ranges_A_B": { "range": { "field": "A_B", "ranges": [ { "to": 0 }, { "from": 0, "to": 100 }, { "from": 100 } ] } } } } 八.Search-highlighted highlighted语法初识 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/highlighting.html ​ 九.Search-Order Order语法初识 官网文档地址: https://www.elastic.co/guide/en/elasticsearch/reference/8.1/sort-search-results.html // 注意:text类型默认是不能排或聚合的,如果非要排序或聚合,需要开启fielddata GET /kibana_sample_data_ecommerce/_search { "query": { "match": { "customer_last_name": "wood" } }, "highlight": { "number_of_fragments": 3, "fragment_size": 150, "fields": { "customer_last_name": { "pre_tags": [ "<em>" ], "post_tags": [ "</em>" ] } } }, "sort": [ { "currency": { "order": "desc" }, "_score": { "order": "asc" } } ] } 十.Search-Page page语法初识 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/paginate-search-results.html # 注意 from的起始值是 0 不是 1 GET kibana_sample_data_ecommerce/_search { "from": 5, "size": 20, "query": { "match": { "customer_last_name": "wood" } } } 真题演练1 # 题目 In the spoken lines of the play, highlight the word Hamlet (int the text_entry field) startint the highlihnt with "#aaa#" and ending it with "#bbb#" return all of speech_number field lines in reverse order; '20' speech lines per page,starting from line '40' # highlight 处理 text_entry 字段 ; 关键词 Hamlet 高亮 # page分页:from:40;size:20 # speech_number:倒序 POST test09/_search { "from": 40, "size": 20, "query": { "bool": { "must": [ { "match": { "text_entry": "Hamlet" } } ] } }, "highlight": { "fields": { "text_entry": { "pre_tags": [ "#aaa#" ], "post_tags": [ "#bbb#" ] } } }, "sort": [ { "speech_number.keyword": { "order": "desc" } } ] } 十一.Search-AsyncSearch 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/async-search.html 发行版本 7.7.0 适用场景 允许用户在异步搜索结果时可以检索,从而消除了仅在查询完成后才等待最终响应的情况 常用命令 执行异步检索 POST /sales*/_async_search?size=0 查看异步检索 GET /_async_search/id值 查看异步检索状态 GET /_async_search/id值 删除、终止异步检索 DELETE /_async_search/id值 异步查询结果说明 返回值 含义 id 异步检索返回的唯一标识符 is_partial 当查询不再运行时,指示再所有分片上搜索是成功还是失败。在执行查询时,is_partial=true is_running 搜索是否仍然再执行 total 将在多少分片上执行搜索 successful 有多少分片已经成功完成搜索 十二.Aliases索引别名 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/aliases.html Aliases的作用 在ES中,索引别名(index aliases)就像一个快捷方式或软连接,可以指向一个或多个索引。别名带给我们极大的灵活性,我们可以使用索引别名实现以下功能: 在一个运行中的ES集群中无缝的切换一个索引到另一个索引上(无需停机) 分组多个索引,比如按月创建的索引,我们可以通过别名构造出一个最近3个月的索引 查询一个索引里面的部分数据构成一个类似数据库的视图(views 假设没有别名,如何处理多索引的检索 方式1:POST index_01,index_02.index_03/_search 方式2:POST index*/search 创建别名的三种方式 创建索引的同时指定别名 # 指定test05的别名为 test05_aliases PUT test05 { "mappings": { "properties": { "name":{ "type": "keyword" } } }, "aliases": { "test05_aliases": {} } } 使用索引模板的方式指定别名 PUT _index_template/template_1 { "index_patterns": ["te*", "bar*"], "template": { "settings": { "number_of_shards": 1 }, "mappings": { "_source": { "enabled": true }, "properties": { "host_name": { "type": "keyword" }, "created_at": { "type": "date", "format": "EEE MMM dd HH:mm:ss Z yyyy" } } }, "aliases": { "mydata": { } } }, "priority": 500, "composed_of": ["component_template1", "runtime_component_template"], "version": 3, "_meta": { "description": "my custom" } } 对已有的索引创建别名 POST _aliases { "actions": [ { "add": { "index": "logs-nginx.access-prod", "alias": "logs" } } ] } 删除别名 POST _aliases { "actions": [ { "remove": { "index": "logs-nginx.access-prod", "alias": "logs" } } ] } 真题演练1 # Define an index alias for 'accounts-row' called 'accounts-male': Apply a filter to only show the male account owners # 为'accounts-row'定义一个索引别名,称为'accounts-male':应用一个过滤器,只显示男性账户所有者 POST _aliases { "actions": [ { "add": { "index": "accounts-row", "alias": "accounts-male", "filter": { "bool": { "filter": [ { "term": { "gender.keyword": "male" } } ] } } } } ] } 十三.Search-template 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-template.html 功能特点 模板接受在运行时指定参数。搜索模板存储在服务器端,可以在不更改客户端代码的情况下进行修改。 初识search-template # 创建检索模板 PUT _scripts/my-search-template { "script": { "lang": "mustache", "source": { "query": { "match": { "{{query_key}}": "{{query_value}}" } }, "from": "{{from}}", "size": "{{size}}" } } } # 使用检索模板查询 GET my-index/_search/template { "id": "my-search-template", "params": { "query_key": "your filed", "query_value": "your filed value", "from": 0, "size": 10 } } 索引模板的操作 创建索引模板 PUT _scripts/my-search-template { "script": { "lang": "mustache", "source": { "query": { "match": { "message": "{{query_string}}" } }, "from": "{{from}}", "size": "{{size}}" }, "params": { "query_string": "My query string" } } } 验证索引模板 POST _render/template { "id": "my-search-template", "params": { "query_string": "hello world", "from": 20, "size": 10 } } 执行检索模板 GET my-index/_search/template { "id": "my-search-template", "params": { "query_string": "hello world", "from": 0, "size": 10 } } 获取全部检索模板 GET _cluster/state/metadata?pretty&filter_path=metadata.stored_scripts 删除检索模板 DELETE _scripts/my-search-templateath=metadata.stored_scripts 十四.Search-dsl 简单检索 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl.html 检索选型 ​ 检索分类 ​ 自定义评分 如何自定义评分 ​ 1.index Boost索引层面修改相关性 // 一批数据里,有不同的标签,数据结构一致,不同的标签存储到不同的索引(A、B、C),最后要严格按照标签来分类展示的话,用什么查询比较好? // 要求:先展示A类,然后B类,然后C类 # 测试数据如下 put /index_a_123/_doc/1 { "title":"this is index_a..." } put /index_b_123/_doc/1 { "title":"this is index_b..." } put /index_c_123/_doc/1 { "title":"this is index_c..." } # 普通不指定的查询方式,该查询方式下,返回的三条结果数据评分是相同的 POST index_*_123/_search { "query": { "bool": { "must": [ { "match": { "title": "this" } } ] } } } 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-search.html indices_boost # 也就是索引层面提升权重 POST index_*_123/_search { "indices_boost": [ { "index_a_123": 10 }, { "index_b_123": 5 }, { "index_c_123": 1 } ], "query": { "bool": { "must": [ { "match": { "title": "this" } } ] } } } 2.boosting 修改文档相关性 某索引index_a有多个字段, 要求实现如下的查询: 1)针对字段title,满足'ssas'或者'sasa’。 2)针对字段tags(数组字段),如果tags字段包含'pingpang', 则提升评分。 要求:写出实现的DSL? # 测试数据如下 put index_a/_bulk {"index":{"_id":1}} {"title":"ssas","tags":"basketball"} {"index":{"_id":2}} {"title":"sasa","tags":"pingpang; football"} # 解法1 POST index_a/_search { "query": { "bool": { "must": [ { "bool": { "should": [ { "match": { "title": "ssas" } }, { "match": { "title": "sasa" } } ] } } ], "should": [ { "match": { "tags": { "query": "pingpang", "boost": 1 } } } ] } } } # 解法2 // https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-function-score-query.html POST index_a/_search { "query": { "bool": { "should": [ { "function_score": { "query": { "match": { "tags": { "query": "pingpang" } } }, "boost": 1 } } ], "must": [ { "bool": { "should": [ { "match": { "title": "ssas" } }, { "match": { "title": "sasa" } } ] } } ] } } } 3.negative_boost降低相关性 对于某些结果不满意,但又不想通过 must_not 排除掉,可以考虑可以考虑boosting query的negative_boost。 即:降低评分 negative_boost (Required, float) Floating point number between 0 and 1.0 used to decrease the relevance scores of documents matching the negative query. 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-boosting-query.html POST index_a/_search { "query": { "boosting": { "positive": { "term": { "tags": "football" } }, "negative": { "term": { "tags": "pingpang" } }, "negative_boost": 0.5 } } } 4.function_score 自定义评分 如何同时根据 销量和浏览人数进行相关度提升? 问题描述:针对商品,例如有想要有一个提升相关度的计算,同时针对销量和浏览人数? 例如oldScore*(销量+浏览人数) ************************** 商品 销量 浏览人数 A 10 10 B 20 20 C 30 30 ************************** # 示例数据如下 put goods_index/_bulk {"index":{"_id":1}} {"name":"A","sales_count":10,"view_count":10} {"index":{"_id":2}} {"name":"B","sales_count":20,"view_count":20} {"index":{"_id":3}} {"name":"C","sales_count":30,"view_count":30} 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-function-score-query.html 知识点:script_score POST goods_index/_search { "query": { "function_score": { "query": { "match_all": {} }, "script_score": { "script": { "source": "_score * (doc['sales_count'].value+doc['view_count'].value)" } } } } } 十五.Search-del Bool复杂检索 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-bool-query.html 基本语法 ​ 真题演练 写一个查询,要求某个关键字再文档的四个字段中至少包含两个以上 功能点:bool 查询,should / minimum_should_match 1.检索的bool查询 2.细节点 minimum_should_match 注意:minimum_should_match 当有其他子句的时候,默认值为0,当没有其他子句的时候默认值为1 POST test_index/_search { "query": { "bool": { "should": [ { "match": { "filed1": "kr" } }, { "match": { "filed2": "kr" } }, { "match": { "filed3": "kr" } }, { "match": { "filed4": "kr" } } ], "minimum_should_match": 2 } } } 十六.Search-Aggregations 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations.html 聚合分类 ​ ​ 分桶聚合(bucket) terms 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-bucket-terms-aggregation.html # 按照作者统计文档数 POST bilili_elasticsearch/_search { "size": 0, "aggs": { "agg_user": { "terms": { "field": "user", "size": 1 } } } } date_histogram 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-bucket-datehistogram-aggregation.html # 按照up_time 按月进行统计 POST bilili_elasticsearch/_search { "size": 0, "aggs": { "agg_up_time": { "date_histogram": { "field": "up_time", "calendar_interval": "month" } } } } 指标聚合 (metrics) Max 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-metrics-max-aggregation.html # 获取up_time最大的 POST bilili_elasticsearch/_search { "size": 0, "aggs": { "agg_max_up_time": { "max": { "field": "up_time" } } } } Top_hits 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-metrics-top-hits-aggregation.html # 根据user聚合只取一个聚合结果,并且获取命中数据的详情前3条,并按照指定字段排序 POST bilili_elasticsearch/_search { "size": 0, "aggs": { "terms_agg_user": { "terms": { "field": "user", "size": 1 }, "aggs": { "top_user_hits": { "top_hits": { "_source": { "includes": [ "video_time", "title", "see", "user", "up_time" ] }, "sort": [ { "see":{ "order": "desc" } } ], "size": 3 } } } } } } // 返回结果如下 { "took" : 91, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1000, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "terms_agg_user" : { "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 975, "buckets" : [ { "key" : "Elastic搜索", "doc_count" : 25, "top_user_hits" : { "hits" : { "total" : { "value" : 25, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "bilili_elasticsearch", "_id" : "5ccCVoQBUyqsIDX6wIcm", "_score" : null, "_source" : { "video_time" : "03:45", "see" : "92", "up_time" : "2021-03-19", "title" : "Elastic 社区大会2021: 用加 Gatling 进行Elasticsearch的负载测试,寓教于乐。", "user" : "Elastic搜索" }, "sort" : [ "92" ] }, { "_index" : "bilili_elasticsearch", "_id" : "8scCVoQBUyqsIDX6wIgn", "_score" : null, "_source" : { "video_time" : "10:18", "see" : "79", "up_time" : "2020-10-20", "title" : "为Elasticsearch启动htpps访问", "user" : "Elastic搜索" }, "sort" : [ "79" ] }, { "_index" : "bilili_elasticsearch", "_id" : "7scCVoQBUyqsIDX6wIcm", "_score" : null, "_source" : { "video_time" : "04:41", "see" : "71", "up_time" : "2021-03-19", "title" : "Elastic 社区大会2021: Elasticsearch作为一个地理空间的数据库", "user" : "Elastic搜索" }, "sort" : [ "71" ] } ] } } } ] } } } 子聚合 (Pipeline) Pipeline:基于聚合的聚合 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-pipeline.html bucket_selector 官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-pipeline-bucket-selector-aggregation.html # 根据order_date按月分组,并且求销售总额大于1000 POST kibana_sample_data_ecommerce/_search { "size": 0, "aggs": { "date_his_aggs": { "date_histogram": { "field": "order_date", "calendar_interval": "month" }, "aggs": { "sum_aggs": { "sum": { "field": "total_unique_products" } }, "sales_bucket_filter": { "bucket_selector": { "buckets_path": { "totalSales": "sum_aggs" }, "script": "params.totalSales > 1000" } } } } } } 真题演练 earthquakes索引中包含了过去30个月的地震信息,请通过一句查询,获取以下信息 l 过去30个月,每个月的平均 mag l 过去30个月里,平均mag最高的一个月及其平均mag l 搜索不能返回任何文档 max_bucket 官网地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-pipeline-max-bucket-aggregation.html POST earthquakes/_search { "size": 0, "query": { "range": { "time": { "gte": "now-30M/d", "lte": "now" } } }, "aggs": { "agg_time_his": { "date_histogram": { "field": "time", "calendar_interval": "month" }, "aggs": { "avg_aggs": { "avg": { "field": "mag" } } } }, "max_mag_sales": { "max_bucket": { "buckets_path": "agg_time_his>avg_aggs" } } } }

优秀的个人博客,低调大师

自定义 WorkManager —— 基础概念

WorkManager是一个Android Jetpack扩展库,它可以让您轻松规划那些可延后、异步但又需要可靠运行的任务。对于绝大部分后台执行任务来说,使用 WorkManager 是目前 Android 平台上的最佳实践。 目前为止本系列已经讨论过: Android Jetpack WorkManager | Android 中文教学视频 WorkManager 在Kotlin 中的实践 WorkManager: 周期性任务 在本篇文章中,我们将会讨论自定义配置相关的内容,包括: 为什么可能会需要自定义配置 如何声明自定义配置 WorkerFactory 以及自定义 WorkerFactory 的原因 DelegatingWorkerFactory 详解 本系列的下一篇文章将对依赖注入和 Dagger 展开讨论,请持续关注我们。 使用 WorkManager 时,您需要自己定义Worker/CoroutineWorker或任何ListenableWorker的派生类。WorkManager 会在正确的时间点实例化您的 Worker,其时机独立于您应用的运行,不受其运行状态的影响。为了可以初始化您的 Worker,WorkManager 会使用一个WorkerFactory。 默认 WorkerFactory 所创建的 Worker 只包含两个参数: Application 的 Context WorkerParameters 如果您需要通过 Worker 的构造函数传入更多参数,则需要一个自定义的 WorkerFactory。 延伸阅读 : 我们讲过默认的 WorkerFactory 使用反射来实例化正确的 ListenableWorker 类,但当我们的 Worker 类的类名被 R8 (或 ProGuard) 最小化之后,这个操作就会失败。为了避免这种情况,WorkManager 包含了一个proguard-rules.pro文件来避免您的 Worker 类的类名被混淆。 自定义配置和 WorkerFactory WorkManager 类遵循 单例模式,而且它只能在实例化之前进行配置。这意味着,如果您想自定义它的配置,就必须先禁用默认配置。 如果您尝试通过 initialize() 方法再次初始化 WorkManager,该方法就会抛出一个异常 (于 1.0.0 版本中加入)。为了避免异常,您需要禁用默认的初始化。您可以稍后在您的 Application 的 onCreate 方法中配置和初始化您的 WorkManager。 2.1.0 版本 中加入了一个更好的初始化 WorkManager 的方式。您可以通过在您的 Application 类中实现 WorkManager 的Configuration.Provider接口的方式来使用按需初始化。接下来,您只需要使用getInstance(context)获得实例,WorkManager 就会通过您的配置初始化它自己。 可配置参数 如上所讲,您可以配置用来创建 Worker 的 WorkerFactory,但是您也可以自定义其他的参数。WorkManager 的Configuration.Builder参考指南中包含了参数的完整列表。这里我想强调两个附加参数: Logging 级别 JobId 范围 当我们有需要时,可以通过修改日志级别方便地理解 WorkManager 中正在发生什么。关于这个话题,我们有一个 专门的文档页。您也可以查看Advanced WorkManager codelab 实战教程,以了解此功能在真实示例中的实现,以及您可以通过此功能获取到什么样的信息。 如果以在我们的应用中使用 JobScheduler API 一样的方式使用 WorkManager,我们可能也会想要自定义 JobId 范围。因为在这种情况下,您会想要避免在同一个地方使用相同的 JobId 范围。版本 2.4.0 中也加入了一个新的Lint 规则 来覆盖这种情况。 WorkManager 的 WorkerFactory 我们已经知道,WorkManager 有一个默认的 WorkerFactory,它可以根据我们经由 WorkRequest 传入的 Worker 类的类名,通过反射来找到应该实例化的 Worker 类。 ⚠️ 如果您在创建了一个 WorkRequest 后重构了应用,并为您的 Worker 类起了另一个名字,WorkManager 就会因为无法找到正确的类而抛出一个 ClassNotFoundException。 您可能会想要为您的 Worker 的构造函数添加其他参数。假设您有一个 Worker 需要引用一个 Retrofit 服务来跟远程服务器进行通讯: /* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ class UpvoteStoryWorker( appContext: Context, workerParams: WorkerParameters, private val service: UpvoteStoryHttpApi) : CoroutineWorker(appContext, workerParams) { override suspend fun doWork(): Result { return try { // 投票操作 Result.success() } catch (e: Exception) { if (runAttemptCount < MAX_NUMBER_OF_RETRY) { Result.retry() } else { Result.failure() } } } } 如果我们在一个应用上作出上面的修改,程序仍然可被正常编译。但是只要代码被执行、WorkManager 尝试去实例化这个 CoroutineWorker 时,应用就会因为抛出异常而被关闭。异常的描述为无法找到正确的方法来进行实例化: Caused by java.lang.NoSuchMethodException: <init> [class android.content.Context, class androidx.work.WorkerParameters] 这时我们就需要一个自定义的 WorkerFactory。 但是别着急,我们已经看到其中涉及的几个步骤。现在让我们回顾一下我们已经做了的事情,然后深入了解其中每一步的详细信息: 禁用默认初始化 实现一个自定义 WorkerFactory 创建自定义配置 初始化 WorkManager 禁用默认初始化 如WorkManager 的文档 中描述,禁用操作要在您的 AndroidManifest.xml 文件中完成。移除默认情况下从 WorkManager 库中自动合并的节点。 <!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> <application … <provider android:name="androidx.work.impl.WorkManagerInitializer" android:authorities="${applicationId}.workmanager-init" tools:node="remove" /> </application> 实现一个自定义 WorkerFactory 为了创建包含正确参数的 Worker,现在需要实现我们自己的工厂 (factory): /* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ class MyWorkerFactory(private val service: UpvoteStoryHttpApi) : WorkerFactory() { override fun createWorker( appContext: Context, workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? { // 这里只能处理一个 Worker,请不要这样做! // 参考下文来更好地使用 DelegatingWorkerFactory return UpvoteStoryWorker(appContext, workerParameters, DesignerNewsService) } } 创建一个自定义 WorkerConfiguration** 接下来,我们必须将我们的工厂注册到我们的 WorkManager 的自定义配置中: /* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ class MyApplication : Application(), Configuration.Provider { override fun getWorkManagerConfiguration(): Configuration = Configuration.Builder() .setMinimumLoggingLevel(android.util.Log.DEBUG) .setWorkerFactory(MyWorkerFactory(DesignerNewsService)) .build() ... } 初始化 WorkManager 当您的应用中只有一个 Worker 类时,以上便是您所需要做的所有事情。而如果您有多个或者预计未来会有多个 Worker 类,更好的解决方案是使用在 2.1 版中加入的DelegatingWorkerFactory。 使用 DelegatingWorkerFactory 我们可以通过使用 DelegatingWorkerFactory 来替代将 WorkManager 配置为直接使用某个工厂的操作。我们可以使用 DelegatingWorkerFactory 的addFactory()方法向其添加我们的工厂,这样一来,您就有了多个工厂,其中每个都可以管理一个或多个 Worker。在 DelegatingWorkerFactory 中注册您的工厂,这将有助于协调多个工厂的执行。 在这种情况下,您的工厂需要检查是否知道如何处理作为参数传入的 workerClassName。如果答案是否定的,就返回 null,而 DelegatingWorkerFactory 便会去寻找下一个注册的工厂。如果没有任何被注册的工厂知道如何处理某个类,那么它将回退到使用反射的默认工厂。 下面是我们的工厂类代码,修改为当它不知道如何处理某个 workerClassName 时,将返回 null: /* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ class MyWorkerFactory(private val service: DesignerNewsService) : WorkerFactory() { override fun createWorker( appContext: Context, workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? { return when(workerClassName) { UpvoteStoryWorker::class.java.name -> ConferenceDataWorker(appContext, workerParameters, service) else -> // 返回 null,这样基类就可以代理到默认的 WorkerFactory null } } } 我们的 WorkManager 配置会变成: /* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ class MyApplication : Application(), Configuration.Provider { override fun getWorkManagerConfiguration(): Configuration { val myWorkerFactory = DelegatingWorkingFactory() myWorkerFactory.addFactory(MyWorkerFactory(service)) // 在这里添加您应用中可能会使用的其他 WorkerFactory return Configuration.Builder() .setMinimumLoggingLevel(android.util.Log.INFO) .setWorkerFactory(myWorkerFactory) .build() } ... } 如果您有多个 Worker 需要不同的参数,您可以创建第二个 WorkerFactory,并通过再次调用 addFactory 来添加它。 总结 WorkManager 是一个功能十分强大的库,它的默认配置已经可以覆盖许多常见的使用场景。然而当您遇到某些情况时,诸如需要增加日志级别或需要传入额外参数到您的 Worker 时,则需要一个自定义的配置。 希望您能通过本文对此主题有一个良好的认识。如果您有任何疑问,可以在评论区中留言。 接下来的文章我们将会讨论如何在自定义 WorkManager 配置时使用 Dagger,感兴趣的读者请继续关注。

优秀的个人博客,低调大师

【Vue_01】基础知识

一、Vue 介绍 1. 作者介绍 2. Vue 简介 ① Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。 ② Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。 ③ Vue 借鉴了 Angular 的模板和双向绑定技术;借鉴了 react 的组件化和虚拟 DOM 技术。 ④ MVVM 模式是 Model-View-ViewModel 的缩写,Model 代表数据模型,定义数据操作的业务逻辑,View 代表视图层,负责将数据模型渲染到页面上,ViewModel 通过双向绑定把 View 和 Model 进行同步交互,不需要手动操作DOM 的一种设计思想 3. MVVM 介绍 M: 即Model,模型,包括数据和一些基本操作 V: 即View,视图,页面渲染结果 VM:即View-Model,模型与视图间的双向操作(无需开发人员干涉) 在MVVM之前,开发人员从后端获取需要的数据模型,然后要通过DOM操作Model渲染到View中。而后当用户操作视图,我们还需要通过DOM获取View中的数据,然后同步到Model中。 而MVVM中的VM要做的事情就是把DOM操作完全封装起来,开发人员不用再关心Model和View之间是如何互相影响的, 只要我们Model发生了改变,View上自然就会表现出来。当用户修改了View,Model中的数据也会跟着改变。把开发人员从繁琐的DOM操作中解放出来,把关注点放在如何操作Model上。 二、快速使用 1. Vue 起步 创建一个 HTML 文件,引入: <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 生产环境版本,优化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script> 【注意】使用自闭合标签可能会出现问题 2. 声明式渲染 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>第一个程序</title> <script src="vue.js" ></script> </head> <body> <div id="app"> <!-- 插值表达式 --> <h2>姓名:{{name}},年龄:{{age}}</h2> </div> <script> // 创建vue实例 var app = new Vue({ el:"#app", // el即element,该vue实例要渲染的页面元素 data:{ // 渲染页面需要的数据 name : "王大锤", age : 250 } }); </script> </body> </html> 此时我们已经成功创建了一个 Vue 应用,打开 HTML 页面会显示 Hello Vue! 。这就是声明式渲染,Vue 的核心是:采用简洁的模板语法声明式地将数据渲染进 DOM 的系统。这里的核心思想就是没有繁琐的 DOM 操作,例如 jQuery 中,我们需要先找到 div 节点,获取到 DOM 对象,然后进行一系列的节点操作。 三、Vue实例 1. 创建Vue实例 var app = new Vue({ // Vue实例 el:"#app", // Vue可以对id为app的div元素进行渲染,该div之外的无法控制 data:{ // 数据,存放可能会用到的数据 name : "王大锤", age : 250 }, methods: { // 方法,声明可能会用到的方法 test() { console.log("测试方法"); } } }); 2. 双向绑定 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>第一个程序</title> <script src="vue.js" ></script> </head> <body> <div id="app"> 请输入年龄:<input type="text" v-model="age"> <h2>姓名:{{name}},年龄:{{age}}</h2> </div> <script> var app = new Vue({ el:"#app", data:{ name : "王大锤", age : 250 } }); </script> </body> </html> 其中 input 的的变化可以影响到 age,age 的变化也可以影响到 input 3. 生命周期 【生命周期钩子函数】 beforeCreated:在 Vue 实例化之前调用,也可以将他理解为初始化函数。在 Vue1.0 时,这个函数的名字就是 init。 created:在创建实例之后进行调用。 beforeMount:页面加载完成,没有渲染。此时页面还是 {{name}} mounted:他的功能就是:在 dom 文档渲染完毕之后将要执行的函数,该函数在 Vue1.0 版本中名字为 compiled。可以将他理解为原生 js 中的 window.onload=function({.,.}) ,所以也可以理解为 jquery 中的 $(document).ready(function(){….})。此时页面中的 {{name}} 已被渲染 beforeDestroy:该函数将在销毁实例前进行调用 。 destroyed:改函数将在销毁实例后进行调用。 beforeUpdate:组件更新之前。 updated:组件更新之后。 4. this var app = new Vue({ el:"#app", data:{ num : 10 }, methods: { test() { let a = num; // 拿不到值 let b = this.num ++; // 可以拿到值 } } }); 四、模板语法 1. 插值表达式 {{ }} <div id="app"> <h2>姓名:{{name}},年龄:{{age}}</h2> </div> 2. v-model <div id="app"> <!-- 双向绑定 --> <input type="text" v-model="num"> <span>{{num}}</span> </div> <script> var app = new Vue({ el:"#app", data:{ num : 200 } }); </script> 3. v-test / v-html <div id="app"> <!-- 纯文本展示 --> <span v-text="num"></span> <!-- 解析为html --> <span v-html="num"></span> </div> <script> var app = new Vue({ el:"#app", data:{ num : "<h1>Hello Vue!</h1>" } }); </script> 4. v-on 语法 <div id="app"> {{num}} <br/> <!-- 绑定事件 --> <button v-on:click="add">add</button> <!-- 简写形式 --> <button @click="add">add</button> </div> <script> var app = new Vue({ el:"#app", data:{ num : 1 }, methods: { add() { this.num ++; } } }); </script> 事件修饰符 .prevent:阻止默认事件发生 <!-- 阻止默认链接跳转,使用 add 方法处理该事件,也可不处理 --> <a href="" @click.prevent="add">链接</a> 5. v-for 语法 <div id="app"> <lu> <!-- 遍历集合 --> <li v-for="item in list"> {{item}} </li> <hr/> <!-- 遍历集合,拿到索引 --> <li v-for="(item, index) in list"> {{index + 1}}---{{item}} </li> <hr/> <!-- 遍历对象,拿到 key,拿到索引 --> <li v-for="(item, key, index) in user"> {{index + 1}}---{{key}}---{{item}} </li> </lu> </div> <script> var app = new Vue({ el:"#app", data:{ list: ["唐三藏", "熏悟空", "猪八盖", "洒悟净", "小百龙"], user: {name: "王大锤", age: 23} } }); </script> 注意 我们一般会加 key 来提高效率,理想的 key 是每项都有且唯一的 <li v-for="(item, index) in lsit" :key="index"> {{index + 1}}---{{item}} </li> 6. v-if 语法 <div id="app"> <span v-if="flag">10086</span> <span v-if="!flag">10001</span> </div> <script> var app = new Vue({ el:"#app", data:{ flag: true } }); </script> 注意 v-if 和 v-for 在一起使用时,v-for 的优先级高于 v-if <div id="app"> <span v-for="item in list" v-if="item.age > 24">{{item}}</span> </div> <script> var app = new Vue({ el:"#app", data:{ list: [ {name: "001", age: 23}, {name: "002", age: 24}, {name: "003", age: 25} ] } }); </script> v-else-if、v-else 可以与 v-if 连用,但是之间不允许出现任何其他标签 <div id="app"> <!-- 中间间不允许出现任何其他标签 --> <!-- ########## --> <span v-if="num > 80">优秀</span> <span v-else-if="num > 60">及格</span> <span v-else>不合格</span> <!-- ########## --> </div> <script> var app = new Vue({ el:"#app", data:{ num: 61 } }); </script> 7. v-show 与 v-if 类似,但是 v-show 不满足条件时是将标签隐藏 8. v-bind 不使用 v-bind 时,变量会被认为时字符串,而不会解析为值 <!-- 动态绑定 img 地址 --> <div id="app"> <!-- 响应的更新属性 --> <img v-bind:src="url"> <!-- 简写形式 --> <img :src="url"> </div> <script> var app = new Vue({ el:"#app", data:{ url: "http://pic1.win4000.com/pic/0/4a/ff62f14ce3.jpg" } }); </script> --------------------------- <!-- 动态改变字体大小 --> <div id="app"> <!-- 此处要使用驼峰命名法 即:fontSize 而不能使用 font-size --> <span v-bind:style="{fontSize:size}">你好</span> <button @click="add">加大</button> </div> <script> var app = new Vue({ el:"#app", data:{ size: "10px", num: 10 }, methods: { add() { this.num += 5, this.size = this.num + "px" } } }); </script> 五、常用特性 1. 计算属性 computed 计算属性用法与方法相似,但是计算属性有缓存数据,只有在它的相关依赖发生改变时才会重新求值。而且计算属性一定要有返回值。 <div id="app"> {{add}} </div> <script> var app = new Vue({ el:"#app", computed: { add() { return 10 } } }); </script> 2. 监听器 watch <div id="app"> <input type="text" v-model="msg"> </div> <script> var app = new Vue({ el:"#app", data: { msg: "" }, watch: { <!-- 监听 msg --> msg(newValue, oldValue) { console.log("旧数据:" + oldValue + "\t" + "新数据:" + newValue) } } }); </script>

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册