FTS không hoạt động như mong đợi với các email có dấu chấm


9

Chúng tôi đang phát triển một tìm kiếm như là một phần của hệ thống lớn hơn.

Chúng tôi có Microsoft SQL Server 2014 - 12.0.2000.8 (X64) Standard Edition (64-bit)thiết lập này:

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)
);
  1. Phone là một chuỗi chữ số được phân tách bằng dấu phẩy có cấu trúc như "77777777777, 88888888888"
  2. Emailđược cấu trúc chuỗi email với dấu phẩy như "email1@gmail.com, email2@gmail.com"(hoặc không có dấu phẩy nào cả "email1@gmail.com")
  3. Contacts1, Contacts2, Contacts3, Contacts4là các trường văn bản nơi người dùng có thể chỉ định chi tiết liên hệ ở dạng miễn phí. Thích "John Smith +1 202 555 0156"hay "Bob, +1-999-888-0156, bob@company.com". Các trường này có thể chứa email và điện thoại mà chúng tôi muốn tìm kiếm thêm.

Ở đây chúng tôi tạo ra các công cụ toàn văn

-- 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

Đây là một mẫu dữ liệu

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)

Thật ra chúng tôi có khoảng 100 ngàn hồ sơ như vậy.

Chúng tôi hy vọng người dùng có thể chỉ định một phần của email như "@ gmail.com" và điều này sẽ trả về tất cả các hàng có địa chỉ email Gmail trong bất kỳ Email, Contacts1, Contacts2, Contacts3, Contacts4trường nào.

Tương tự cho số điện thoại. Người dùng có thể tìm kiếm một mẫu như "70283" và một truy vấn sẽ trả về điện thoại có các chữ số này. Ngay cả đối với Contacts1, Contacts2, Contacts3, Contacts4các trường mẫu tự do , trước tiên chúng ta có thể xóa tất cả trừ các chữ số và ký tự khoảng trắng trước khi tìm kiếm.

Chúng tôi thường sử dụng LIKEđể tìm kiếm khi chúng tôi có khoảng 1500 hồ sơ và nó hoạt động tốt nhưng bây giờ chúng tôi có rất nhiều hồ sơ và việc LIKEtìm kiếm mất vô hạn để có kết quả.

Đây là cách chúng tôi cố gắng để có được dữ liệu từ đó:

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

5
Tại sao tất cả các cột của bạn nvarchar(MAX)ở đây? Tôi chưa bao giờ nghe nói, hoặc gặp bất kỳ ai tên dài 1 tỷ ~ ký tự. Và, theo câu trả lời này , một địa chỉ email không thể dài hơn 254 ký tự; Vì vậy, bạn cũng có 1 tỷ ~ nhân vật lãng phí ở đó.
Larnu

2
Âm thanh như bạn đang chiến đấu với bộ ngắt từ tìm kiếm toàn văn bản. Bạn không thể tìm thấy bất cứ điều gì bằng cách sử dụng @gmail.comnhư một thuật ngữ tìm kiếm vì @ký tự là một công cụ ngắt từ. Nói cách khác, tùy theo phiên bản của SQL Server mà bạn có, từ trong chỉ mục cho user@gmail.comsẽ là một trong hai (A) user, gmailcomhoặc (B) user, user@gmail.com, gmailcom. REF: Thay đổi hành vi đối với Tìm kiếm toàn văn bản
Luôn luôn học

1
"nhưng tôi không muốn tìm kiếm bất cứ thứ gì ngoại trừ email và điện thoại trong các lĩnh vực đó " thì chúng nên được lưu trữ trong một cột thích hợp, như tôi đã nói trước đây. Bạn có các cột cho dữ liệu đó, cần được chuẩn hóa. Word breakers được đặt ở mức cá thể / cơ sở dữ liệu. Vì vậy, nó sẽ là một thay đổi đáng kể để loại bỏ ..
Larnu

1
Bạn muốn bình thường hóa các bảng thành 1 M cho tất cả các bản ghi điện thoại, email, v.v. Tùy chọn thứ hai là phân tách các cột (sử dụng chuỗi_split (email, ','), kết hợp với Áp dụng ngoài. Bạn phải chỉ định giới hạn lý thuyết về số lượng email mà người dùng có thể có. Sau đó viết một tìm kiếm như thế này : SELECT * FROM NewCompanies WHERE Id IN (SELECT ID from .... where MyOuterApply.EmailCol1 LIKE '%'+@SearchString+'%') OR Id IN (SELECT ID from .... where MyOuterApply.EmailCol2 LIKE '%'+@SearchString+'%'). Tạo khoảng năm chỉ mục riêng lẻ trên mỗi trường và bao gồm khóa chính.
starbyone

2
@TheDudeWithHat Không đi, không có nghĩa là không nên. Lý do OP gặp vấn đề là vì thiếu bình thường hóa.
Larnu

Câu trả lời:


2

Thực tế yêu cầu

CHỌN [...] CONTAIN ([...], '"6662211 *"') - không nhận được gì

chống lại 'Call only at weekends +7-999-666-22-11'

CHỌN [...] CONTAIN (Tên, '"zimuth *"') - không nhận được gì

chống lại 'PJSC Azimuth'

làm việc như mong đợi .
Xem Thuật ngữ tiền tố . Bởi vì 6662211*không phải là một tiền tố của +7-999-666-22-11cũng như như zimuth*là không phải là một tiền tố củaAzimuth

Đối với

CHỌN [...] CONTAIN ([...], '"sms@gmail.com*"') - điều này không nhận được hàng

Điều này có lẽ là do trình ngắt từ như luôn luôn chỉ ra trong các bình luận. Xem phần ngắt từ

Tôi không nghĩ rằng Tìm kiếm toàn văn bản có thể áp dụng cho nhiệm vụ của bạn.

Tại sao sử dụng cho FTS trong cùng một nhiệm vụ chính xác mà toán tử THÍCH được sử dụng cho? Nếu có một loại chỉ mục tốt hơn cho các truy vấn THÍCH ... thì sẽ có loại chỉ mục tốt hơn , không phải là công nghệ và cú pháp hoàn toàn khác.
Và không có cách nào nó sẽ giúp bạn phù hợp "6662211*"với "666 một số char tùy ý 22 một số char 11 tùy ý ".
Tìm kiếm toàn văn bản không phải là về regex-es (và "6662211*"thậm chí không phải là một biểu thức chính xác cho công việc - không có gì về "một số char tùy ý") đó là về từ đồng nghĩa, hình thức từ, v.v.

Nhưng liệu có thể tìm kiếm các chuỗi con một cách hiệu quả?

Vâng, đúng vậy. Bỏ qua những triển vọng như viết công cụ tìm kiếm của riêng bạn, chúng ta có thể làm gì trong đó SQL?

Trước hết - bắt buộc phải dọn sạch dữ liệu của bạn! Nếu bạn muốn trả lại cho người dùng chuỗi chính xác họ đã nhập

người dùng có thể chỉ định chi tiết liên lạc ở dạng miễn phí

... bạn có thể lưu chúng như ... và để chúng lại.
Sau đó, bạn cần trích xuất dữ liệu từ văn bản biểu mẫu miễn phí (không quá khó đối với email và số điện thoại) và lưu dữ liệu ở một số dạng chính tắc. Đối với email, điều duy nhất bạn thực sự cần làm - làm cho tất cả chúng là chữ thường hoặc chữ hoa (không quan trọng), và có thể tách ra sau đó @hát. Nhưng trong các số điện thoại, bạn chỉ cần để lại các chữ số
(... Và sau đó bạn thậm chí có thể lưu trữ chúng dưới dạng số . Điều đó có thể giúp bạn tiết kiệm không gian và thời gian. Nhưng việc tìm kiếm sẽ khác ... Bây giờ, hãy đi sâu vào đơn giản hơn và giải pháp phổ quát sử dụng chuỗi.)

Như MatthewBaker đã đề cập, bạn có thể tạo một bảng các hậu tố. Sau đó, bạn có thể tìm kiếm như vậy

SELECT DISTINCT * FROM NewCompanies JOIN Sufficies ON NewCompanies.Id = Sufficies.Id WHERE Sufficies.sufficies LIKE 'some text%'

Bạn chỉ nên đặt ký tự đại diện %ở cuối . Hoặc sẽ không có lợi ích từ bảng Suffixes.

Lấy ví dụ số điện thoại

+ 7-999-666-22-11

Sau khi chúng ta loại bỏ các ký tự thải trong đó, nó sẽ có 11 chữ số. Điều đó có nghĩa là chúng ta sẽ cần 11 hậu tố cho một số điện thoại

           1
          11
         211
        2211
       62211
      662211
     6662211
    96662211
   996662211
  9996662211
 79996662211

Vì vậy, độ phức tạp không gian cho giải pháp này là tuyến tính ... không quá tệ, tôi nói ... Nhưng hãy chờ xem nó phức tạp về số lượng hồ sơ. Nhưng trong các biểu tượng ... chúng ta cần N(N+1)/2các biểu tượng để lưu trữ tất cả các hậu tố - đó là độ phức tạp bậc hai ... không tốt ... nhưng nếu bây giờ bạn có 100 000các bản ghi và không có kế hoạch cho hàng triệu trong tương lai gần - bạn có thể đi với điều này giải pháp.

Chúng ta có thể giảm độ phức tạp không gian?

Tôi sẽ chỉ mô tả ý tưởng, thực hiện nó sẽ mất một số nỗ lực. Và có lẽ chúng ta sẽ cần phải vượt qua ranh giới củaSQL

Giả sử bạn có 2 hàng trong NewCompaniesvà 2 chuỗi văn bản biểu mẫu miễn phí trong đó:

    aaaaa
    11111

Bàn Suffixes nên lớn như thế nào? Rõ ràng, chúng tôi chỉ cần 2 hồ sơ.

Hãy lấy một ví dụ khác. Ngoài ra 2 hàng, 2 chuỗi văn bản miễn phí để tìm kiếm. Nhưng bây giờ:

    aa11aa
    cc11cc

Chúng ta hãy xem bây giờ chúng ta cần bao nhiêu hậu tố:

         a // no need, LIKE `a%`  will match against 'aa' and 'a11aa' and 'aa11aa'
        aa // no need, LIKE `aa%` will match against 'aa11aa'
       1aa
      11aa
     a11aa
    aa11aa
         c // no need, LIKE `c%`  will match against 'cc' and 'c11cc' and 'cc11cc'
        cc // no need, LIKE `cc%` will match against 'cc11cc'
       1cc
      11cc
     c11cc
    cc11cc

Không quá tệ, nhưng cũng không tốt lắm.

Ta còn làm gì khác được nữa?

Giả sử, người dùng tham gia vào "c11"trường tìm kiếm. Sau đó, LIKE 'c11%'cần hậu tố ' c11 cc' để thành công. Nhưng nếu thay vì tìm kiếm "c11"đầu tiên chúng ta tìm kiếm "c%", sau đó "c1%"và vân vân? Tìm kiếm đầu tiên sẽ cho chỉ một hàng từ NewCompanies. Và sẽ không cần cho các tìm kiếm tiếp theo. Và chúng tôi có thể

       1aa // drop this as well, because LIKE '1%' matches '11aa'
      11aa
     a11aa // drop this as well, because LIKE 'a%' matches 'aa11aa'
    aa11aa
       1cc // same here
      11cc
     c11cc // same here
    cc11cc

và chúng tôi kết thúc chỉ với 4 hậu tố

      11aa
    aa11aa
      11cc
    cc11cc

Tôi không thể nói sự phức tạp của không gian trong trường hợp này là gì, nhưng cảm giác như nó sẽ được chấp nhận.


1

Trong trường hợp như tìm kiếm toàn văn bản này là ít hơn lý tưởng. Tôi đã ở trong cùng một chiếc thuyền như bạn. Giống như các tìm kiếm quá chậm và tìm kiếm toàn văn tìm kiếm các từ bắt đầu bằng một thuật ngữ thay vì chứa một thuật ngữ.

Chúng tôi đã thử một số giải pháp, một tùy chọn SQL thuần túy là xây dựng phiên bản tìm kiếm toàn văn của riêng bạn, đặc biệt là tìm kiếm chỉ mục ngược. Chúng tôi đã thử điều này, và nó đã thành công, nhưng chiếm rất nhiều không gian. Chúng tôi đã tạo một bảng giữ thứ cấp cho các cụm từ tìm kiếm một phần và sử dụng lập chỉ mục toàn văn trên đó. Tuy nhiên điều này có nghĩa là chúng tôi liên tục lưu trữ nhiều bản sao của cùng một thứ. Ví dụ: chúng tôi đã lưu trữ "longword" dưới dạng Longword, ongword, ngword, gword .... vv Vì vậy, bất kỳ cụm từ có chứa nào sẽ luôn luôn ở đầu của thuật ngữ được lập chỉ mục. Một giải pháp khủng khiếp, đầy sai sót, nhưng nó đã làm việc.

Sau đó chúng tôi đã xem xét việc lưu trữ một máy chủ riêng để tra cứu. Googling Lucene và elastisearch sẽ cung cấp cho bạn thông tin tốt về những gói này.

Cuối cùng, chúng tôi đã phát triển công cụ tìm kiếm nội bộ của riêng mình, chạy dọc theo SQL. Điều này đã cho phép chúng tôi thực hiện tìm kiếm ngữ âm (metaphone kép) và sau đó sử dụng tính toán levenshtein dọc theo soundex bên cạnh để thiết lập mức độ liên quan. Quá mức cho rất nhiều giải pháp, nhưng đáng nỗ lực trong trường hợp sử dụng của chúng tôi. Bây giờ chúng tôi thậm chí còn có tùy chọn tận dụng GPU Nvidia cho các tìm kiếm cuda, nhưng điều này đại diện cho một loạt các cơn đau đầu và mất ngủ mới. Sự liên quan của tất cả những điều này sẽ phụ thuộc vào tần suất bạn thấy các tìm kiếm của mình được thực hiện và mức độ phản ứng mà bạn cần chúng.


1

Chỉ mục toàn văn có một số hạn chế. Bạn có thể sử dụng ký tự đại diện cho các từ mà chỉ mục tìm thấy là toàn bộ "phần" nhưng ngay cả khi đó bạn bị giới hạn ở phần kết thúc của từ. Đó là lý do tại sao bạn có thể sử dụng CONTAINS(Name, '"Azimut*"')nhưng khôngCONTAINS(Name, '"zimuth*"')

Từ tài liệu của Microsoft :

Khi thuật ngữ tiền tố là một cụm từ, mỗi mã thông báo tạo thành cụm từ được coi là một thuật ngữ tiền tố riêng. Tất cả các hàng có từ bắt đầu bằng thuật ngữ tiền tố sẽ được trả về. Ví dụ: thuật ngữ tiền tố "bánh mì nhẹ *" sẽ tìm thấy các hàng có văn bản "bánh mì nhẹ", "bánh mì nhẹ" hoặc "bánh mì nhẹ" nhưng nó sẽ không trả về "bánh mì nướng nhẹ".

Các dấu chấm trong email, như được chỉ định bởi tiêu đề, không phải là vấn đề chính. Điều này, ví dụ, hoạt động:

SELECT * FROM NewCompanies 
WHERE CONTAINS((Email, Contacts1, Contacts2, Contacts3, Contacts4), 's.m.s@gmail.com') 

Trong trường hợp này, chỉ mục xác định toàn bộ chuỗi email là hợp lệ, cũng như "gmail" và "gmail.com." Chỉ "sms" mặc dù không hợp lệ.

Ví dụ cuối cùng là tương tự. Các phần của số điện thoại được lập chỉ mục (ví dụ 666-22-11 và 999-666-22-11), nhưng loại bỏ các dấu gạch nối không phải là một chuỗi mà chỉ mục sẽ biết. Mặt khác, điều này không hoạt động:

SELECT * FROM NewCompanies 
WHERE CONTAINS((Phone, Contacts1, Contacts2, Contacts3, Contacts4), '"666-22-11*"')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.