2009-02-19 12 views
21

Es gibt keine Volltextsuche in Linq gebaut und es scheint nicht viele Beiträge zu dem Thema zu sein, so hatte ich ein Spiel um und kamen mit dieser Methode für meine utlity Klasse bis:Volltextsuche in Linq

public static IEnumerable<TSource> GenericFullTextSearch<TSource>(string text, MyDataContext context) 
{ 
    //Find LINQ Table attribute 
    object[] info = typeof(TSource).GetCustomAttributes(typeof(System.Data.Linq.Mapping.TableAttribute), true); 
    //Get table name 
    String table = (info[0] as System.Data.Linq.Mapping.TableAttribute).Name; 
    //Full text search on that table 
    return context.ExecuteQuery<TSource>(String.Concat("SELECT * FROM ", table, " WHERE CONTAINS(*, {0})"), text); 
} 

Und hinzugefügt, um diese Wrapper jeden Teil Linq Klasse, wo es einen Volltextindex

public static IEnumerable<Pet> FullTextSearch(string text, MyDataContext context) 
{ 
    return (LinqUtilities.GenericFullTextSearch<Pet>(text, context) as IEnumerable<Pet>); 
} 

So jetzt habe ich eine Volltextsuche mit netten Dingen wie

var Pets = Pet.FullTextSearch(helloimatextbox.Text, MyDataContext).Skip(10).Take(10); 
tun kann

Ich gehe davon aus, dass derzeit nur eine sehr einfache Suche notwendig ist. Kann jemand das verbessern? Ist es möglich, als Erweiterungsmethode zu implementieren und den Wrapper zu vermeiden?

+4

Eine gefährliche/unoptimal Frage in Bezug auf Ihre Frage ist, dass die .skip(). Take() wird clientseitig, nicht serverseitig ausgeführt. Wenn Sie also ein FTS machen, das 10^6 Ergebnisse liefert und Sie wollen nur die ersten 10 haben, werden alle 10^6 von der Datenbank zurückgegeben, und nur dann führen Sie die Filterung durch. –

+0

Aye, bei einem so großen Datensatz würde ich eine andere Technik in Erwägung ziehen;) – ctrlalt3nd

+0

Mögliches Duplikat von [Ist es möglich, die Volltextsuche (FTS) mit LINQ zu verwenden?] (Http://stackoverflow.com/questions/224475/is -it-possible-to-use-Volltextsuche-fts-with-linq) –

Antwort

-1

können Sie tun nur so etwas wie dieser

var results = (from tags in _dataContext.View_GetDeterminationTags 
        where tags.TagName.Contains(TagName) || 
        SqlMethods.Like(tags.TagName,TagName) 
        select new DeterminationTags 
        { 
         Row = tags.Row, 
         Record = tags.Record, 
         TagID = tags.TagID, 
         TagName = tags.TagName, 
         DateTagged = tags.DateTagged, 
         DeterminationID = tags.DeterminationID, 
         DeterminationMemberID = tags.DeterminationMemberID, 
         MemberID = tags.MemberID, 
         TotalTagged = tags.TotalTagged.Value 
        }).ToList(); 

Hinweis, wo TagName.Contains auch die SQLMethods.Like einfach ein

using System.Data.Linq.SqlClient; 

mit Zugang zu diesem SQLMethods zu gewinnen.

+2

Das .contains übersetzt in eine LIKE '% TAGNAME%', die suboptimal ist. –

+0

'LIKE' ist keine' FULLTEXT' Suche. – Dementic

-2

dswatik - der Grund für die Volltextsuche zu wollen, ist, dass .contains zu

SELECT * FROM MYTABLE WHERE COLUMNNAME LIKE '%TEXT%' 

Welche alle Indizes ignoriert übersetzt und ist schrecklich auf einem großen Tisch.

2

A slighty schönere Methode (nimmt Rang in Kraft) mit CONTAINS

String pkey = context.Mapping.GetTable(typeof(TSource)).RowType.DataMembers.SingleOrDefault(x => x.IsPrimaryKey).Name; 
string query = String.Concat(@"SELECT * 
    FROM ", table, @" AS FT_TBL INNER JOIN 
    CONTAINSTABLE(", table, @", *, {0}) AS KEY_TBL 
    ON FT_TBL.", pkey, @" = KEY_TBL.[KEY] 
    ORDER BY KEY_TBL.[RANK] DESC"); 
return context.ExecuteQuery<TSource>(query, text); 
-1

Ich habe versucht, das genaue Problem zu lösen. Ich schreibe gerne meine SQL-Logik in meine LINQtoSQL, aber ich brauchte eine Möglichkeit, Volltextsuche zu machen. Im Moment benutze ich nur SQL-Funktionen und dann die benutzerdefinierten Funktionen inline der linq-Abfragen aufrufen. nicht sicher, ob das der effizienteste Weg ist. Was denkt ihr?

3

ich mit dem Mangel an klaren Beispielen ziemlich frustriert war ... vor allem, wenn es potenziell große Datenmengen und Paging wird benötigt. So, hier ist ein Beispiel, das hoffentlich alles umfasst die Sie benötigen :-)

create function TS_projectResourceSearch 
    ( @KeyStr nvarchar(4000), 
     @OwnId int, 
     @SkipN int, 
     @TakeN int) 
    returns @srch_rslt table (ProjectResourceId bigint not null, Ranking int not null) 
    as 
    begin 

     declare @TakeLast int 
     set @TakeLast = @SkipN + @TakeN 
     set @SkipN = @SkipN + 1 

     insert into @srch_rslt 
     select pr.ProjectResourceId, Ranking 
     from 
     (
      select t.[KEY] as ProjectResourceId, t.[RANK] as Ranking, ROW_NUMBER() over (order by t.[Rank] desc) row_num 
      from containstable(ProjectResource,(ResourceInfo, ResourceName), @KeyStr) 
      as t   
     ) as r 
     join ProjectResource pr on r.ProjectResourceId = pr.ProjectResourceId 
     where (pr.CreatorPersonId = @OwnId 
      or pr.ResourceAvailType < 40) 
      and r.row_num between @SkipN and @TakeLast 
     order by r.Ranking desc 

     return 
    end 
    go 


    select * from ts_projectResourceSearch(' "test*" ',1002, 0,1) 

Genießen, Patrick

2

Ich benutze einen kleinen Hack Provider Wrapper Techniken. Ich habe einen C# -Code, der Zauberwort in SQL mit FTS-Suche nach MS SQL umschreibt (Sie können für jeden Server anpassen, den Sie mögen).

, wenn Sie Kontextklasse MyEntities haben, erstellen Sie Unterklasse wie

public class MyEntitiesWithWrappers : MyEntities 
{ 
    private IEFTraceListener listener; 
    public string FullTextPrefix = "-FTSPREFIX-"; 

    public MyEntitiesWithWrappers(): this("name=MyEntities") 
    { 
    } 

    public MyEntitiesWithWrappers(string connectionString) 
     : base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(connectionString,"EFTracingProvider")) 
    { 
     TracingConnection.CommandExecuting += RewriteFullTextQuery; 
    } 

    /// <summary> 
    /// Rewrites query that contains predefined prefix like: where n.NOTETEXT.Contains(Db.FullTextPrefix + text) with SQL server FTS 
    /// To be removed when EF will support FTS 
    /// </summary> 
    /// <param name="o"></param> 
    /// <param name="args"></param> 
    public void RewriteFullTextQuery(object o, CommandExecutionEventArgs args) 
    { 
     var text = args.Command.CommandText; 
     for (int i = 0; i < args.Command.Parameters.Count; i++) 
     { 
      DbParameter parameter = args.Command.Parameters[i]; 
      if (parameter.DbType.In(DbType.String, DbType.AnsiString, DbType.StringFixedLength, DbType.AnsiStringFixedLength)) 
      { 
       if (parameter.Value == DBNull.Value) 
        continue; 
       var value = (string) parameter.Value; 
       parameter.Size = 4096; 
       if (value.IndexOf(FullTextPrefix) >= 0) 
       { 
        value = value.Replace(FullTextPrefix, ""); // remove prefix we added n linq query 
        value = value.Substring(1, value.Length-2); // remove %% escaping by linq translator from string.Contains to sql LIKE 
        parameter.Value = value; 
        args.Command.CommandText = Regex.Replace(text, 
         string.Format(@"\(\[(\w*)\].\[(\w*)\]\s*LIKE\s*@{0}\s?(?:ESCAPE '~')\)", parameter.ParameterName), 
         string.Format(@"contains([$1].[$2], @{0})", parameter.ParameterName)); 
       } 
      } 
     } 
    } 

    } 

Und es dann wie folgt verwenden:

var fullTextSearch = Db.FullTextPrefix + textToSearch; 
var q = Db.Notes.Where(n => !n.Private && n.NoteText.Contains(fullTextSearch));