2009-02-19 31 views
21

Không có tìm kiếm văn bản đầy đủ xây dựng vào LINQ và có dường như không có nhiều bài viết về đề tài này vì vậy tôi đã một vở kịch xung quanh và đã đưa ra phương pháp này cho các lớp học utlity tôi:Full Text Search trong 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); 
} 

Và thêm wrapper này đến từng lớp LINQ một phần, nơi có một chỉ mục văn bản đầy đủ

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

Vì vậy, bây giờ tôi có thể thực hiện tìm kiếm văn bản đầy đủ với công cụ hữu ích như

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

Tôi giả định chỉ có một tìm kiếm rất cơ bản là cần thiết hiện nay. Có ai có thể cải thiện điều này không? Có thể thực hiện như một phương pháp mở rộng và tránh các wrapper?

+4

Một nguy hiểm/vấn đề unoptimal về truy vấn của bạn là .Skip(). Take() sẽ được thực hiện clientside, chứ không phải serverside. Vì vậy, nếu bạn thực hiện một FTS trả về 10^6 kết quả và bạn muốn chỉ có 10 kết quả đầu tiên, tất cả 10^6 trong số chúng sẽ được trả lại từ cơ sở dữ liệu và chỉ khi đó bạn mới thực hiện việc lọc. –

+0

Aye, trên tập dữ liệu lớn tôi sẽ xem xét kỹ thuật khác;) – ctrlalt3nd

+0

Có thể trùng lặp của [Có thể sử dụng Tìm kiếm Toàn văn (FTS) với LINQ không?] (Http://stackoverflow.com/questions/224475/is -it-possible-to-use-full-text-search-fts-với-linq) –

Trả lời

-1

Bạn chỉ có thể làm điều gì đó như Thông báo này

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(); 

nơi TagName.Contains cũng là SQLMethods.Like chỉ cần làm một bằng

using System.Data.Linq.SqlClient; 

để đạt được quyền truy cập vào SQLMethods đó.

+2

Điều đó .contains dịch thành LIKE '% TAGNAME%', là tối ưu. –

+0

'LIKE' không phải là tìm kiếm' FULLTEXT'. – Dementic

-2

dswatik - lý do muốn tìm kiếm văn bản đầy đủ là Chứa dịch để

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

nào bỏ qua bất kỳ chỉ số và là khủng khiếp trên một chiếc bàn lớn.

2

Một phương pháp đẹp hơn slighty (mất thứ hạng đầu có hiệu lực) sử dụng CONTAINSTABLE

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

Tôi đã cố gắng để giải quyết vấn đề chính xác. Tôi thích viết logic SQL của mình trong LINQtoSQL nhưng tôi cần một cách để thực hiện tìm kiếm toàn văn bản. ngay bây giờ tôi chỉ sử dụng các hàm SQL và sau đó gọi các hàm do người dùng định nghĩa trong các truy vấn LINQ. không chắc chắn nếu đó là cách hiệu quả nhất. các bạn nghĩ sao?

6

Các giải pháp thú vị nhất là sử dụng một bảng nội tuyến chức năng có giá trị trong sql và thêm nó vào mô hình của bạn

http://sqlblogcasts.com/blogs/simons/archive/2008/12/18/LINQ-to-SQL---Enabling-Fulltext-searching.aspx

+1

Điều này cũng đòi hỏi một udf cho mỗi bảng từ những gì tôi có thể nhìn thấy, giải pháp OPs nên làm việc với tất cả các bảng. Cảm ơn các bài mặc dù – Smudge202

3

tôi đã khá thất vọng với việc thiếu các ví dụ rõ ràng ... đặc biệt là khi có khả năng bộ dữ liệu lớn và phân trang là cần thiết. Vì vậy, đây là một ví dụ mà hy vọng tất cả mọi thứ bao gồm bạn có thể cần :-)

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) 

Thưởng thức, Patrick

2

tôi sử dụng một ít hack sử dụng Provider Wrapper kỹ thuật. Tôi có một mã C# viết lại từ ma thuật trong SQL với tìm kiếm FTS cho MS SQL (bạn có thể điều chỉnh cho bất kỳ máy chủ nào bạn thích).

nếu bạn có MyEntities lớp bối cảnh, tạo lớp con như

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)); 
       } 
      } 
     } 
    } 

    } 

Và sau đó sử dụng nó như thế này:

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