关于我

第二步:REPLACE,UPDATE,词形
当我撰写 第一篇文章 时,来自宠物店的朋友联系了我。他们想将数据库用于他们的商店,而由于他们的销售员仍然对牵引绳和胸背带感到困惑,我想实现数据库的附加功能。
本文是 上一篇文章 的延续,并基于它。
与大多数数据库不同,Manticore使用先进的模型来识别其存储的文本。文本处理系统基于NLP(自然语言处理)解决方案。
在此处,对技术的一点描述有助于一般理解。NLP旨在识别我们交流的“自然”语言。乍看之下,文本识别似乎没有复杂之处,但随着使用机器学习算法的文本处理解决方案的出现,技术上已经变得如此。在我们的情况下,我们不会深入探讨,而是使用Manticore Search内置的现成文本处理解决方案。该系统使用分词(将文本分成小的独立部分):按句子和单个单词进行分词,这使您能够快速在数据库中找到所需的单词、短语和段落。(有关数据分词的更多信息,请查看此 链接。 )
关于Manticore利用的技术,有几点说明:
- 词干提取是将单词减少到其词根形式的过程。例如,“walking”、“walks”和“walked”都是单词“walk”的词干。
- 词形还原是将单词的不同形式还原为其基本形式(称为词形)的过程。例如,单词“eat”可以以“eating”、“eats”和“ate”的形式出现。所有这些变体的词形都是“eat”,即其基本词典形式。
- 为了提高高级搜索的准确性和质量,还有其他几种解决方案参与其中:词形、例外词和停用词。
词形还原器和词干提取器执行一个共同的功能,即以不同的方式将单词归一化为相同的形式,每种方式都有其优缺点。
此外,包含常用词列表的停用词文件,如冠词、连词和感叹词,有助于加快处理速度。基本上,所有这些小词使我们的语言听起来不错,但对计算机来说意义不大。
如果标准功能集不足以舒适地操作搜索引擎,例如当数据库包含专业术语或地方俚语,并且词语之间存在具有附加语义意义的逻辑联系时,可以使用附加词形文件。数据库管理员可以在文件中添加词语之间的链接,这些词语在定义规则上有所不同,但在上下文中相似。例如,牵引绳和胸背带。在不同的上下文中,这两个词可能具有相同的意义或完全不同。
在表中使用词形文件
新表
宠物店的朋友们提到他们的销售员对牵引绳和胸背带了解不多。他们建议在查询牵引绳时,也应考虑询问胸背带,反之亦然。
由于牵引绳和胸背带属于同一产品类别,而数据库中没有特定字段来指示它们的组别,因此使用词形文件会很有帮助。该文件可以通过在搜索“牵引绳”时添加相关词汇来帮助。例如,通过将“胸背带”或“flexi”添加到词形字典中,搜索“牵引绳”时也会显示“胸背带”和“flexi”的结果。
让我们看一下这家宠物店提供的产品:
| 标题 | 描述 | 价格 | 可用性 |
|---|---|---|---|
| 网眼牵引绳,绿色,最多50公斤,5米 | 坚固的网眼狗牵引绳,适合大型犬品种 | 5.00€ | 是 |
| 弹性牵引绳,粉色,最多10公斤,3米 | 为优雅女士和她们的四足伴侣准备的美丽牵引绳 | 12.00€ | 否 |
| 粉色胸背带,最多10公斤 | 用于被释放到街道上并被错误称为狗的看门犬 | 8.00€ | 是 |
| The flexi可伸缩狗牵引绳,10公斤,5米 | 适用于最多10公斤的狗的flexi。长度为5米,考虑到主人拉出手臂的长度 | 7.50€ | 是 |
| 狗粮,1公斤 | 你宠物的干粮 | 4.30€ | 否 |
| 猫粮,1公斤 | 如果你的猫大声叫嚷并要求食物! | 2.80€ | 是 |
| 猫用驱蚤项圈 | 猫不应该携带跳蚤。 | 23.20€ | 是 |
| 狗用驱蚤滴剂,最多10公斤 | 从不速之客身上滴在你的守护者皮肤上的药剂 | 14.30€ | 是 |
我们将使 标题 字段仅作为字符串,使用 描述 进行全文搜索,将 价格 设为浮点数,将 可用性 设为布尔值。
为了在 "牵引绳"、"胸背带" 和 "flexi" 之间建立逻辑词关联,我们将它们放在 /tmp/wordforms/ 文件夹中,并确保该文件在系统所有用户之间共享。
mkdir /tmp/wordforms
cd /tmp/wordforms
mcedit wf_pet_products.txt
MC 内置的编辑器称为 mcedit。要执行它,请输入 mcedit <文件名>。
让我们在其中添加我们的词形文件:
flexi > leash
harness > leash
如果你正确执行了所有步骤,你的终端中将显示如下内容:
F2 保存
Enter 确认
F10 退出
现在我们有了一个将稍后与新表一起使用的词形文件。请记住或记下它保存的位置:
/tmp/wordforms/wf_pet_products.txt
现在,为了对数据库进行更多更改,我们必须连接到它并设置一个带有我们列的表。然后,我们需要链接一个词干提取器和我们之前创建的词形文件:
mysql -h0 -P9306
CREATE TABLE products (name STRING, info TEXT, price FLOAT, avl BOOL) morphology = 'stem_en' wordforms='/tmp/wordforms/wf_pet_products.txt';
现在让我们检查我们的表中创建了哪些字段:
DESC products;

我们指定的所有字段都存在,但出现了一个额外的字段 - id。此字段旨在让 Manticore 唯一标识数据库中的文档,因此在表初始化时会自动创建,无论是否指定。info 字段具有 indexed stored 属性,表明其参与全文搜索过程。需要注意的是,字段的顺序与创建表时指定的顺序不同。因此,在填充表时必须考虑这一点,尤其是在不指定字段顺序的情况下更新整个行时。例如,我们稍后将讨论的 REPLACE 命令。
接下来,我们应该验证表的一般参数,例如词形文件和之前连接的词干提取器。创建表时,如果词形文件名有错误,系统将忽略它,不会产生任何错误或警告。
SHOW TABLE products SETTINGS;

上面你可以注意到,尽管我们在 /tmp/wordforms/ 中指定了文件路径,但 Manticore 将其保存在 /var/lib/manticore/products/ 中。这表明 Manticore 已将文件的副本附加到创建的表中。现在该文件已固定用于索引此表。出于设计原因,无法实时替换词形文件,因为这需要重新索引整个表,如果你有一个非常大的表,这可能不是理想的。我们稍后将讨论如何安全且可预测地替换此类文件。
这完成了表的设置,下一步是输入所需的数据。数据输入与任何 SQL 数据库类似。
INSERT INTO products (name, price, avl) VALUES ('Canvas leash, green, up to 50 kg, 5m', 5.0, 1);
INSERT INTO products (name, price, avl, info) VALUES ('Elastic leash, pink, up to 10 kg, 3m', 12.00, 0, 'A beautiful leash for sophisticated ladies and their four-legged companions');
INSERT INTO products (name, price, avl, info) VALUES ('Pink harness, up to 10 kg', 8.00, 1, 'For room guards released into the street and mistakenly called a dog');
INSERT INTO products (name, price, avl, info) VALUES ('The flexi retractable dog leash, 10 kg, 5m', 7.50, 1, 'A flexi for dogs up to 10 kg. The length is 5 meters, taking into account the length of the owner\'s pulled out arm');
INSERT INTO products (name, price, avl, info) VALUES ('Dog food, 1kg', 4.30, 0, 'Dry food for your pet');
INSERT INTO products (name, price, avl, info) VALUES ('Cat food, 1kg', 2.80, 1, 'If your cat is yelling loudly and demanding food!');
INSERT INTO products (name, price, avl, info) VALUES ('Flea collar for cats , 1kg', 23.20, 1, 'Cats shouldn\'t be flea carriers.');
INSERT INTO products (name, price, avl, info) VALUES ('Flea drops for dogs up to 10 kg', 14.30, 1, 'Drops from uninvited passengers on the skin of your defender');
注意撇号的使用;文本中有一些使用它们的缩写,重要的是用 \ 将它们与其余文本隔离:'Isn\'t it'。Manticore 不支持双引号用于字符串,这将有助于避免转义单撇号。
在上面的第一个请求中,故意省略了 info 字段,以演示如何更新全文字段。需要注意的是,更新文本字段和属性字段的处理方式不同。全文字段使用 REPLACE 命令更新,这会触发新值的重新索引,而 UPDATE 命令足以处理其他字段。这是因为属性字段不参与全文搜索索引过程。
现在,让我们使用 REPLACE 命令向现有记录的某些字段添加数据。要使用此命令,你需要知道要更改或添加信息的行的唯一 ID。首先,我们将通过使用 SELECT * FROM products 获取必要的数据;
SELECT * FROM products WHERE name = 'Canvas leash, green, up to 50 kg, 5m';

确保记住字段的位置。我们稍后需要这些信息。此外,我们需要字段中的所有当前数据,因为替换命令将用这些数据更新整个文件。如果我们不指定所有数据,未指定的字段将被重置。根据字段位置填写 REPLACE 命令。
REPLACE INTO products VALUES (<id>, <info>, <avl>, <price>, <name>);
在开发版本中,您阅读本文时可能已经发布,可以替换特定字段。有关详细信息,请参阅 文档 。
REPLACE INTO products VALUES (8217224102746783745, 'Sturdy canvas dog leash for 5 meters, suitable for large dog breeds', 1, 5.0, 'Canvas leash, green, up to 50 kg, 5m');
请注意此处的代码,如果您直接从文章中复制命令,表中的ID字段值将不同!如果Manticore找不到指定ID的记录,将会创建一条新记录。
要更新"attributes"字段,可以使用UPDATE命令。顺便提一下,在输入数据时,绿色项圈的价格略有下降:
update products set price = 4.6 where id = 8217224102746783745;
让我们检查结果:
SELECT * FROM products WHERE id = 8217224102746783745;

我们已经达到了预期结果,现在让我们尝试搜索,因为我们正在为商店创建产品数据库,以便卖家更容易找到产品。用于在表中搜索的命令是select * from <table> where match('<query>')。
SELECT * FROM products WHERE match('harness');
SELECT * FROM products WHERE match ('leash');

很好,现在数据库能够根据我们通过词形文件创建的连接来回答查询。但似乎输出中缺少了什么?粉色项圈用于室内警卫的条目在哪里?
在info字段中,没有像项圈或项圈这样的词,它们只出现在name字段中,因此该条目未包含在输出中。让我们修复这个问题:
SELECT * FROM products WHERE name = 'Pink harness, up to 10 kg';

REPLACE INTO products VALUES (8217224102746783747,'Harness for room guards released into the street and mistakenly called a dog', 1, 8.0, 'Pink harness, up to 10 kg');
让我们检查发生了什么:
SELECT * FROM products WHERE match('harness');

现在这条记录包含在输出中。从示例中可以看出,只有与索引字段相关的信息参与搜索;其余字段是索引字段的属性。
扩展表
宠物店带来了新货,现在他们还有水族箱设备。为了确保我们能够轻松找到与水族箱相关的词汇,如泵或驱动器,我们需要向我们的词表中添加条目。
| 标题 | 描述 | 价格 | 可用性 |
|---|---|---|---|
| 水族箱的泵。 | 带内置过滤器的水族箱泵。容量150升/小时 | 32.00€ | 是 |
| 水族箱自动过滤器 | 带过滤器的可更换驱动器,容量100升/小时。 | 28.00€ | 是 |
| 鱼网 | 无痛水族箱鱼网 | 3.00€ | 是 |
让我们将它们添加到数据库中:
INSERT INTO products (name, info, price, avl) VALUES ('The pump for the aquarium.', 'Pump with built-in aquarium filter. Capacity 150 l/h', 32, 1), ('Automatic filter for aquarium', 'Disposable drive with filter, capacity 100 l/h', 28.00, 1), ('Fish net', 'Atraumatic aquarium fish net', 3, 1);
这里我们使用一个命令通过逗号分隔的新行列表进行填充,因此您可以使用一个命令添加大量文档。
让我们检查搜索:
SELECT * FROM products WHERE match ('pump');
SELECT * FROM products WHERE match ('filter');

好吧,这说明了某些问题。然而,驱动器不被视为泵。这应该添加到我们使用的词汇列表中…
让我们这么做。
哦,这里有一个小问题。这并不容易...
当我们为文本创建搜索系统时,我们会将所有单词放入一个表中,并将它们转换为标记以提高搜索效率。这些标记不会随后更新,以加快搜索过程。然而,有些情况下我们需要更新词形文件并相应地修改标记。让我们更新我们词形文件中的词汇列表,并将产品名称包含在name字段中到搜索索引中。
要更新表中的词形文件,我遵循了以下步骤:
- 使用
mysqldump创建此表的转储。 - 更新词形文件。
- 删除旧表。
- 在形态学部分创建一个带有更新词形的新表。
- 从转储文件中填充新表。
创建转储文件(备份):
在任何不确定的情况下,对表进行备份以防止数据丢失。(这条规则可以贴在墙上作为提醒)。
这也有助于我们实现目标。
要完成这项工作,我们需要断开SQL连接并使用mysqldump工具。
exit;
mkdir /tmp/manticore_dumps
cd /tmp/manticore_dumps
mysqldump -h0 -P9306 -tc --compact manticore products > products.sql
我使用的标志是:
-t- 从转储中排除create table语句。-c- 在INSERT命令中指定列名。--compact- 以紧凑格式执行数据库转储,省略检查表可用性、删除它们、创建新表、为创建的表指定配置以及其他部署组织操作的程序。在我们的情况下,这些程序是不必要的,因为我们需要在表中注册一个新的字典文件。manticore- 是mysqldump的数据库名称,使用mysqldump与Manticore时,必须始终是manticore。products- 是我们创建转储的表的名称。products.sql- 是转储将上传到的文件的名称。放置文件的目录默认为从mysqldump启动的目录。我建议指定/tmp/以确保示例后续成功运行。或者,您可以输入完整路径:/tmp/manticore_dumps/products.sql。
如果您看到:"-- Warning: column statistics not supported by the server." 不用担心,当您使用此mysql工具与Manticore时,这是正常的。
太好了,我们有了第一个转储!
更新词形文件:
您是否忘记了词形源文件的位置?
cd /tmp/wordforms
如果您使用MC并且由于某些原因看不到带有文件的列,请尝试使用Ctrl + O组合键。如果没有任何变化,可能是MC没有运行。

接下来,使用箭头键进行导航。当需要选择文件时,按 F4 打开所选文件。
添加新字符串:"drive > pump"
保存并关闭。 F2, Enter, F10。
文件已更新。
删除表并创建新表:
在删除表之前,请确保您已备份数据,并且备份中确实包含我们的数据。
cd /tmp/manticore_dumps/
选择备份文件并按 F3 按钮查看它。
您也可以在控制台中使用命令 cat /tmp/manticore_dumps/products.sql 实现相同操作。
您应该看到类似以下内容:
好的,让我们继续删除旧表并创建新表:
mysql -h0 -P9306
DROP TABLE products;
CREATE TABLE products (name TEXT, info TEXT, price FLOAT, avl BOOL) morphology = 'stem_en' wordforms='/tmp/wordforms/wf_pet_products.txt';
因此,我们删除了这张表,然后立即重新创建(使用与之前相同的命令,但有一个小改动:现在名称字段也用于文本),现在我们需要向其中添加信息。
exit;
从备份重新填充数据:
mysql -h0 -P9306 < /tmp/manticore_dumps/products.sql
mysql -h0 -P9306
我们已经填充了数据。让我们检查最终结果:
SELECT * FROM products WHERE match ('pump');
很好,全部正常工作!
在更新表时,尤其是大型表,存在一个时间段,旧表已被删除而新表尚未创建。在此期间,系统可能会以错误响应请求。为了确保 Manticore 的平稳运行,有几种系统可以帮助避免丢失用户请求。我之后会学习如何实现这些方法。
在本文中,我展示了如何为宠物店的库存设置和使用 Manticore Search。通过词形和词形学,Manticore 通过将相关产品名称和类型链接起来,帮助改进搜索结果。我介绍了如何添加新项目、更新现有项目,并在重大更新(如更改词形文件)期间确保数据一致性。这有助于初学者理解和有效应用 Manticore Search 的功能,使数据搜索更加高效。未来的文章将探索更多功能,让我们继续一起实验 Manticore 以增强您的项目。
今天就到这里。Mike,签收。

