使用PostgreSQL创建高级搜索引擎

news2024/11/24 22:23:29

​本文我们将探索PostgreSQL中的全文搜索功能,并研究我们能够复制多少典型搜索引擎功能。

如果您想跟随并尝试示例查询(我们建议这样做,这样更有趣),可以使用来自Kaggle的Wikipedia电影情节数据集执行代码示例。要导入它,请下载CSV文件,然后创建以下表格:

CREATE TABLE movies(  ReleaseYear int,  Title text,  Origin text,  Director text,  Casting text,  Genre text,  WikiPage text,  Plot text);

并像这样导入 CSV 文件:

\COPY movies(ReleaseYear, Title, Origin, Director, Casting, Genre, WikiPage, Plot)  FROM 'wiki_movie_plots_deduped.csv' DELIMITER ',' CSV HEADER;

该数据集包含 34,000 个电影标题,CSV 格式大小约为 81 MB。

PostgreSQL全文搜索原语 

PostgreSQL的全文搜索方法提供了一些基础组件,您可以将它们组合起来创建自己的搜索引擎。这种方法非常灵活,但也意味着与Elasticsearch、Typesense或Mellisearch等搜索引擎相比,它通常感觉更低级,因为全文搜索并非主要用例。

主要的基础组件,我们将通过示例进行介绍,包括:

  • tsvector和tsquery数据类型 

  • match运算符@@,用于检查tsquery是否与tsvector匹配 

  • 用于对每个匹配进行排名的函数(ts_rank、ts_rank_cd) 

  • GIN索引类型,用于高效查询tsvector的倒排索引 

我们将从这些基础组件开始,然后深入研究更高级的主题,包括相关性提升、容错处理和分面搜索。

tsvector 

tsvector数据类型存储了一个排序后的词元列表。词元是一个字符串,就像一个标记,但它已被规范化,以便生成不同形式的同一个词。例如,规范化通常包括将大写字母转换为小写字母,并经常涉及去除后缀(例如英语中的s或ing)。下面是一个示例,使用to_tsvector函数将一个英语短语解析为tsvector。

SELECT * FROM unnest(to_tsvector('english',  'I''m going to make him an offer he can''t refuse. Refusing is not an option.')); lexeme | positions | weights--------+-----------+--------- go     | {3}       | {D} m      | {2}       | {D} make   | {5}       | {D} offer  | {8}       | {D} option | {17}      | {D} refus  | {12,13}   | {D,D}(6 rows)

正如您所见,停用词(例如"I"、"to"或"an")被移除,因为它们在搜索中没有太大用处。这些词被规范化并缩减到它们的词根形式(例如"refuse"和"Refusing"都被转换为"refus")。标点符号被忽略。对于每个词,记录了它在原始短语中的位置(例如"refus"是文本中的第12和第13个词),以及权重(在后面我们将讨论它们在排名中的用途)。

在上面的示例中,词到词元的转换规则是基于英语搜索配置的。使用简单搜索配置运行相同的查询将导致包含所有单词的tsvector,这些单词与文本中找到的单词一致。

SELECT * FROM unnest(to_tsvector('simple',  'I''m going to make him an offer he can''t refuse. Refusing is not an option.'));  lexeme  | positions | weights----------+-----------+--------- an       | {7,16}    | {D,D} can      | {10}      | {D} going    | {3}       | {D} he       | {9}       | {D} him      | {6}       | {D} i        | {1}       | {D} is       | {14}      | {D} m        | {2}       | {D} make     | {5}       | {D} not      | {15}      | {D} offer    | {8}       | {D} option   | {17}      | {D} refuse   | {12}      | {D} refusing | {13}      | {D} t        | {11}      | {D} to       | {4}       | {D}(16 rows)

正如您所见,"refuse"和"refusing"现在生成了不同的词元。简单配置在包含标签或标记的列中非常有用。

PostgreSQL内置了一套相当不错的语言配置。您可以运行以下命令查看列表:

SELECT cfgname FROM pg_ts_config;

值得注意的是,目前没有适用于CJK(中日韩)语言的配置,如果您需要在这些语言中创建搜索查询,这一点值得记住。虽然简单配置在实践中对不支持的语言应该工作得很好,但我不确定对于CJK语言是否足够。

tsquery tsquery数据类型用于表示规范化的查询。tsquery包含搜索术语,这些术语必须是已经规范化的词元,并且可以使用AND、OR、NOT和FOLLOWED BY等运算符组合多个术语。有一些函数(如to_tsquery、plainto_tsquery和websearch_to_tsquery)可帮助将用户编写的文本转换为正确的tsquery,主要是通过对文本中出现的单词进行规范化。

为了对tsquery有所了解,让我们通过websearch_to_tsquery看几个示例:

SELECT websearch_to_tsquery('english', 'the dark vader'); websearch_to_tsquery----------------------'dark' & 'vader'

这是一个逻辑上的AND,意味着文档需要同时包含“quick”和“dog”才能匹配。您也可以进行逻辑上的OR操作:

SELECT websearch_to_tsquery('english', 'quick OR dog'); websearch_to_tsquery---------------------- 'dark' | 'vader'

您还可以排除某些单词:

SELECT websearch_to_tsquery('english', 'dark vader -wars');   websearch_to_tsquery--------------------------- 'dark' & 'vader' & !'war'

此外,您还可以表示短语搜索:

SELECT websearch_to_tsquery('english', '"the dark vader son"');     websearch_to_tsquery------------------------------ 'dark' <-> 'vader' <-> 'son'

这意味着:“dark”后面是“vader”,然后是“son”。

然而,请注意,“the”一词被忽略了,因为它是根据英文搜索配置的停用词。这可能会在像这样的短语中引发问题:

SELECT websearch_to_tsquery('english', '"do or do not, there is no try"'); websearch_to_tsquery---------------------- 'tri'(1 row)

糟糕,几乎整个短语都消失了。使用简单配置可以得到预期的结果:

SELECT websearch_to_tsquery('simple', '"do or do not, there is no try"');                           websearch_to_tsquery-------------------------------------------------------------------------- 'do' <-> 'or' <-> 'do' <-> 'not' <-> 'there' <-> 'is' <-> 'no' <-> 'try'

您可以使用匹配操作符@@来检查tsquery是否与tsvector匹配。

SELECT websearch_to_tsquery('english', 'dark vader') @@  to_tsvector('english',    'Dark Vader is my father.');?column?---------- t

虽然下面的例子不匹配:

SELECT websearch_to_tsquery('english', 'dark vader -father') @@  to_tsvector('english',    'Dark Vader is my father.');?column?---------- f

GIN 

既然我们已经看到了 tsvector 和 tsquery 的工作原理,现在让我们来看另一个关键构建块:GIN 索引类型是使其快速运行的关键。GIN 代表广义倒排索引(Generalized Inverted Index)。GIN 专门用于处理需要对复合值进行索引的情况,以及需要在索引中搜索出现在复合项内的元素值的查询。这意味着 GIN 不仅可以用于文本搜索,还可以用于 JSON 查询等其他用途。

您可以在一组列上创建 GIN 索引,或者您可以首先创建一个 tsvector 类型的列,以包括所有可搜索的列。例如:

ALTER TABLE movies ADD search tsvector GENERATED ALWAYS AS  (to_tsvector('english', Title) || ' ' ||   to_tsvector('english', Plot) || ' ' ||   to_tsvector('simple', Director) || ' ' ||   to_tsvector('simple', Genre) || ' ' ||   to_tsvector('simple', Origin) || ' ' ||   to_tsvector('simple', Casting)) STORED;

然后创建实际的索引:

CREATE INDEX idx_search ON movies USING GIN(search);

现在您可以执行如下简单的搜索测试:

SELECT title FROM movies WHERE search @@ websearch_to_tsquery('english','dark vader');                         title-------------------------------------------------- Star Wars Episode IV: A New Hope (aka Star Wars) Return of the Jedi Star Wars: Episode III – Revenge of the Sith(3 rows)

为了看到索引的效果,您可以比较上述查询的计时情况,包括有索引和无索引的情况。在我的计算机上,使用GIN索引的时间从200毫秒左右减少到约4毫秒。

ts_rank 

到目前为止,我们已经看到了如何使用ts_vector和ts_query来匹配搜索查询。然而,为了获得良好的搜索体验,重要的是首先显示最佳结果,这意味着结果需要按相关性进行排序。

直接从文档中摘录:

PostgreSQL提供了两个预定义的排名函数,它们考虑了词汇、接近度和结构信息;也就是说,它们考虑查询词在文档中出现的频率、词项在文档中的接近程度以及它们出现的文档部分的重要性。然而,"相关性"的概念是模糊的,并且非常应用程序特定。不同的应用可能需要额外的信息来进行排名,例如文档的修改时间。内置的排名函数只是示例。您可以编写自己的排名函数和/或将它们的结果与其他因素结合起来,以适应您的特定需求。

这两个提到的排名函数是ts_rank和ts_rank_cd。它们之间的区别在于,虽然它们都考虑了词项的频率,但ts_rank_cd还考虑了匹配词项之间的接近程度。

要在查询中使用它们,可以这样做:

SELECT title,       ts_rank(search, websearch_to_tsquery('english', 'dark vader')) rank  FROM movies  WHERE search @@ websearch_to_tsquery('english','dark vader')  ORDER BY rank DESC  LIMIT 10;  title                                            |    rank--------------------------------------------------+------------ Return of the Jedi                               | 0.21563873 Star Wars: Episode III – Revenge of the Sith     | 0.12592985 Star Wars Episode IV: A New Hope (aka Star Wars) | 0.05174401

关于ts_rank需要注意的一点是它需要访问每个结果的搜索列。这意味着如果WHERE条件匹配了很多行,PostgreSQL需要访问它们所有以进行排名,这可能会很慢。举个例子,上面的查询在我的计算机上返回时间为5-7毫秒。如果我修改查询以搜索dark OR vader,返回时间约为80毫秒,因为现在有1000多个匹配结果需要进行排名和排序。

相关性调整 

尽管基于词频的相关性对于搜索排序来说是一个很好的默认设置,但数据通常包含比简单的频率更重要的指标。

以下是一些电影数据集的示例:

  • 标题中的匹配应该比描述或剧情中的匹配更重要。 

  • 更受欢迎的电影可以根据评级和/或收到的投票数进行推广。 

  • 考虑到用户偏好,某些类别可以得到更大的提升。例如,如果某个用户喜欢喜剧片,那么这些电影可以优先考虑。 

  • 在对搜索结果进行排名时,较新的标题可以被认为比非常老的标题更相关。 

这就是为什么专用的搜索引擎通常提供使用不同的列或字段来影响排名的方法。这里是来自Elastic、Typesense和Meilisearch的调优示例指南。

数字、日期和精确值增强器

虽然PostgreSQL没有直接支持基于其他列进行提升的功能,但排名实际上只是一个排序表达式,因此您可以向其中添加自定义信号。

例如,如果您想根据投票数量添加提升,可以执行以下操作:

SELECT title,  ts_rank(search, websearch_to_tsquery('english', 'jedi'))    -- numeric booster example    + log(NumberOfVotes)*0.01 FROM movies WHERE search @@ websearch_to_tsquery('english','jedi') ORDER BY rank DESC LIMIT 10;

对数函数用于平滑影响,而0.01因子使得提升与排名得分具有可比性。

您还可以设计更复杂的增强器,例如,只有在排名有一定数量的投票时才提升评级。为此,您可以创建以下函数:

create function numericBooster(rating numeric, votes numeric, voteThreshold numeric)  returns numeric as $$    select case when votes < voteThreshold then 0 else rating end;$$ language sql;

然后可以这样使用它:

SELECT title,  ts_rank(search, websearch_to_tsquery('english', 'jedi'))    -- numeric booster example    + numericBooster(Rating, NumberOfVotes, 100)*0.005 FROM movies WHERE search @@ websearch_to_tsquery('english','jedi') ORDER BY rank DESC LIMIT 10;

让我们再举一个例子。假设我们想提高喜剧的排名。你可以创建一个类似下面的 valueBooster 函数:

create function valueBooster (col text, val text, factor integer)  returns integer as $$    select case when col = val then factor else 0 end;$$ language sql;

如果列的值与特定值匹配,则该函数返回一个因子;否则返回 0。可以像这样在查询中使用它:

SELECT title, genre,
   ts_rank(search, websearch_to_tsquery('english', 'jedi'))
   -- value booster example
   + valueBooster(Genre, 'comedy', 0.05) rank
FROM movies
   WHERE search @@ websearch_to_tsquery('english','jedi')                                                                                                 ORDER BY rank DESC LIMIT 10;
                      title                       |               genre                |        rank
--------------------------------------------------+------------------------------------+---------------------
 The Men Who Stare at Goats                       | comedy                             |  0.1107927106320858
 Clerks                                           | comedy                             |  0.1107927106320858
 Star Wars: The Clone Wars                        | animation                          | 0.09513916820287704
 Star Wars: Episode I – The Phantom Menace 3D     | sci-fi                             | 0.09471701085567474
 Star Wars: Episode I – The Phantom Menace        | space opera                        | 0.09471701085567474
 Star Wars: Episode II – Attack of the Clones     | science fiction                    | 0.09285612404346466
 Star Wars: Episode III – Revenge of the Sith     | science fiction, action            | 0.09285612404346466
 Star Wars: The Last Jedi                         | action, adventure, fantasy, sci-fi |  0.0889768898487091
 Return of the Jedi                               | science fiction                    | 0.07599088549613953
 Star Wars Episode IV: A New Hope (aka Star Wars) | science fiction                    | 0.07599088549613953
(10 rows)

列权重

记得我们谈到过 tsvector 词元可以附带权重吗?PostgreSQL 支持 4 种权重,它们分别是 A、B、C 和 D。A 是最高的权重,而 D 是最低的,默认权重。您可以通过 setweight 函数来控制权重,通常在构建 tsvector 列时调用该函数:

ALTER TABLE movies ADD search tsvector GENERATED ALWAYS AS   (setweight(to_tsvector('english', Title), 'A') || ' ' ||   to_tsvector('english', Plot) || ' ' ||   to_tsvector('simple', Director) || ' ' ||   to_tsvector('simple', Genre) || ' ' ||   to_tsvector('simple', Origin) || ' ' ||   to_tsvector('simple', Casting)) STORED;

让我们看看这个的效果。如果没有使用 setweight,搜索 dark vader OR jedi 的结果是:

SELECT title, ts_rank(search, websearch_to_tsquery('english', 'jedi')) rank   FROM movies   WHERE search @@ websearch_to_tsquery('english','jedi')   ORDER BY rank DESC;                      title                       |    rank--------------------------------------------------+------------- Star Wars: The Clone Wars                        |  0.09513917 Star Wars: Episode I – The Phantom Menace        |  0.09471701 Star Wars: Episode I – The Phantom Menace 3D     |  0.09471701 Star Wars: Episode III – Revenge of the Sith     | 0.092856124 Star Wars: Episode II – Attack of the Clones     | 0.092856124 Star Wars: The Last Jedi                         |  0.08897689 Return of the Jedi                               | 0.075990885 Star Wars Episode IV: A New Hope (aka Star Wars) | 0.075990885 Clerks                                           |  0.06079271 The Empire Strikes Back                          |  0.06079271 The Men Who Stare at Goats                       |  0.06079271 How to Deal                                      |  0.06079271(12 rows)

而使用标题列上的 setweight 后,结果为:

SELECT title, ts_rank(search, websearch_to_tsquery('english', 'jedi')) rank   FROM movies   WHERE search @@ websearch_to_tsquery('english','jedi')   ORDER BY rank DESC;                      title                       |    rank--------------------------------------------------+------------- Star Wars: The Last Jedi                         |   0.6361112 Return of the Jedi                               |   0.6231253 Star Wars: The Clone Wars                        |  0.09513917 Star Wars: Episode I – The Phantom Menace        |  0.09471701 Star Wars: Episode I – The Phantom Menace 3D     |  0.09471701 Star Wars: Episode III – Revenge of the Sith     | 0.092856124 Star Wars: Episode II – Attack of the Clones     | 0.092856124 Star Wars Episode IV: A New Hope (aka Star Wars) | 0.075990885 The Empire Strikes Back                          |  0.06079271 Clerks                                           |  0.06079271 The Men Who Stare at Goats                       |  0.06079271 How to Deal                                      |  0.06079271(12 rows)

容忍错别字/模糊搜索 

PostgreSQL 在使用 tsvector 和 tsquery 时不直接支持模糊搜索或容忍错别字。然而,基于以下假设,我们可以实现以下思路:

  • 在单独的表中索引内容中的所有词元

  • 对查询中的每个单词,使用相似度或Levenshtein距离在此表中进行搜索

  • 修改查询以包括找到的任何单词

  • 执行搜索

以下是其工作原理。首先,使用 ts_stats 获取所有单词并存储在一个物化视图中:

CREATE MATERLIAZED VIEW unique_lexeme AS   SELECT word FROM ts_stat('SELECT search FROM movies');

现在,对于查询中的每个单词,检查它是否在 unique_lexeme 视图中。如果不存在,则在该视图中进行模糊搜索,以找到可能的拼写错误:

SELECT * FROM unique_lexeme   WHERE levenshtein_less_equal(word, 'pregant', 2) < 2;    word---------- premant pregrant pregnant paegant

在上面的代码中,我们使用了Levenshtein距离,因为这是像Elasticsearch这样的搜索引擎在模糊搜索中使用的算法。

一旦你有了候选词列表,你需要调整查询以包含它们所有。

分面搜索 

分面搜索在电子商务网站上很受欢迎,特别是因为它帮助客户逐步缩小他们的搜索范围。以下是来自amazon.com的一个示例:

上述方法可以通过手动定义分类并将其作为搜索的 WHERE 条件添加来实现。另一种方法是根据现有数据以算法方式创建分类。例如,您可以使用以下代码创建一个“年代”分面:

SELECT ReleaseYear/10*10 decade, count(Title) cnt FROM movies  WHERE search @@ websearch_to_tsquery('english','star wars')  GROUP BY decade ORDER BY cnt DESC; decade | cnt--------+-----   2000 |  39   2010 |  31   1990 |  29   1950 |  28   1940 |  26   1980 |  22   1930 |  13   1960 |  11   1970 |   7   1910 |   3   1920 |   3(11 rows)

这还提供了每个年代的匹配计数,您可以在括号中显示出来。

如果您想在单个查询中获取多个分面,可以将它们组合起来,例如使用公共表表达式(CTEs):

WITH releaseYearFacets AS (  SELECT 'Decade' facet, (ReleaseYear/10*10)::text val, count(Title) cnt  FROM movies  WHERE search @@ websearch_to_tsquery('english','star wars')  GROUP BY val ORDER BY cnt DESC),genreFacets AS (  SELECT 'Genre' facet, Genre val, count(Title) cnt FROM movies  WHERE search @@ websearch_to_tsquery('english','star wars')  GROUP BY val ORDER BY cnt DESC LIMIT 5)SELECT * FROM releaseYearFacets UNION SELECT * FROM genreFacets;  facet  |   val   | cnt--------+---------+----- Decade | 1910    |   3 Decade | 1920    |   3 Decade | 1930    |  13 Decade | 1940    |  26 Decade | 1950    |  28 Decade | 1960    |  11 Decade | 1970    |   7 Decade | 1980    |  22 Decade | 1990    |  29 Decade | 2000    |  39 Decade | 2010    |  31 Genre  | comedy  |  21 Genre  | drama   |  35 Genre  | musical |   9 Genre  | unknown |  13 Genre  | war     |  15(16 rows)

上述方法在小到中等规模的数据集上应该能够很好地工作,但在非常大的数据集上可能会变得较慢。

结论

我们已经了解了PostgreSQL的全文搜索基础知识,以及如何将它们组合起来创建一个相当高级的全文搜索引擎,这个引擎还支持诸如连接和ACID事务等功能。换句话说,它具有其他搜索引擎通常没有的功能。

作者:Tudor Golubenco

更多技术干货请关注公号“云原生数据库

squids.cn,目前可体验全网zui低价云数据库RDS,免费的数据库迁移工具DBMotion、备份工具、SQL开发工具等

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

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

相关文章

C++day4 (拷贝构造函数、拷贝赋值函数、匿名对象、友元函数、常成员函数、常对象、运算符重载)

#include <iostream> #include <cstring> using namespace std;class mystring { private:char *str; //记录C风格字符串int size; //记录字符串的实际长度public://无参构造mystring():size(10){strnew char[size];//构造出一个长度为10的字符串strcpy(str,&…

第七章嵌套矢量中断控制器(Cortex-M7 Processor)

目录 第七章嵌套矢量中断控制器 7.1关于NVIC 7.2NVIC功能描述 7.2.1低功耗模式 7.2.2电平与脉冲中断 7.3NVIC程序员模型 7.3.1中断控制器类型寄存器 第七章嵌套矢量中断控制器 本章描述了嵌套矢量中断控制器(NVIC)。它包含以下部分: 关于NVIC在7-2页。NVIC功能描述见第7-…

【软考】系统架构设计风格分类的个人理解

个人适当学习了软考系统架构设计师中关于系统架构设计相关的内容&#xff0c;梳理了一下相关信息。 常见架构类型和常见分类 常见的软考中出现的系统架构列举如下&#xff1a; 分层架构管道-过滤器架构客户端-服务器架构模型-视图-控制器架构&#xff0c;即MVC架构事件驱动架…

Meta 最新发布 LLaMA 2(允许商业化)

文章目录 Llama 2 模型介绍Llama 2的核心点Llama 2的测评结果Llama 2的预训练预处理数据预训练设置和模型架构 Llama-2-chat 模型介绍Llama-2-chat 模型在帮助性和安全性上的表现Llama-2-chat 模型的训练过程 Llama 2 模型介绍 2023年7月18日&#xff0c;Meta 发布了Llama 2&a…

字节跳动后端面试,笔试部分

背景 笔者在刷B站的时候&#xff0c;看到了一个关于面试的实录&#xff0c;前半段是八股文&#xff0c;后半段是笔试部分&#xff0c;感觉笔试部分的题目还是挺有意思的&#xff0c;特此记录一下。 笔试部分 问题1&#xff1a;SQL 这题考的是 union all 的用法&#xff0c;在…

流程工业停机的实际成本

流程制造工厂面临着避免停机的巨大压力&#xff0c;因为这可能会严重影响企业的整体生产力、盈利能力和声誉。企业对计划外停机的原因和成本了解得越多&#xff0c;就能做更多的事情来帮助降低停机的发生率&#xff0c;并在停机发生时更好地做好应对准备。 图.石油炼化工厂&…

在Redis主从系统中使用哨兵

一、什么是哨兵 Redis的哨兵&#xff08;Sentinel&#xff09;是Redis分布式系统中的一种特殊角色&#xff0c;用于监控和管理Redis主从复制架构中的主节点&#xff08;master&#xff09;和从节点&#xff08;slave&#xff09;。 哨兵的主要功能是确保Redis系统的高可用性。它…

学校教室巡课,为何你总是出错?

教育是社会进步和个人发展的重要基石&#xff0c;而教师的教学质量和专业能力直接关系着教育的成效和学生的学习成果。为了促进教师的专业发展和提高教学质量&#xff0c;在线巡课系统应运而生。 通过在线巡课系统&#xff0c;巡课者可以远程观察教师的授课过程&#xff0c;并提…

Hugging Face开源库accelerate详解

官网&#xff1a;https://huggingface.co/docs/accelerate/package_reference/accelerator Accelerate使用步骤 初始化accelerate对象accelerator Accelerator()调用prepare方法对model、dataloader、optimizer、lr_schedluer进行预处理删除掉代码中关于gpu的操作&#xff0…

使用Jmeter做性能测试的注意点

目录 一、性能测试注意点 二、性能指标分析 一、性能测试注意点 1. 用jmeter测试时使用BeanShell脚本获取随机参数值&#xff0c;会导致请求时间过长&#xff0c;TPS过低。应改为使用csv读取参数值&#xff0c;记录的TPS会更加准确。 注&#xff1a;进行性能测试时&#xff0…

EMC学习笔记(十七)PCB设计中的安规考虑

PCB设计中的安规考虑 1 概述2.安全标识2.1 对安全标示通用准则2.2 电击和能量的危险2.3 PCB上的熔断器2.4 可更换电池 3.爬电距离和电气间隙4.涂覆印制板4.1 PCB板的机械强度4.2 印制电路板的阻燃等级4.3 热循环试验与热老化试验4.4 抗电强度试验4.5 耐划痕试验 5.布线和供电 1…

C# 属性

文章目录 实例属性静态属性只读属性&#xff1a;内部只读属性&#xff1a;动态计算值的属性方式一&#xff1a;主动计算方式二&#xff1a;被动计算 快速生成属性的方法&#xff1a;输入propfull&#xff0c;按两下tab键&#xff0c;然后再按tab键一次修改有底纹的字段&#xf…

LeetCode141.环形链表

141.环形链表 目录 141.环形链表一、哈希表二、双指针 一、哈希表 最容易想到的方法就是遍历所有节点&#xff0c;每次遍历到一个节点的时候&#xff0c;判断该节点此前是否被访问过 我们可以使用哈希表来存储所有已经访问过的节点 每次到达一个节点&#xff0c;如果该节点已…

Cyber Triage 3.7 (Windows) - 数字取证和事件响应

Cyber Triage 3.7 (Windows) - 数字取证和事件响应 请访问原文链接&#xff1a;https://sysin.org/blog/cybertriage-3&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 唯一专门用于事件响应的数字取证工具 快速、准确和简单地…

matlab使用教程(4)—数组类型

1多维数组 MATLAB 环境中的多维数组是具有多个下标的数组。创建多维数组的一种方法是调用具有多个参数的 zeros 、 ones 、 rand 或 randn 。例如&#xff0c; R randn(3,4,5); 创建一个 345 数组&#xff0c;共包含 3*4*5 60 个正态分布的随机元素。 三维数组可表示在矩…

数据库端口操作指南

数据库端口主要功能是允许用户通过从这些数据库端口推送或拉取数据&#xff0c;从而将各种数据库集成到用户的数据流中。 打开知行之桥 EDI 系统&#xff0c;在工作流界面右侧可以看到端口选项卡下有众多端口&#xff0c;我们打开数据库分类&#xff0c;可以看到知行之桥 EDI 系…

Linux文件处理命令

目录&#xff1a; linux系统与shell环境准备linux常用命令之文件处理Linux系统登录与文件操作 1.linux系统与shell环境准备 Linux 系统简介&#xff1a; Linux 内核最初只是由芬兰人林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;在赫尔辛基大学上学时出于个人爱好而…

电子病历编辑器源码

电子病历系统采取结构化与自由式录入的新模式&#xff0c;自由书写&#xff0c;轻松录入。化实现病人医疗记录&#xff08;包含有首页、病程记录、检查检验结果、医嘱、手术记录、护理记录等等。&#xff09;的保存、管理、传输和重现&#xff0c;取代手写纸张病历。不仅实现了…

如何使用Spring Boot实现分页和排序?

使用Spring Boot实现分页和排序需要借助Spring Data JPA。Spring Data JPA是Spring Data项目中的一个模块&#xff0c;提供了简化数据访问层的功能&#xff0c;包括分页和排序。 接下来我们通过一段Java代码&#xff0c;展示如何使用Spring Data JPA和Spring Boot实现分页和排…

电压放大器在超声波焊接中的作用以及应用

电压放大器是一种运用于电子设备中的信号放大器&#xff0c;主要作用是将小信号放大为更高幅度的信号。在超声波焊接中&#xff0c;电压放大器起到了重要的作用&#xff0c;它可以将从传感器采集到的微小信号放大为能够被检测和处理的合适大小的信号。 超声波焊接是现代工业生产…