Kami sedang mengembangkan pencarian sebagai bagian dari sistem yang lebih besar.
Kami miliki Microsoft SQL Server 2014 - 12.0.2000.8 (X64) Standard Edition (64-bit)
dengan pengaturan ini:
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
adalah string digit yang dipisahkan koma terstruktur seperti"77777777777, 88888888888"
Email
adalah string email terstruktur dengan koma like"[email protected], [email protected]"
(atau tanpa koma sama sekali"[email protected]"
)Contacts1, Contacts2, Contacts3, Contacts4
adalah bidang teks tempat pengguna dapat menentukan detail kontak dalam bentuk bebas. Suka"John Smith +1 202 555 0156"
atau"Bob, +1-999-888-0156, [email protected]"
. Bidang ini dapat berisi email dan telepon yang ingin kami cari lebih lanjut.
Di sini kami membuat teks lengkap
-- 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
Ini adalah contoh data
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)
Sebenarnya kami memiliki sekitar 100 ribu catatan seperti itu.
Kami berharap pengguna dapat menentukan bagian dari email seperti "@ gmail.com" dan ini akan mengembalikan semua baris dengan alamat email Gmail di salah satu Email, Contacts1, Contacts2, Contacts3, Contacts4
bidang.
Sama untuk nomor telepon. Pengguna dapat mencari pola seperti "70283" dan kueri harus mengembalikan ponsel dengan angka-angka ini di dalamnya. Itu bahkan untuk Contacts1, Contacts2, Contacts3, Contacts4
bidang formulir gratis di mana kita mungkin harus menghapus semua kecuali karakter angka dan spasi terlebih dahulu sebelum mencari.
Kami dulu menggunakan LIKE
untuk pencarian ketika kami memiliki sekitar 1500 catatan dan itu bekerja dengan baik tetapi sekarang kami memiliki banyak catatan dan LIKE
pencarian membutuhkan waktu tak terbatas untuk mendapatkan hasil.
Inilah cara kami mencoba mendapatkan data dari sana:
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)
sini? Saya belum pernah mendengar, atau bertemu orang yang namanya panjangnya 1 Miliar ~ karakter. Dan, menurut jawaban ini , alamat email tidak boleh lebih dari 254 karakter; jadi kamu juga punya 1 Billion ~ karakter terbuang di sana.@gmail.com
sebagai istilah pencarian karena@
karakternya adalah pemecah kata. Dengan kata lain, tergantung versi SQL Server yang Anda miliki, kata-kata dalam indeks untuk[email protected]
akan baik (A)user
,gmail
dancom
atau (B)user
,[email protected]
,gmail
dancom
. REF: Perubahan Perilaku menjadi Pencarian Teks Lengkap.
.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+'%')
Buat sekitar lima indeks individu pada masing-masing bidang dan sertakan kunci utamaJawaban:
Sebenarnya permintaan
menentang
'Call only at weekends +7-999-666-22-11'
danmelawan
'PJSC Azimuth'
lakukan pekerjaan seperti yang diharapkan .
Lihat Istilah Awalan . Karena
6662211*
bukanlah awalan dari+7-999-666-22-11
sertazimuth*
bukan merupakan awalan dariAzimuth
Untuk
Ini mungkin karena pemecah kata seperti yang selalu dipelajari dalam komentar. Lihat pemecah kata
Saya tidak berpikir bahwa Pencarian Teks Lengkap dapat diterapkan untuk tugas Anda.
Mengapa menggunakan FTS dalam tugas yang sama persis dengan yang digunakan oleh LIKE operator? Jika ada jenis indeks yang lebih baik untuk permintaan LIKE ... maka akan ada jenis indeks yang lebih baik , bukan teknologi dan sintaks yang sama sekali berbeda.
Dan sama sekali tidak akan membantu Anda untuk mencocokkan
"6662211*"
"666 some char arbitrary 22 some arbit arbit char 11".Pencarian Teks Lengkap bukan tentang regex-es (dan
"6662211*"
bahkan bukan ekspresi yang benar untuk pekerjaan - tidak ada bagian "arang yang berubah-ubah") ini tentang sinonim, bentuk kata, dll.Tetapi mungkinkah mencari substring secara efektif?
Ya itu. Mengesampingkan prospek seperti menulis mesin pencari Anda sendiri, apa yang bisa kita lakukan di dalam
SQL
?Pertama-tama - sangat penting untuk membersihkan data Anda! Jika Anda ingin kembali ke pengguna, string yang telah mereka masukkan
... Anda dapat menyimpannya apa adanya ... dan membiarkannya.
Maka Anda perlu mengekstraksi data dari teks formulir gratis (tidak begitu sulit untuk email dan nomor telepon) dan menyimpan data dalam beberapa bentuk kanonik. Untuk email, satu-satunya hal yang benar-benar perlu Anda lakukan - buat semuanya menjadi huruf kecil atau besar (tidak masalah), dan mungkin terbagi dua saat
@
bernyanyi. Tetapi dalam nomor telepon Anda hanya perlu menyisakan digit(... Dan kemudian Anda bahkan dapat menyimpannya sebagai angka . Itu dapat menghemat ruang dan waktu. Tetapi pencarian akan berbeda ... Untuk sekarang mari selami yang lebih sederhana dan solusi universal menggunakan string.)
Seperti yang disebutkan MatthewBaker, Anda bisa membuat tabel sufiks. Maka Anda dapat mencari seperti itu
Anda harus menempatkan wildcard
%
hanya di akhir . Atau tidak akan ada manfaat dari tabel Suffix.Misalnya, ambil nomor telepon
Setelah kita menyingkirkan karakter limbah di dalamnya, itu akan memiliki 11 digit. Itu berarti kita akan membutuhkan 11 sufiks untuk satu nomor telepon
Jadi kompleksitas ruang untuk solusi ini adalah linier ... tidak terlalu buruk, saya akan mengatakan ... Tapi tunggu dulu kompleksitasnya dalam jumlah record. Tetapi dalam simbol ... kita perlu
N(N+1)/2
simbol untuk menyimpan semua sufiks - yaitu kompleksitas kuadrat ... tidak baik ... tetapi jika Anda sekarang memiliki100 000
catatan dan tidak memiliki rencana untuk jutaan dalam waktu dekat - Anda dapat menggunakan ini larutan.Bisakah kita mengurangi kompleksitas ruang?
Saya hanya akan menjelaskan ide itu, mengimplementasikannya akan membutuhkan usaha. Dan mungkin kita harus melewati batas
SQL
Katakanlah Anda memiliki 2 baris
NewCompanies
dan 2 string teks formulir gratis di dalamnya:Seberapa besar seharusnya tabel Suffix? Jelas, kami hanya membutuhkan 2 catatan.
Mari kita ambil contoh lain. Juga 2 baris, 2 string teks gratis untuk dicari. Tapi sekarang:
Mari kita lihat berapa banyak sufiks yang kita butuhkan sekarang:
Tidak terlalu buruk, tapi juga tidak begitu baik.
apa lagi yang bisa kita lakukan?
Katakanlah, pengguna masuk
"c11"
di bidang pencarian. MakaLIKE 'c11%'
perlu sufiks ' c11 cc' untuk berhasil. Tetapi jika alih-alih mencari,"c11"
pertama-tama kita mencari"c%"
, lalu untuk"c1%"
dan seterusnya? Pencarian pertama akan memberikan hanya satu baris dariNewCompanies
. Dan tidak perlu untuk pencarian selanjutnya. Dan kita bisadan kita berakhir dengan hanya 4 sufiks
Saya tidak bisa mengatakan apa kompleksitas ruang dalam kasus ini, tetapi rasanya itu bisa diterima.
sumber
Dalam kasus seperti ini pencarian teks lengkap kurang dari ideal. Saya berada di kapal yang sama seperti Anda. Seperti pencarian terlalu lambat, dan pencarian teks lengkap mencari kata-kata yang dimulai dengan istilah daripada mengandung istilah.
Kami mencoba beberapa solusi, salah satu opsi SQL murni adalah membangun versi Anda sendiri dari pencarian teks lengkap, khususnya pencarian indeks terbalik. Kami mencoba ini, dan itu berhasil, tetapi memakan banyak ruang. Kami membuat tabel penahan sekunder untuk istilah pencarian parsial, dan menggunakan pengindeksan teks lengkap untuk itu. Namun ini berarti kami berulang kali menyimpan banyak salinan dari hal yang sama. Sebagai contoh, kami menyimpan "kata panjang" sebagai Longword, ongword, ngword, gword .... dll. Jadi setiap frase yang terkandung akan selalu berada di awal istilah yang diindeks. Solusi menghebohkan, penuh kekurangan, tetapi berhasil.
Kami kemudian mencari hosting server terpisah untuk pencarian. Googling Lucene dan elastisearch akan memberi Anda informasi yang baik tentang paket yang ada di rak ini.
Akhirnya, kami mengembangkan sendiri mesin pencari in-house kami, yang berjalan di sepanjang sisi SQL. Ini memungkinkan kami untuk mengimplementasikan pencarian fonetik (metafon ganda) dan kemudian menggunakan perhitungan levenshtein di samping soundex untuk membangun relevansi. Berlebihan untuk banyak solusi, tetapi sepadan dengan usaha dalam kasus penggunaan kami. Kami bahkan sekarang memiliki opsi untuk meningkatkan GPU Nvidia untuk pencarian cuda, tetapi ini merepresentasikan serangkaian sakit kepala baru dan malam tanpa tidur. Relevansi semua ini akan tergantung pada seberapa sering Anda melihat pencarian Anda dilakukan, dan seberapa reaktif Anda membutuhkannya.
sumber
Indeks Teks Lengkap memiliki sejumlah batasan. Anda dapat menggunakan wildcard pada kata-kata yang indeks temukan adalah seluruh "bagian" tetapi meskipun begitu Anda dibatasi pada bagian akhir kata. Itu sebabnya Anda bisa menggunakan
CONTAINS(Name, '"Azimut*"')
tetapi tidakCONTAINS(Name, '"zimuth*"')
Dari dokumentasi Microsoft :
Titik-titik dalam email, seperti yang ditunjukkan oleh judul, bukan masalah utama. Ini, misalnya, berfungsi:
Dalam hal ini, indeks mengidentifikasi seluruh string email sebagai valid, serta "gmail" dan "gmail.com." Hanya "sms" meskipun tidak valid.
Contoh terakhir mirip. Bagian-bagian dari nomor telepon diindeks (666-22-11 dan 999-666-22-11 misalnya), tetapi menghapus tanda hubung bukanlah string yang akan diketahui oleh indeks. Kalau tidak, ini bekerja:
sumber