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?
La mejor solución es usar una función de tabla en línea valorada en sql y agregarla a su modelo
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.