# Mike learning: REPLACE, UPDATE, wordforms

This article is for beginners at Manticore Search. It will guide you through the first steps of using the search engine: using wordforms

### 关于我

<div style="display: flex; align-items: flex-start; padding: 0; margin: 0;">
  <img src="./mike-replace-update-wordforms/mike.jpg" alt="Mike" style="max-width: 50%; max-height: 400px; margin: 0; padding: 0;">
  <div style="margin-left: 20px;">
    <p>大家好，我是Mike。</p>
    <p>我最近开始在Manticore担任开发者倡导者。我不是完全远离IT的人，但正在追赶现代技术。在这篇博客中，我将分享我的经验和关于Manticore的学习内容。我计划以日记的形式记录我的旅程，解释Manticore是什么以及如何使用它。让我们一起发现事物如何协同工作，识别问题，并实时与开发者互动。</p>
    <p>这是我的第二篇博客文章。如果您有兴趣与我一起学习Manticore，我会在以下地方更新：
    <ul>
      <li>
      <a href="https://twitter.com/manticoresearch">推特</a>
      </li>
      <li>
      电报： <a href="https://t.me/manticoresearch_en">EN</a> / <a href="https://t.me/manticore_chat">RU</a>
      </li>
      <li>
      <a href="https://slack.manticoresearch.com/">Slack</a>
      </li>
    </ul>
  </div>
</div>


### 第二步：REPLACE，UPDATE，词形
当我撰写[第一篇文章](/blog/mike-first-table/)时，来自宠物店的朋友联系了我。他们想将数据库用于他们的商店，而由于他们的销售员仍然对牵引绳和胸背带感到困惑，我想实现数据库的附加功能。

本文是[上一篇文章](/blog/mike-first-table/)的延续，并基于它。

与大多数数据库不同，Manticore使用先进的模型来识别其存储的文本。文本处理系统基于NLP（自然语言处理）解决方案。

在此处，对技术的一点描述有助于一般理解。NLP旨在识别我们交流的“自然”语言。乍看之下，文本识别似乎没有复杂之处，但随着使用机器学习算法的文本处理解决方案的出现，技术上已经变得如此。在我们的情况下，我们不会深入探讨，而是使用Manticore Search内置的现成文本处理解决方案。该系统使用分词（将文本分成小的独立部分）：按句子和单个单词进行分词，这使您能够快速在数据库中找到所需的单词、短语和段落。（有关数据分词的更多信息，请查看此[链接。](https://manual.manticoresearch.com/Creating_a_table/NLP_and_tokenization/Data_tokenization)）

关于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/` 文件夹中，并确保该文件在系统所有用户之间共享。
```bash
mkdir /tmp/wordforms
cd /tmp/wordforms
mcedit wf_pet_products.txt
```
*MC 内置的编辑器称为 `mcedit`。要执行它，请输入 `mcedit <文件名>`。*

让我们在其中添加我们的词形文件：
```bash
flexi > leash
harness > leash
```

如果你正确执行了所有步骤，你的终端中将显示如下内容：
![终端视图](./mike-replace-update-wordforms/QSG_7.png)
<kbd>F2</kbd> 保存
<kbd>Enter</kbd> 确认
<kbd>F10</kbd> 退出

现在我们有了一个将稍后与新表一起使用的词形文件。请记住或记下它保存的位置：
```bash
/tmp/wordforms/wf_pet_products.txt
```
现在，为了对数据库进行更多更改，我们必须连接到它并设置一个带有我们列的表。然后，我们需要链接一个词干提取器和我们之前创建的词形文件：

```bash
mysql -h0 -P9306
```

```sql
CREATE TABLE products (name STRING, info TEXT, price FLOAT, avl BOOL) morphology = 'stem_en' wordforms='/tmp/wordforms/wf_pet_products.txt';
```

现在让我们检查我们的表中创建了哪些字段：

```sql
DESC products;
```
![DESC 结果](./mike-replace-update-wordforms/QSG_7-1E.png)

我们指定的所有字段都存在，但出现了一个额外的字段 - `id`。此字段旨在让 Manticore 唯一标识数据库中的文档，因此在表初始化时会自动创建，无论是否指定。`info` 字段具有 `indexed stored` 属性，表明其参与全文搜索过程。需要注意的是，字段的顺序与创建表时指定的顺序不同。因此，在填充表时必须考虑这一点，尤其是在不指定字段顺序的情况下更新整个行时。例如，我们稍后将讨论的 `REPLACE` 命令。

接下来，我们应该验证表的一般参数，例如词形文件和之前连接的词干提取器。创建表时，如果词形文件名有错误，系统将忽略它，不会产生任何错误或警告。

```sql
SHOW TABLE products SETTINGS;
```
![显示表 products 设置结果](./mike-replace-update-wordforms/QSG_7-2E.png)


上面你可以注意到，尽管我们在 `/tmp/wordforms/` 中指定了文件路径，但 Manticore 将其保存在 `/var/lib/manticore/products/` 中。这表明 Manticore 已将文件的副本附加到创建的表中。现在该文件已固定用于索引此表。出于设计原因，无法实时替换词形文件，因为这需要重新索引整个表，如果你有一个非常大的表，这可能不是理想的。我们稍后将讨论如何安全且可预测地替换此类文件。

这完成了表的设置，下一步是输入所需的数据。数据输入与任何 SQL 数据库类似。

```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');
```
{{< notice "warning" >}}
注意撇号的使用；文本中有一些使用它们的缩写，重要的是用 `\` 将它们与其余文本隔离：`'Isn\'t it'`。Manticore **不支持双引号**用于字符串，这将有助于避免转义单撇号。
{{</notice>}}

在上面的第一个请求中，故意省略了 `info` 字段，以演示如何更新全文字段。需要注意的是，更新文本字段和属性字段的处理方式不同。全文字段使用 `REPLACE` 命令更新，这会触发新值的重新索引，而 `UPDATE` 命令足以处理其他字段。这是因为属性字段不参与全文搜索索引过程。

现在，让我们使用 `REPLACE` 命令向现有记录的某些字段添加数据。要使用此命令，你需要知道要更改或添加信息的行的唯一 ID。首先，我们将通过使用 `SELECT * FROM products` 获取必要的数据；

```sql
SELECT * FROM products WHERE name = 'Canvas leash, green, up to 50 kg, 5m';
```
![select * from products where name... 的结果](./mike-replace-update-wordforms/QSG_7-3E.png)

确保记住字段的位置。我们稍后需要这些信息。此外，我们需要字段中的所有当前数据，因为替换命令将用这些数据更新整个文件。如果我们不指定所有数据，未指定的字段将被重置。根据字段位置填写 `REPLACE` 命令。
```sql
REPLACE INTO products VALUES (<id>, <info>, <avl>, <price>, <name>);
```

{{< notice "info">}}
在开发版本中，您阅读本文时可能已经发布，可以替换特定字段。有关详细信息，请参阅[文档](https://manual.manticoresearch.com/Data_creation_and_modification/Updating_documents/REPLACE#SQL-REPLACE)。
{{</notice>}}

```sql
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');
```
{{< notice "warning">}}
请注意此处的代码，如果您直接从文章中复制命令，表中的ID字段值将不同！如果Manticore找不到指定ID的记录，将会创建一条新记录。
{{</notice>}}

要更新"attributes"字段，可以使用`UPDATE`命令。顺便提一下，在输入数据时，绿色项圈的价格略有下降：
```sql
update products set price = 4.6 where id = 8217224102746783745;
```

让我们检查结果：
```sql
SELECT * FROM products WHERE id = 8217224102746783745;
```
![查询 'where id' 结果的截图](./mike-replace-update-wordforms/QSG_8.png)

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

```sql
SELECT * FROM products WHERE match('harness');
SELECT * FROM products WHERE match ('leash');
```
![查询 'roulette' 的结果](./mike-replace-update-wordforms/QSG_9.png)

很好，现在数据库能够根据我们通过词形文件创建的连接来回答查询。但似乎输出中缺少了什么？粉色项圈用于室内警卫的条目在哪里？
在`info`字段中，没有像项圈或项圈这样的词，它们只出现在`name`字段中，因此该条目未包含在输出中。让我们修复这个问题：

```sql
SELECT * FROM products WHERE name = 'Pink harness, up to 10 kg';
```
![从 "pink harness" 查询的结果](./mike-replace-update-wordforms/QSG_10.png)

```sql
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');
```
让我们检查发生了什么：
```sql
SELECT * FROM products WHERE match('harness');
```
![更新表后 'harness' 查询的结果](./mike-replace-update-wordforms/QSG_11.png)
现在这条记录包含在输出中。从示例中可以看出，只有与索引字段相关的信息参与搜索；其余字段是索引字段的属性。

#### 扩展表

宠物店带来了新货，现在他们还有水族箱设备。为了确保我们能够轻松找到与水族箱相关的词汇，如泵或驱动器，我们需要向我们的词表中添加条目。

| 标题                             | 描述                                                       | 价格     | 可用性  |
|-------                            |-------------                                                      |-------    |-------------- |
| 水族箱的泵。        | 带内置过滤器的水族箱泵。容量150升/小时  | 32.00€    | 是           |
| 水族箱自动过滤器     | 带过滤器的可更换驱动器，容量100升/小时。                   | 28.00€    | 是           |
| 鱼网                          | 无痛水族箱鱼网                                      | 3.00€     | 是           |

让我们将它们添加到数据库中：
```sql
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);
```
{{< notice "info">}}
这里我们使用一个命令通过逗号分隔的新行列表进行填充，因此您可以使用一个命令添加大量文档。
{{</notice>}}

让我们检查搜索：
```sql
SELECT * FROM products WHERE match ('pump');
SELECT * FROM products WHERE match ('filter');
```
![查询 'pump' 和 'filter' 的结果](./mike-replace-update-wordforms/QSG_12.png)

好吧，这说明了某些问题。然而，驱动器不被视为泵。这应该添加到我们使用的词汇列表中…
让我们这么做。
哦，这里有一个小问题。这并不容易...
当我们为文本创建搜索系统时，我们会将所有单词放入一个表中，并将它们转换为标记以提高搜索效率。这些标记不会随后更新，以加快搜索过程。然而，有些情况下我们需要更新词形文件并相应地修改标记。让我们更新我们词形文件中的词汇列表，并将产品名称包含在`name`字段中到搜索索引中。

要更新表中的词形文件，我遵循了以下步骤：
1. 使用`mysqldump`创建此表的转储。
2. 更新词形文件。
3. 删除旧表。
4. 在形态学部分创建一个带有更新词形的新表。
5. 从转储文件中填充新表。

##### 创建转储文件（备份）：
在任何不确定的情况下，对表进行备份以防止数据丢失。（这条规则可以贴在墙上作为提醒）。
这也有助于我们实现目标。
要完成这项工作，我们需要断开SQL连接并使用`mysqldump`工具。
```sql
exit;
```
```bash
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时，这是正常的。

太好了，我们有了第一个转储！
![产品转储在文件夹中](./mike-replace-update-wordforms/QSG_13.png)

##### 更新词形文件：
您是否忘记了词形源文件的位置？

```bash
cd /tmp/wordforms
```

*如果您使用MC并且由于某些原因看不到带有文件的列，请尝试使用<kbd>Ctrl</kbd> + <kbd>O</kbd>组合键。如果没有任何变化，可能是MC没有运行。*

![词形位置](./mike-replace-update-wordforms/QSG_14.png)
接下来，使用箭头键进行导航。当需要选择文件时，按 <kbd>F4</kbd> 打开所选文件。
添加新字符串："drive > pump"
![带有添加字符串的词形文件](./mike-replace-update-wordforms/QSG_15.png)
保存并关闭。 <kbd>F2</kbd>, <kbd>Enter</kbd>, <kbd>F10</kbd>。
文件已更新。

##### 删除表并创建新表：

在删除表之前，请确保您已备份数据，并且备份中确实包含我们的数据。

```bash
cd /tmp/manticore_dumps/
```
选择备份文件并按 <kbd>F3</kbd> 按钮查看它。
您也可以在控制台中使用命令 `cat /tmp/manticore_dumps/products.sql` 实现相同操作。

您应该看到类似以下内容：
![备份视图](./mike-replace-update-wordforms/QSG_16.png)

好的，让我们继续删除旧表并创建新表：
```bash
mysql -h0 -P9306
```
```sql
DROP TABLE products;
CREATE TABLE products (name TEXT, info TEXT, price FLOAT, avl BOOL) morphology = 'stem_en' wordforms='/tmp/wordforms/wf_pet_products.txt';
```
因此，我们删除了这张表，然后立即重新创建（使用与之前相同的命令，但有一个小改动：现在名称字段也用于文本），现在我们需要向其中添加信息。
```sql
exit;
```

##### 从备份重新填充数据：

```bash
mysql -h0 -P9306 < /tmp/manticore_dumps/products.sql
mysql -h0 -P9306
```
我们已经填充了数据。让我们检查最终结果：
```sql
SELECT * FROM products WHERE match ('pump');
```
很好，全部正常工作！
![更新词形后的结果 "select * ... mathc('pump')"](./mike-replace-update-wordforms/QSG_17.png)

{{< notice "info">}}
在更新表时，尤其是大型表，存在一个时间段，旧表已被删除而新表尚未创建。在此期间，系统可能会以错误响应请求。为了确保 Manticore 的平稳运行，有几种系统可以帮助避免丢失用户请求。我之后会学习如何实现这些方法。
{{</notice>}}

在本文中，我展示了如何为宠物店的库存设置和使用 Manticore Search。通过词形和词形学，Manticore 通过将相关产品名称和类型链接起来，帮助改进搜索结果。我介绍了如何添加新项目、更新现有项目，并在重大更新（如更改词形文件）期间确保数据一致性。这有助于初学者理解和有效应用 Manticore Search 的功能，使数据搜索更加高效。未来的文章将探索更多功能，让我们继续一起实验 Manticore 以增强您的项目。

今天就到这里。Mike，签收。

![结束照片](./mike-replace-update-wordforms/QSG_18.JPG)
