2009-02-19 11 views
21

No hay búsqueda de texto completo integrada en LINQ y allí no parece haber muchos mensajes sobre el tema, así que tenían un juego alrededor y se acercó con este método para mi clase utlity:búsqueda de texto completo en 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); 
} 

y ha añadido este contenedor a cada clase parcial LINQ donde hay un índice de texto completo

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

Así que ahora puedo hacer búsquedas de texto completo con cosas muy buenas como

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

Supongo que solo es necesaria una búsqueda muy básica en este momento. ¿Alguien puede mejorar en esto? ¿Es posible implementarlo como método de extensión y evitar el contenedor?

+4

Una cuestión peligrosa/unoptimal con respecto a su consulta es que el Saltee(). Take() se realizará en el lado del cliente, no en el lado del servidor. Por lo tanto, si realiza un FTS que devuelve 10^6 resultados y desea obtener los primeros 10, se devolverán todos los 10^6 de la base de datos, y solo entonces realizará el filtrado. –

+0

Sí, en un conjunto de datos tan grande, estaría considerando otra técnica;) – ctrlalt3nd

+0

Posible duplicado de [¿Es posible utilizar Búsqueda de texto completo (FTS) con LINQ?] (Http://stackoverflow.com/questions/224475/is -it-possible-to-use-full-text-search-fts-with-linq) –

Respuesta

-1

Usted sólo puede hacer algo como esto

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

Aviso donde TagName.Contains también la SQLMethods.Like acaba de hacer un uso

using System.Data.Linq.SqlClient; 

para tener acceso a ese SQLMethods.

+2

Eso .contains se traduce en un LIKE '% TAGNAME%', que es subóptimo. –

+0

'LIKE' no es una búsqueda' FULLTEXT'. – Dementic

-2

dswatik - la razón para querer buscar con texto completo es que .contains se traduce en

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

que ignora cualquier índice y es horrible en una mesa grande.

2

Un método más agradable levemente (tarda rango en vigor) usando 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

que he estado tratando de resolver el problema exacto. Me gusta escribir mi lógica de SQL en mi LINQtoSQL pero necesitaba una forma de hacer la búsqueda de texto completo. en este momento solo estoy usando funciones de SQL y luego llamando a las funciones definidas por el usuario en línea de las consultas de linq. no estoy seguro si esa es la forma más eficiente. ¿Qué piensan ustedes?

3

estaba bastante frustrado con la falta de ejemplos claros ... especialmente cuando hay potencialmente se necesitan grandes conjuntos de datos y paginación. Por lo tanto, aquí hay un ejemplo que espero abarca todo lo que pueda necesitar :-)

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) 

Enjoy, Patrick

2

uso un pequeño programa usando técnicas Provider Wrapper. Tengo un código C# que reescribe la palabra mágica en SQL con búsqueda FTS para MS SQL (puede ajustar para cualquier servidor que desee).

si tiene MyEntities clase de contexto, crear subclase como

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

    } 

y luego usarlo como esto:

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