linq linq-to-sql search full-text-search

Búsqueda de texto completo en Linq



linq-to-sql search (7)

No hay una búsqueda de texto completo en Linq y parece que no hay muchas publicaciones sobre el tema, así que tuve un juego y se me ocurrió este método para mi clase de utlity:

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 agregué este envoltorio a cada clase parcial de 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 interesantes como

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

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


Estaba bastante frustrado con la falta de ejemplos claros ... especialmente cuando hay conjuntos de datos potencialmente grandes y se necesita paginación. Entonces, aquí hay un ejemplo que con suerte 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)

Disfruta, Patrick


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 manera más eficiente. ¿Qué piensan ustedes?



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

Observe dónde TagName.Contains también el SQLMethods.Me gusta solo hacer un uso

using System.Data.Linq.SqlClient;

para obtener acceso a ese SQLMethods.


Un método ligeramente más agradable (toma rango en el efecto) 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);


Utilizo un pequeño truco usando las técnicas de Contenedor Envolvente . Tengo código de CA # que reescribe la palabra mágica en SQL con búsqueda FTS para MS SQL (puede ajustar para cualquier servidor que desee).

si tienes MyEntities de clase de contexto, crea una 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 úsalo así:

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


dswatik: la razón para querer buscar texto completo es que .contains traduce a

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

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