# Relevance scoring in Manticore : part II

在关于相关性评分的第二部分中，我们讨论了如何利用位置信息进行匹配和评分。


了解字段中词语的位置信息非常重要，可以提供更好的相关性。位置信息允许使用一系列文本运算符，这些运算符可以通过词语在字段中的相对位置进行匹配，或者通过计算字段内找到的关键词之间的距离，或与输入查询进行比较。基于位置的最常用运算符之一是**短语**运算符 - `"A B"`。**短语匹配**具有限制性，因为它们强制要求关键词按照查询中指定的方式匹配。这意味着关键词必须相邻，并且顺序与查询中的一致。

知道词语的位置后，我们可以执行匹配以查找字段是否以特定词语开头或结尾，使用起始 (**^**) 和结束 (**$**) [运算符](https://docs.manticoresearch.com/latest/html/searching/extended_query_syntax.html)。还可以通过在字段名称运算符后指定限制，将字段内的搜索限制为仅当词语出现在字段的前N个词语中时才匹配。例如，可以使用 `@myfield [10] word`。我们还可以通过使用顺序运算符确保输入词语按特定顺序出现 - `word1 << word2 << word3`，或者如果输入词语在字段中以一定邻近度出现，可以使用**邻近**运算符：`"word1 word2"~10`。

**邻近**匹配允许在字段中匹配的关键词之间最多有**N**个词语。例如，`"A B C"~4` 匹配 `"A D E B F C"`，但不匹配 `"A D E B F G C"`。

对于更宽松的邻近度，可以使用 **NEAR** 运算符。与邻近运算符处理一组关键词不同，**NEAR** 处理两个操作数，这两个操作数可以是词语或其他子表达式。例如，可以执行 `"A B" NEAR/2 C` - 如果短语 `"A B"` 和 `C` 之间最多有 2 个词语，则匹配有效。类似地，还存在 **NOTNEAR** 运算符，它仅在操作数（词语或子表达式）之间有最小数量的词语时才匹配。

多种相关性度量使用词语位置来计算查询与文档之间的距离。**LCS**（最长公共子序列）给出文档与查询之间最大逐字匹配的长度。这意味着我们尝试查找输入查询的子序列是否存在于文档中。如果查询有完全匹配，**LCS** 的最大值是查询中的词语数量。子序列的 LCS 值取决于其长度，最低值为 1，即当词语被找到但不形成超过 1 个词语的子序列时。需要注意的是，子序列不需要由相邻匹配的词语组成。例如，如果输入为 `A B C`，而在文档中找到 `A D C`，该子序列将获得 2 分，因为 A 和 C 按顺序出现在查询中的相同位置：


```bash
mysql> SELECT *,WEIGHT() FROM testrt WHERE MATCH('hello world program') OPTION ranker=expr('top(lcs)');
+------+--------------------------+----------------------------+----------+
| id   | title                    | content                    | weight() |
+------+--------------------------+----------------------------+----------+
|    6 | hello world program      | just some content          |        3 |
|    4 | hello test program       | just some world content    |        2 |
|    5 | hello test world program | just some content          |        2 |
|    9 | hello world              | just program world content |        2 |
|    7 | hello test world         | just program some content  |        1 |
|    8 | test program hello       | just some world content    |        1 |
+------+--------------------------+----------------------------+----------+
6 rows in set (0.00 sec)

```

文档 4 在标题中的 LCS 值为 2，因为我们的查询是 `hello world program`，而子序列是 `hello test program` - 即使我们有一个不在匹配列表中的词语，`hello` 和 `program` 词语仍位于它们的位置（1 和 3），并且它们之间。

文档 7 中的 `hello test world` 虽然有 2 个词语（`hello` 和 `world`）按顺序出现，但它们的位置是 1 和 3，而在查询中它们的位置是 1 和 2。由于位置不匹配，我们不能说这两个匹配的词语形成了一个子序列。

**LCCS**（最长公共连续子序列）是 LCS 的一种更严格的版本。LCCS 仅对由相邻词语组成的子序列进行评分。为了说明差异，我们可以使用之前的例子，但现在使用 lccs：

```bash
mysql> select *,weight() from testrt where match('hello world program') option ranker=expr('top(lccs)');
+------+--------------------------+----------------------------+----------+
| id   | title                    | content                    | weight() |
+------+--------------------------+----------------------------+----------+
|    6 | hello  world program     | just some content          |        3 |
|    5 | hello test world program | just some content          |        2 |
|    9 | hello world              | just program world content |        2 |
|    4 | hello test program       | just some world content    |        1 |
|    7 | hello test world         | just program some content  |        1 |
|    8 | test program hello       | just some world content    |        1 |
+------+--------------------------+----------------------------+----------+
6 rows in set (0.00 sec)

```

现在，文档 4 的 LCS=2，但 LCCS=1，因为虽然它有一个子序列且词语位置相同，但这些词语并不相邻。
LCS 和 LCCS 在公式中将每个关键词计为 1，而不考虑词语的重要性（稀有或常见程度）。LCCS 的一种变体称为 **WLCCS** 或加权最长公共连续子序列，它可以将关键词的 **IDFs** 相加，而不是简单的计数。与 LCS 和 LCCS 不同，WLCCS 使用浮点值，因为 IDFs 以浮点数表示，输出取决于数据本身（因为 IDF 是相对于当前索引的相对度量）。WLCCS 会为包含稀有词语的子序列提供更好的值，并可以对使用 LCS 或 LCCS 否则评分较低的文档进行更高排名。

我们之前讨论过一个运算符，它仅在关键词开始字段时匹配。第一个出现的位置也可以用于相关性计算，因为这表明如果关键词在字段中更早出现，甚至在字段的开头，它对该字段的重要性更大。**min_hit_pos** 表示第一个匹配关键词的位置。在排名表达式中，我们可以添加一个检查 **min_hit_pos==1** 来检查第一个匹配的关键词是否出现在字段的开头。

最早的位置也可以应用于子序列。想象一下，我们有多个文档，它们提供了与查询（部分或全部）匹配的子序列，我们想知道哪些文档的子序列在字段中出现得更早。我们可以使用 **min_best_span_pos** 来实现这一点，它会给出字段中最佳子序列（按 LCS 计算）的首次出现位置。
