Rozwijamy wyszukiwanie w ramach większego systemu.
Mamy Microsoft SQL Server 2014 - 12.0.2000.8 (X64) Standard Edition (64-bit)
z tą konfiguracją:
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)
);
Phone
jest ciągiem cyfr rozdzielonych przecinkami, np"77777777777, 88888888888"
Email
jest uporządkowanym ciągiem wiadomości e-mail z przecinkami podobnymi"[email protected], [email protected]"
(lub w ogóle bez przecinków"[email protected]"
)Contacts1, Contacts2, Contacts3, Contacts4
to pola tekstowe, w których użytkownicy mogą podać dane kontaktowe w dowolnej formie. Jak"John Smith +1 202 555 0156"
lub"Bob, +1-999-888-0156, [email protected]"
. Te pola mogą zawierać wiadomości e-mail i telefony, które chcemy dalej wyszukiwać.
Tutaj tworzymy treści pełnotekstowe
-- 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
Oto próbka danych
INSERT INTO NewCompanies(Id, Name, Phone, Email, Contacts1, Contacts2, Contacts3, Contacts4)
VALUES ('7BA05F18-1337-4AFB-80D9-00001A777E4F', 'PJSC Azimuth', '79001002030, 78005005044', '[email protected], [email protected]', 'John Smith', 'Call only at weekends +7-999-666-22-11', NULL, NULL)
W rzeczywistości mamy około 100 tysięcy takich zapisów.
Oczekujemy, że użytkownicy mogą określić część wiadomości e-mail, np. „@ Gmail.com”, co powinno zwrócić wszystkie wiersze z adresami e-mail Gmaila w dowolnym Email, Contacts1, Contacts2, Contacts3, Contacts4
polu.
To samo dotyczy numerów telefonów. Użytkownicy mogą wyszukiwać wzór taki jak „70283”, a zapytanie powinno zwrócić telefony z tymi cyframi. Dotyczy to nawet Contacts1, Contacts2, Contacts3, Contacts4
pól o dowolnych formach, w których prawdopodobnie powinniśmy usunąć wszystkie oprócz cyfr i spacji przed rozpoczęciem wyszukiwania.
Kiedyś mieliśmy do LIKE
wyszukiwania, kiedy mieliśmy około 1500 rekordów i działało dobrze, ale teraz mamy wiele rekordów, a LIKE
wyszukiwanie trwa nieskończenie, aby uzyskać wyniki.
W ten sposób staramy się uzyskać stamtąd dane:
SELECT * FROM NewCompanies WHERE CONTAINS((Email, Contacts1, Contacts2, Contacts3, Contacts4), '"[email protected]*"') -- 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
nvarchar(MAX)
tutaj? Nigdy nie słyszałem ani nie spotkałem nikogo, kto ma na imię 1 miliard ~ znaków. Zgodnie z tą odpowiedzią adres e-mail nie może być dłuższy niż 254 znaki; więc masz tam również 1 miliard ~ zmarnowanych postaci.@gmail.com
jako wyszukiwanego terminu, ponieważ@
znak jest łamaczem słów. Innymi słowy, w zależności od wersji SQL Server masz, wyrazy w indeksie na[email protected]
będzie albo (A)user
,gmail
acom
lub (B)user
,[email protected]
,gmail
icom
. ODNIESIENIE: Zmiany w zachowaniu wyszukiwania.
.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+'%')
Utwórz około pięciu indywidualnych indeksów na każdym polu iOdpowiedzi:
Właściwie to prośby
przeciw
'Call only at weekends +7-999-666-22-11'
iprzeciwko
'PJSC Azimuth'
wykonuj prace zgodnie z oczekiwaniami .
Patrz termin przedrostek . Ponieważ
6662211*
nie jest to prefiks z+7-999-666-22-11
jak równieżzimuth*
nie jest prefiks zAzimuth
Jeśli chodzi o
Wynika to prawdopodobnie z łamaczy słów, jak zawsze wskazano w komentarzach. Zobacz dzielniki słów
Nie sądzę, że wyszukiwanie pełnotekstowe ma zastosowanie do twojego zadania.
Dlaczego warto korzystać z FTS w dokładnie takich samych zadaniach, jak w przypadku operatora LIKE? Gdyby istniał lepszy typ indeksu dla zapytań LIKE ... to byłby lepszy typ indeksu , a nie zupełnie inna technologia i składnia.
I w żaden sposób nie pomoże ci dopasować się
"6662211*"
do „666 jakiegoś arbitralnego char 22 jakiegoś arbitralnego char 11”.Wyszukiwanie pełnotekstowe
"6662211*"
nie dotyczy wyrażeń regularnych (i nie jest nawet poprawnym wyrażeniem dla zadania - nie ma nic o części „dowolnych znaków”), ale o synonimach, formach słów itp.Ale czy w ogóle można skutecznie szukać podciągów?
Tak to jest. Pomijając takie perspektywy, jak napisanie własnej wyszukiwarki, co możemy zrobić
SQL
?Przede wszystkim - konieczne jest oczyszczenie danych! Jeśli chcesz zwrócić użytkownikom dokładne ciągi, które wprowadzili
... możesz je uratować takimi, jakie są ... i zostawić je przy sobie.
Następnie musisz wyodrębnić dane z dowolnego tekstu (nie jest to takie trudne dla wiadomości e-mail i numerów telefonów) i zapisać dane w jakiejś kanonicznej formie. W przypadku wiadomości e-mail jedyne, co naprawdę musisz zrobić - ułóż je małymi lub wielkimi literami (nie ma to znaczenia), a być może podzielone na
@
sing. Ale w numerach telefonów musisz pozostawić tylko cyfry(... A potem możesz nawet zapisać je jako liczby . To może zaoszczędzić trochę miejsca i czasu. Ale wyszukiwanie będzie inne ... Na razie przejdźmy do prostszego i uniwersalne rozwiązanie wykorzystujące ciągi.)
Jak wspomniał MatthewBaker , możesz utworzyć tabelę przyrostków. Następnie możesz tak wyszukiwać
Powinieneś umieścić symbol wieloznaczny
%
tylko na końcu . Albo nie byłoby żadnych korzyści z tabeli sufiksów.Weźmy na przykład numer telefonu
Gdy pozbędziemy się w nim znaków odpadów, będzie miał 11 cyfr. Oznacza to, że potrzebujemy 11 sufiksów dla jednego numeru telefonu
Więc złożoność przestrzeni dla tego rozwiązania jest liniowa ... nie jest taka zła, powiedziałbym ... Ale poczekaj, to złożoność liczby rekordów. Ale w symbolach ... potrzebujemy
N(N+1)/2
symboli do przechowywania wszystkich sufiksów - to kwadratowa złożoność ... nie dobrze ... ale jeśli masz teraz100 000
zapisy i nie masz planów na miliony w najbliższej przyszłości - możesz to zrobić rozwiązanie.Czy możemy zmniejszyć złożoność przestrzeni?
Opiszę tylko pomysł, jego wdrożenie zajmie trochę wysiłku. I prawdopodobnie będziemy musieli przekroczyć granice
SQL
Załóżmy, że masz 2 wiersze
NewCompanies
i 2 ciągi tekstu w dowolnym formacie:Jak duża powinna być tabela sufiksów? Oczywiście potrzebujemy tylko 2 płyt.
Weźmy inny przykład. Również 2 wiersze, 2 ciągi tekstowe do wyszukiwania. Ale teraz jest to:
Zobaczmy, ile sufiksów potrzebujemy teraz:
Nie tak źle, ale też nie tak dobrze.
Co jeszcze możemy zrobić?
Załóżmy, że użytkownik wchodzi
"c11"
w pole wyszukiwania. NastępnieLIKE 'c11%'
potrzebuje przyrostka „ c11 cc”, aby odnieść sukces. Ale jeśli zamiast wyszukiwania"c11"
najpierw szukamy"c%"
, to"c1%"
i tak dalej? Pierwsze wyszukiwanie da tylko jeden wiersz odNewCompanies
. I nie będzie potrzeby kolejnych wyszukiwań. I możemyi otrzymujemy tylko 4 sufiksy
Nie mogę powiedzieć, jaka byłaby w tym przypadku złożoność przestrzeni, ale wydaje się, że byłaby do przyjęcia.
źródło
W takich przypadkach wyszukiwanie pełnotekstowe jest mniej niż idealne. Byłem na tej samej łodzi co ty. Podobnie, jak wyszukiwania są zbyt wolne, a wyszukiwania pełnotekstowe szukają słów, które zaczynają się od terminu, a nie zawierają terminu.
Wypróbowaliśmy kilka rozwiązań, jedną z czystych opcji SQL jest zbudowanie własnej wersji wyszukiwania pełnotekstowego, w szczególności wyszukiwania indeksu odwróconego. Próbowaliśmy tego i udało się, ale zajęło dużo miejsca. Stworzyliśmy dodatkową tabelę wstrzymującą dla częściowych wyszukiwanych haseł i wykorzystaliśmy w niej indeksowanie pełnotekstowe. Oznacza to jednak, że wielokrotnie przechowywaliśmy wiele kopii tej samej rzeczy. Na przykład, zapisaliśmy „longword” jako Longword, ongword, ngword, gword .... itd. Tak więc każda zawarta fraza zawsze będzie na początku indeksowanego terminu. Przerażające rozwiązanie, pełne wad, ale zadziałało.
Następnie przyjrzeliśmy się hostowaniu oddzielnego serwera do wyszukiwania. Googling Lucene i elastisearch dadzą ci dobre informacje na temat tych gotowych opakowań.
W końcu opracowaliśmy własną wyszukiwarkę, która działa obok SQL. Pozwoliło nam to na przeprowadzenie wyszukiwania fonetycznego (podwójny metafon), a następnie zastosowanie obliczeń lewenshteina obok soundexa w celu ustalenia znaczenia. Przesada w przypadku wielu rozwiązań, ale warta wysiłku w naszym przypadku użycia. Nawet teraz mamy opcję wykorzystania procesorów graficznych Nvidia do wyszukiwania cuda, ale stanowiło to zupełnie nowy zestaw bólów głowy i nieprzespanych nocy. Istotność tych wszystkich zależeć będzie od tego, jak często widzisz, że Twoje wyszukiwanie jest przeprowadzane, i od tego, jak reaktywne potrzebujesz.
źródło
Indeksy pełnotekstowe mają wiele ograniczeń. Możesz używać symboli wieloznacznych dla słów, które znalezione w indeksie są całymi „częściami”, ale nawet wtedy jesteś ograniczony do końcowej części słowa. Dlatego możesz używać,
CONTAINS(Name, '"Azimut*"')
ale nie możeszCONTAINS(Name, '"zimuth*"')
Z dokumentacji Microsoft :
Kropki w wiadomości e-mail, jak wskazuje tytuł, nie są głównym problemem. To na przykład działa:
W takim przypadku indeks identyfikuje cały ciąg wiadomości e-mail jako prawidłowy, a także „gmail” i „gmail.com”. Tylko „sms” nie jest poprawne.
Ostatni przykład jest podobny. Części numeru telefonu są indeksowane (na przykład 666-22-11 i 999-666-22-11), ale usunięcie łączników nie jest ciągiem, o którym indeks będzie wiedział. W przeciwnym razie działa to:
źródło