Scoring Bug in FTS v1 and v2

Hello,
we are experiencing a bug in the FTS (v1 and v2). At least i think it is a bug and not a feature? :sweat_smile:

The matching score can differ even if the content is exactly the same. If the score is the same for each 3 rows on v2, just drop and re-create the table until it isn’t anymore. I can reproduce it on 8.9 and also 9.0 RC. Let me know if you need any more infos :slight_smile:

CREATE database production;

-- DROP TABLE example;

CREATE TABLE `example` (
  `id` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
  UNIQUE KEY `PRIMARY` (`id`) USING HASH,
  SHARD KEY `__SHARDKEY` (`id`),
  FULLTEXT USING VERSION 1 KEY `descr` (`description`)
);

INSERT INTO example(id, description)
VALUES 
(uuid(), 'Ihr habt es geschafft 🎉! Ihr seid die Gewinner, die Überlebenden von Staffel 4 - 7vs.Wild. Unter den härtesten Bedingungen, die wir jemals in einer Staffel hatten, habt ihr der Natur getrotzt. Unglaublich, wie ihr durchgezogen habt. Wir ziehen den Hut vor euch, eine unfassbar starke Leistung. Danke Julia, Joe, Joey und Uwe und herzlichen Glückwunsch 💚. Ihr seid zu krass. #7vswild'),
(uuid(), 'Ihr habt es geschafft 🎉! Ihr seid die Gewinner, die Überlebenden von Staffel 4 - 7vs.Wild. Unter den härtesten Bedingungen, die wir jemals in einer Staffel hatten, habt ihr der Natur getrotzt. Unglaublich, wie ihr durchgezogen habt. Wir ziehen den Hut vor euch, eine unfassbar starke Leistung. Danke Julia, Joe, Joey und Uwe und herzlichen Glückwunsch 💚. Ihr seid zu krass. #7vswild'),
(uuid(), 'Ihr habt es geschafft 🎉! Ihr seid die Gewinner, die Überlebenden von Staffel 4 - 7vs.Wild. Unter den härtesten Bedingungen, die wir jemals in einer Staffel hatten, habt ihr der Natur getrotzt. Unglaublich, wie ihr durchgezogen habt. Wir ziehen den Hut vor euch, eine unfassbar starke Leistung. Danke Julia, Joe, Joey und Uwe und herzlichen Glückwunsch 💚. Ihr seid zu krass. #7vswild')
;

OPTIMIZE TABLE example FULL;

SELECT
    *,
    MATCH(`description`) AGAINST ('7vswild OR #7vswild')
FROM example
;

---

CREATE TABLE `example_v2` (
  `id` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
  UNIQUE KEY `PRIMARY` (`id`) USING HASH,
  SHARD KEY `__SHARDKEY` (`id`),
  FULLTEXT USING VERSION 2 KEY `descr` (`description`)
);

INSERT INTO example_v2(id, description)
VALUES 
(uuid(), 'Ihr habt es geschafft 🎉! Ihr seid die Gewinner, die Überlebenden von Staffel 4 - 7vs.Wild. Unter den härtesten Bedingungen, die wir jemals in einer Staffel hatten, habt ihr der Natur getrotzt. Unglaublich, wie ihr durchgezogen habt. Wir ziehen den Hut vor euch, eine unfassbar starke Leistung. Danke Julia, Joe, Joey und Uwe und herzlichen Glückwunsch 💚. Ihr seid zu krass. #7vswild'),
(uuid(), 'Ihr habt es geschafft 🎉! Ihr seid die Gewinner, die Überlebenden von Staffel 4 - 7vs.Wild. Unter den härtesten Bedingungen, die wir jemals in einer Staffel hatten, habt ihr der Natur getrotzt. Unglaublich, wie ihr durchgezogen habt. Wir ziehen den Hut vor euch, eine unfassbar starke Leistung. Danke Julia, Joe, Joey und Uwe und herzlichen Glückwunsch 💚. Ihr seid zu krass. #7vswild'),
(uuid(), 'Ihr habt es geschafft 🎉! Ihr seid die Gewinner, die Überlebenden von Staffel 4 - 7vs.Wild. Unter den härtesten Bedingungen, die wir jemals in einer Staffel hatten, habt ihr der Natur getrotzt. Unglaublich, wie ihr durchgezogen habt. Wir ziehen den Hut vor euch, eine unfassbar starke Leistung. Danke Julia, Joe, Joey und Uwe und herzlichen Glückwunsch 💚. Ihr seid zu krass. #7vswild')
;

OPTIMIZE TABLE example_v2 FULL;

SELECT
    *,
    MATCH (TABLE example_v2) AGAINST ('description:7vswild|#7vswild')
FROM example_v2
;

The scores are sensitive to the way the data is laid out in segments and partitions. You are sharding on random id values and you probably have 8 or more partitions. There is a full-text index on each segment. So sometimes you might get 0, 1, 2, or 3 docs in the same segment. That could influence the scoring. For most apps, this doesn’t really matter. You still get meaningful output.

The BM25_GLOBAL function can be used to even this out to produce more regular scores that are less sensitive to how data is partitioned and segmented. But I don’t think even it is 100% insensitive to data layout. There still might be rounding error-related differences using BM25_GLOBAL.