FTS 对于带点的电子邮件无法按预期工作

kse*_*een 9 sql sql-server indexing full-text-search full-text-indexing

我们正在开发搜索作为更大系统的一部分。

我们有Microsoft SQL Server 2014 - 12.0.2000.8 (X64) Standard Edition (64-bit)这个设置:

CREATE TABLE NewCompanies(
    [Id] [uniqueidentifier] NOT NULL,
    [Name] [nvarchar](400) NOT NULL,
    [Phone] [nvarchar](max) NULL,
    [Email] [nvarchar](max) NULL,
    [Contacts1] [nvarchar](max) NULL,
    [Contacts2] [nvarchar](max) NULL,
    [Contacts3] [nvarchar](max) NULL,
    [Contacts4] [nvarchar](max) NULL,
    [Address] [nvarchar](max) NULL,
    CONSTRAINT PK_Id PRIMARY KEY (Id)
);
Run Code Online (Sandbox Code Playgroud)
  1. Phone 是一个结构化的逗号分隔的数字字符串,如 "77777777777, 88888888888"
  2. Email是带逗号的结构化电子邮件字符串 "email1@gmail.com, email2@gmail.com"(或根本不带逗号 "email1@gmail.com"
  3. Contacts1, Contacts2, Contacts3, Contacts4是文本字段,用户可以在其中以自由形式指定联系方式。喜欢"John Smith +1 202 555 0156""Bob, +1-999-888-0156, bob@company.com"。这些字段可以包含我们想要进一步搜索的电子邮件和电话。

在这里我们创建全文内容

-- FULL TEXT SEARCH
CREATE FULLTEXT CATALOG NewCompanySearch AS DEFAULT;  
CREATE FULLTEXT INDEX ON NewCompanies(Name, Phone, Email, Contacts1, Contacts2, Contacts3, Contacts4, Address)
KEY INDEX PK_Id
Run Code Online (Sandbox Code Playgroud)

这是一个数据样本

INSERT INTO NewCompanies(Id, Name, Phone, Email, Contacts1, Contacts2, Contacts3, Contacts4) 
VALUES ('7BA05F18-1337-4AFB-80D9-00001A777E4F', 'PJSC Azimuth', '79001002030, 78005005044', 'regular@hotmail.com, s.m.s@gmail.com', 'John Smith', 'Call only at weekends +7-999-666-22-11', NULL, NULL)
Run Code Online (Sandbox Code Playgroud)

实际上,我们有大约 10 万条这样的记录。

我们希望用户可以指定电子邮件的一部分,例如“@gmail.com”,这应该返回任何Email, Contacts1, Contacts2, Contacts3, Contacts4字段中包含 Gmail 电子邮件地址的所有行。

电话号码也是一样。用户可以搜索像“70283”这样的模式,并且查询应该返回包含这些数字的电话。甚至对于自由格式Contacts1, Contacts2, Contacts3, Contacts4字段,我们可能应该在搜索之前首先删除除数字和空格字符之外的所有字符。

我们以前使用LIKE的时候,我们有大约1500年记录的搜索和它工作得很好,但现在我们有很多的记录和LIKE搜索需要无限的才能得到结果。

这是我们尝试从那里获取数据的方式:

SELECT * FROM NewCompanies WHERE CONTAINS((Email, Contacts1, Contacts2, Contacts3, Contacts4), '"s.m.s@gmail.com*"') -- this doesn't get the row
SELECT * FROM NewCompanies WHERE CONTAINS((Phone, Contacts1, Contacts2, Contacts3, Contacts4), '"6662211*"') -- doesn't get anything
SELECT * FROM NewCompanies WHERE CONTAINS(Name, '"zimuth*"') -- doesn't get anything
Run Code Online (Sandbox Code Playgroud)

Mat*_*ker 1

在这种情况下,全文搜索不太理想。我和你在同一条船上。点赞搜索太慢,全文搜索搜索以术语开头而不是包含术语的单词。

我们尝试了多种解决方案,其中一种纯 SQL 选项是构建您自己的全文搜索版本,特别是倒排索引搜索。我们尝试了这个,很成功,但是占用了很多空间。我们为部分搜索词创建了一个辅助保存表,并对其使用了全文索引。然而,这意味着我们重复存储同一事物的多个副本。例如,我们将“longword”存储为 Longword、ongword、ngword、gword... 等。因此,任何包含的短语将始终位于索引术语的开头。这是一个可怕的解决方案,充满缺陷,但它有效。

然后我们考虑托管一个单独的服务器来进行查找。谷歌搜索 Lucene 和 elastisearch 将为您提供有关这些现成软件包的详细信息。

最终,我们开发了自己的内部搜索引擎,它与 SQL 一起运行。这使我们能够实现语音搜索(双变音位),然后使用 levenshtein 计算和 soundex 来建立相关性。对于很多解决方案来说都有些过头了,但在我们的用例中值得付出努力。我们现在甚至可以选择利用 Nvidia GPU 进行 cuda 搜索,但这代表了一系列全新的头痛和不眠之夜。所有这些的相关性将取决于您执行搜索的频率以及您需要它们的反应程度。