LINQで全文検索(FTS)を使用することはできますか?
-
03-07-2019 - |
質問
.NET Framework 3.5を使用して、LINQでFTSを使用できるかどうか疑問に思います。まだ有用なものが見つからなかったドキュメントを検索しています。
これに関する経験はありますか?
解決
はい。ただし、最初にSQLサーバー関数を作成し、デフォルトでLINQがlikeを使用するように呼び出す必要があります。
この詳細を説明するブログ投稿ですが、これは抜粋です:
機能させるには、テーブル値関数を作成する必要があります。 渡すキーワードに基づいたCONTAINSTABLEクエリにすぎません で、
create function udf_sessionSearch (@keywords nvarchar(4000)) returns table as return (select [SessionId],[rank] from containstable(Session,(description,title),@keywords))
この関数をLINQ 2 SQLモデルに追加すると、彼はあなたに 次のようなクエリを記述できるようになりました。
var sessList = from s in DB.Sessions join fts in DB.udf_sessionSearch(SearchText) on s.sessionId equals fts.SessionId select s;
他のヒント
いいえ。 LINQ To SQLは全文検索をサポートしていません。
とはいえ、FTSを利用するストアドプロシージャを使用して、そこからLINQ To SQLクエリにデータをプルさせることができます。
私はそうは思わない。フィールドに「含む」を使用できますが、 LIKE
クエリのみを生成します。全文を使用する場合は、ストアドプロシージャを使用してクエリを実行し、LINQに返すことをお勧めします
結合を作成せず、C#コードを単純化する場合は、SQL関数を作成して「from」で使用できます。句:
CREATE FUNCTION ad_Search
(
@keyword nvarchar(4000)
)
RETURNS TABLE
AS
RETURN
(
select * from Ad where
(CONTAINS(Description, @keyword) OR CONTAINS(Title, @keyword))
)
DBMLを更新した後、linqで使用します:
string searchKeyword = "word and subword";
var result = from ad in context.ad_Search(searchKeyword)
select ad;
これにより、次のような単純なSQLが生成されます。
SELECT [t0].ID, [t0].Title, [t0].Description
FROM [dbo].[ad_Search](@p0) AS [t0]
これは、ad_Search関数の実装からわかるように、いくつかの列による検索で機能します。
いいえ、全文検索はSQLサーバーに非常に固有のものです(テキストは単語でインデックス付けされ、クエリはこのインデックスをヒットするのに対して、文字配列を走査します)。 Linqはこれをサポートしていません。.Contains()呼び出しは管理されていない文字列関数にヒットしますが、インデックス作成の恩恵は受けません。
SQL Serverの CONTAINS 専用で、ワイルドカード列は使用しないプロトタイプを作成しました。達成することは、通常のLINQ関数のように CONTAINS を使用することです:
var query = context.CreateObjectSet<MyFile>()
.Where(file => file.FileName.Contains("pdf")
&& FullTextFunctions.ContainsBinary(file.FileTable_Ref.file_stream, "Hello"));
必要なもの:
1. CONTAINS キーワードをサポートするコードおよびEDMXの関数定義。
2.CONTAINSは関数ではなく、デフォルトでは生成されたSQLは結果を bit として扱うため、EFProviderWrapperToolkit / EFTracingProviderでEF SQLを書き換えます。
しかし:
1.Containsは実際には関数ではないため、ブール値の結果を選択することはできません。条件でのみ使用できます。
2。クエリに特殊文字を含むパラメーター化されていない文字列が含まれている場合、以下のSQL書き換えコードは破損する可能性があります。
プロトタイプのソース
関数定義:(EDMX)
edmx:StorageModels / Schemaの下
<Function Name="conTAINs" BuiltIn="true" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" ReturnType="bit" Schema="dbo">
<Parameter Name="dataColumn" Type="varbinary" Mode="In" />
<Parameter Name="keywords" Type="nvarchar" Mode="In" />
</Function>
<Function Name="conTAInS" BuiltIn="true" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" ReturnType="bit" Schema="dbo">
<Parameter Name="textColumn" Type="nvarchar" Mode="In" />
<Parameter Name="keywords" Type="nvarchar" Mode="In" />
</Function>
PS:charsの奇妙なケースは、異なるパラメータータイプ(varbinaryとnvarchar)で同じ関数を有効にするために使用されます
関数定義:(コード)
using System.Data.Objects.DataClasses;
public static class FullTextFunctions
{
[EdmFunction("MyModel.Store", "conTAINs")]
public static bool ContainsBinary(byte[] dataColumn, string keywords)
{
throw new System.NotSupportedException("Direct calls are not supported.");
}
[EdmFunction("MyModel.Store", "conTAInS")]
public static bool ContainsString(string textColumn, string keywords)
{
throw new System.NotSupportedException("Direct calls are not supported.");
}
}
PS:&quot; MyModel.Store&quot; はedmx:StorageModels / Schema / @ Namespaceの値と同じです
EF SQLの書き換え:(EFProviderWrapperToolkitによる)
using EFProviderWrapperToolkit;
using EFTracingProvider;
public class TracedMyDataContext : MyDataContext
{
public TracedMyDataContext()
: base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
"name=MyDataContext", "EFTracingProvider"))
{
var tracingConnection = (EFTracingConnection) ((EntityConnection) Connection).StoreConnection;
tracingConnection.CommandExecuting += TracedMyDataContext_CommandExecuting;
}
protected static void TracedMyDataContext_CommandExecuting(object sender, CommandExecutionEventArgs e)
{
e.Command.CommandText = FixFullTextContainsBinary(e.Command.CommandText);
e.Command.CommandText = FixFullTextContainsString(e.Command.CommandText);
}
private static string FixFullTextContainsBinary(string commandText, int startIndex = 0)
{
var patternBeg = "(conTAINs(";
var patternEnd = ")) = 1";
var exprBeg = commandText.IndexOf(patternBeg, startIndex, StringComparison.Ordinal);
if (exprBeg == -1)
return commandText;
var exprEnd = FindEnd(commandText, exprBeg + patternBeg.Length, ')');
if (commandText.Substring(exprEnd).StartsWith(patternEnd))
{
var newCommandText = commandText.Substring(0, exprEnd + 2) + commandText.Substring(exprEnd + patternEnd.Length);
return FixFullTextContainsBinary(newCommandText, exprEnd + 2);
}
return commandText;
}
private static string FixFullTextContainsString(string commandText, int startIndex = 0)
{
var patternBeg = "(conTAInS(";
var patternEnd = ")) = 1";
var exprBeg = commandText.IndexOf(patternBeg, startIndex, StringComparison.Ordinal);
if (exprBeg == -1)
return commandText;
var exprEnd = FindEnd(commandText, exprBeg + patternBeg.Length, ')');
if (exprEnd != -1 && commandText.Substring(exprEnd).StartsWith(patternEnd))
{
var newCommandText = commandText.Substring(0, exprEnd + 2) + commandText.Substring(exprEnd + patternEnd.Length);
return FixFullTextContainsString(newCommandText, exprEnd + 2);
}
return commandText;
}
private static int FindEnd(string commandText, int startIndex, char endChar)
{
// TODO: handle escape chars between parens/squares/quotes
var lvlParan = 0;
var lvlSquare = 0;
var lvlQuoteS = 0;
var lvlQuoteD = 0;
for (var i = startIndex; i < commandText.Length; i++)
{
var c = commandText[i];
if (c == endChar && lvlParan == 0 && lvlSquare == 0
&& (lvlQuoteS % 2) == 0 && (lvlQuoteD % 2) == 0)
return i;
switch (c)
{
case '(':
++lvlParan;
break;
case ')':
--lvlParan;
break;
case '[':
++lvlSquare;
break;
case ']':
--lvlSquare;
break;
case '\'':
++lvlQuoteS;
break;
case '"':
++lvlQuoteD;
break;
}
}
return -1;
}
}
EFProviderWrapperToolkitを有効にする:
nugetで取得した場合、これらの行をapp.configまたはweb.configに追加する必要があります。
<system.data>
<DbProviderFactories>
<add name="EFTracingProvider" invariant="EFTracingProvider" description="Tracing Provider Wrapper" type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
<add name="EFProviderWrapper" invariant="EFProviderWrapper" description="Generic Provider Wrapper" type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
</DbProviderFactories>
</system.data>