FTSがドットのあるメールで期待どおりに機能しない


9

より大きなシステムの一部として検索を開発しています。

私たちは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)
);
  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

ここにデータサンプルがあります

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)

実際、そのような記録は約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

5
なぜすべての列がnvarchar(MAX)ここにあるのですか?聞いたことも、名前が10億人以上の人に会ったこともありません。また、この回答によれば、メールアドレスは254文字を超えることはできません。そこにも10億〜の無駄なキャラクターがいます。
ラルヌ

2
フルテキスト検索のワードブレーカーで戦っているようです。文字はワードブレーカーである@gmail.comため、検索語として使用しているものを見つけることはほとんどありません@。言い換えれば、あなたが持っているSQL Serverのバージョンに依存、ためのインデックス内の単語はuser@gmail.comどちらか(A)になりusergmailおよびcomまたは(B) 、、user と。REF:全文検索の動作の変更user@gmail.comgmailcom
AlwaysLearning

1
「しかし、私はそれらのフィールドで電子メールと電話以外のものを検索したくない」それらはそれらが前に言ったように適切な列に保存されるべきです。そのデータの列があり、正規化する必要があります。ワードブレーカーはインスタンス/データベースレベルで設定されます。そのため、削除することは重大な重大な変更になり.ます。
ラルヌ

1
すべての電話、電子メールなどのテーブルを1-Mに正規化する必要があります。レコード2番目のオプションは、列を分割することです(string_split(email、 '、'を使用)と、外部適用を組み合わせて使用​​します。 :。次に、このような検索を書き、ユーザーが持つことができるメールの数に理論上の制限を指定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+'%')の各フィールドに5についての個々のインデックスを作成し、主キーなどがあります。
starbyoneを

2
@TheDudeWithHat行くつもりはない、それがそうであってはならないという意味ではありません。OPに問題がある理由は、正規化の欠如によるものです。
ラルヌ

回答:


2

実際にリクエスト

SELECT [...] CONTAINS([...]、 '"6662211 *"')-何も取得しません

に対して'Call only at weekends +7-999-666-22-11'

SELECT [...] CONTAINS(Name、 '"zimuth *"')-何も取得しません

に対して 'PJSC Azimuth'

やる期待通りの仕事をPrefix Termを
参照してください。なぜなら6662211*はない接頭辞+7-999-666-22-11と同様にはzimuth*されていない接頭語Azimuth

はどうかと言うと

SELECT [...] CONTAINS([...]、 '"sms@gmail.com*"')-これは行を取得しません

常に学習がコメントで指摘されているように、これはおそらくワードブレーカーによるものです。見るワードブレーカーを

全文検索があなたの仕事に適用できるとは思いません。

LIKE演算子が使用されるのとまったく同じタスクでFTSを使用する理由 LIKEクエリに適したインデックスタイプがある場合... 完全に異なるテクノロジーや構文ではなく、より適切なインデックスタイプがあります。
そして、"6662211*"「666 some any char 22 some some char 11」と照合するのに役立つことは決してありません。
全文検索は正規表現についてで"6662211*"はありません(また、ジョブの正しい表現でさえありません。「任意の文字」の部分については何もありません)同義語や単語形式などについてです。

しかし、部分文字列を効果的に検索することはまったく可能ですか?

はい、そうです。独自の検索エンジンを作成するような見込み客は別として、私たちは何ができるのSQLでしょうか?

まず第一に-それはあなたのデータをクリーンアップすることが不可欠です!ユーザーに入力した正確な文字列をユーザーに返したい場合

ユーザーは連絡先の詳細を自由形式で指定できます

...そのまま保存して、そのままにしておくことができます。
次に、自由形式のテキストからデータを抽出し(電子メールや電話番号の場合はそれほど難しくありません)、データを正規の形式で保存する必要があります。メールの場合、本当にする必要があるのは唯一のことです-それらをすべて小文字にするか大文字にするか(重要ではありません)、おそらく分割して@歌います。しかし、電話番号では、数字だけを残す必要があります
(...そして、数字として保存することもできます。これにより、スペースと時間を節約できます。しかし、検索は異なります...今のところ、もっと簡単に調べてみましょうおよび文字列を使用したユニバーサルソリューション。)

MatthewBakerが述べたように、サフィックスのテーブルを作成できます。次に、そのように検索できます

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

ワイルドカード%は最後にのみ配置する必要がありますます。または、サフィックステーブルからのメリットはありません。

たとえば電話番号を考えてみましょう

+ 7-999-666-22-11

そこにある廃物を取り除くと、11桁になります。つまり、1つの電話番号に11のサフィックスが必要になります。

           1
          11
         211
        2211
       62211
      662211
     6662211
    96662211
   996662211
  9996662211
 79996662211

したがって、このソリューションのスペースの複雑さは直線的です...それほど悪くはない、と私は言います... しかし、レコード数の複雑さを待っください。しかし、シンボルでは... N(N+1)/2すべてのサフィックスを格納するためのシンボルが必要です。これは、2次の複雑さです...良くありません...しかし、100 000レコードがあり、近い将来に何百万もの計画解決。

スペースの複雑さを軽減できますか?

アイデアについてのみ説明します。実装には多少の労力が必要です。そしておそらく私達はの境界を越える必要があるでしょうSQL

2つの行がNewCompaniesあり、その中に2つの自由形式のテキストの文字列があるとします。

    aaaaa
    11111

サフィックステーブルはどのくらいの大きさにする必要がありますか?明らかに、必要なレコードは2つだけです。

別の例を見てみましょう。また、2行、2つのフリーテキスト文字列を検索します。しかし今それは:

    aa11aa
    cc11cc

今必要なサフィックスの数を見てみましょう:

         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

それほど悪くはないが、あまり良くない。

他に何ができますか?

たとえば、ユーザー"c11"が検索フィールドに入力したとします。次に、成功LIKE 'c11%'するには「c11 cc」サフィックスが必要です。しかし、もし代わりに探して"c11"、我々は最初の検索"c%"、その後のため、"c1%"というように?最初の検索では、からの1行のみが返されNewCompaniesます。そして、その後の検索は必要ありません。そして私たちはできる

       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

そして、最後には4つのサフィックスしかありません

      11aa
    aa11aa
      11cc
    cc11cc

この場合、スペースの複雑さはどうなるかはわかりませんが、許容できると感じています。


1

このような場合、全文検索は理想的とは言えません。私はあなたと同じ船に乗っていました。Like検索は遅すぎ、全文検索では、用語を含むのではなく、用語で始まる単語を検索します。

私たちはいくつかのソリューションを試しましたが、純粋なSQLオプションの1つは、独自のバージョンの全文検索、特に逆索引検索を作成することです。私たちはこれを試しましたが、成功しましたが、多くのスペースを取りました。部分的な検索用語用の2次保持テーブルを作成し、それに全文索引付けを使用しました。ただし、これは同じものの複数のコピーを繰り返し保存したことを意味します。たとえば、「longword」をLongword、ongword、ngword、gword ....などとして格納しました。したがって、含まれるフレーズは常にインデックス付き用語の先頭に置かれます。欠陥の多い恐ろしい解決策ですが、うまくいきました。

次に、ルックアップ用に別のサーバーをホストする方法を検討しました。Luceneとelastisearchをググリングすると、これらの既成のパッケージに関する優れた情報が得られます。

最終的に、SQLと一緒に実行する独自の自社検索エンジンを開発しました。これにより、音声検索(ダブルメタフォン)を実装し、soundexに沿ってレーベンシュテイン計算を使用して関連性を確立できました。多くのソリューションではやり過ぎですが、私たちのユースケースでは努力する価値があります。今でも、NVIDIA GPUを使用してcuda検索を実行するオプションがありますが、これはまったく新しい一連の頭痛と眠れない夜を表しています。これらすべての関連性は、検索が実行される頻度と、検索をどの程度反応させる必要があるかによって異なります。


1

フルテキストインデックスには、いくつかの制限があります。インデックスが完全な「部分」であると検出した単語にワイルドカードを使用できますが、それでも単語の末尾部分に制限されます。そのためCONTAINS(Name, '"Azimut*"')、使用できますが使用できませんCONTAINS(Name, '"zimuth*"')

Microsoftのドキュメントから:

接頭辞の用語が句である場合、その句を構成する各トークンは個別の接頭辞の用語と見なされます。プレフィックス用語で始まる単語を含むすべての行が返されます。たとえば、「軽いパン*」という接頭辞は、「軽いパン」、「軽くパン」、「軽いパン」のテキストを含む行を検索しますが、「軽くトーストしたパン」を返しません。

タイトルに示されているメールのドットは、主要な問題ではありません。たとえば、これは機能します:

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

この場合、インデックスはメール文字列全体を「gmail」と「gmail.com」だけでなく、有効なものとして識別します。ただ "sms"は無効です。

最後の例も同様です。電話番号の一部にはインデックスが付けられます(たとえば、666-22-11および999-666-22-11)。ただし、ハイフンの削除は、インデックスが認識する文字列ではありません。それ以外の場合、これは機能します:

SELECT * FROM NewCompanies 
WHERE CONTAINS((Phone, Contacts1, Contacts2, Contacts3, Contacts4), '"666-22-11*"')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.