在关于相关性评分的第二部分中,我们讨论了如何使用位置进行匹配和评分。
了解字段中单词的位置很重要,可以提供更好的相关性。位置允许使用广泛的文本操作符,这些操作符可以通过单词相对于字段的位置或通过计算在字段内找到的关键字与输入查询之间的距离来执行匹配。基于位置的最常用操作符之一是 phrase 操作符 - "A B"。短语匹配是限制性的,因为它强制关键字按照查询中指定的方式进行匹配。这意味着关键字必须相邻并且顺序与查询中的相同。
知道单词的位置后,我们可以执行匹配,以查找是否有字段以特定单词开头或结尾,使用开始 (^) 和结束($)
操作符
。在字段内的搜索也可以限制为仅在字段的前 N 个单词中找到单词时提供匹配。这可以通过在字段名称操作符后指定限制来实现 - @myfield [10] word。我们还可以确保我们的输入单词以特定顺序出现,使用顺序操作符 - word1 << word2 << word3,或者如果输入单词在字段中以给定的接近度出现,使用 proximity 操作符:"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 按照查询中的顺序和位置被找到:
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。由于位置不匹配,我们不能谈论 2 个匹配单词形成子序列。
LCS 的一个更限制版本是 LCCS(最长公共连续子序列)。LCCS 仅对由相邻单词形成的子序列进行评分。为了说明差异,让我们以之前的例子为例,但现在使用 lccs:
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,而不考虑单词的重要性(稀有或常见)。一种名为 WLCCS 的 LCCS 变体可以将关键字的 IDFs 相加,而不仅仅是简单计数。与 LCS 和 LCCS 不同,WLCCS 采用浮动值,因为 IDFs 表示为浮动,输出取决于数据本身(因为 IDF 是相对于当前索引的度量)。WLCCS 将为包含稀有单词的子序列提供更好的值,并且可以更高地排名一个文档,而使用 lcs 或 lccs 则会得分较低。
我们之前谈到过一个操作符,它只能在关键字开始字段时进行匹配。第一个出现的位置也可以用于相关性计算,因为这表明关键字在字段中出现得越早,对该字段的重要性越大,甚至更好的是,在字段的开头。min_hit_pos 告诉第一个匹配关键字的位置。在排名表达式中,我们可以添加一个检查 min_hit_pos==1,以检查第一个匹配的关键字是否出现在字段的开头。
最早的位置也可以应用于子序列。想象一下,我们有多个文档,这些文档给我们提供与查询匹配的子序列(部分或全部),我们想知道哪些文档的子序列在字段中出现得更早。我们可以使用 min_best_span_pos 来实现,这给我们提供字段中最佳子序列(计算为 LCS)的第一次出现。