Ricerca di Testo completo in Linq

Non c’è ricerca di testo completo costruito in Linq e ci non sembrano essere molti post sull’argomento, così ho avuto un gioco intorno e si avvicinò con questo metodo per il mio utlity classe:

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

E aggiunto questo wrapper per ogni parziale Linq classe dove c’è un indice di testo completo

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

Così ora posso fare le ricerche full text con roba pulita come

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

Sto assumendo solo una ricerca di base è necessario per il presente. Chiunque può migliorare su questo? È possibile l’applicazione di un metodo di estensione e di evitare il wrapper?

  • Un pericoloso/unoptimal problema per quanto riguarda la tua domanda è che l’ .Skip().Prendere() sarà eseguita clientside, non serverside. Quindi, se fate un FTS che restituisce 10^6 risultati e si desidera solo i primi 10, 10^6 di loro saranno restituiti dal database, e solo allora si esegue il filtraggio.
  • Aye, su un set di dati che grande mi verrebbe di pensare ad un’altra tecnica 😉
  • Possibile duplicato di È possibile utilizzare la Ricerca Full Text (FTS) con LINQ?

 

7 Replies
  1. 3

    Ero molto frustrato per la mancanza di chiari esempi… soprattutto quando ci sono potenzialmente grandi insiemi di dati e di paging è necessario. Così, ecco un esempio che si spera comprende tutto il necessario 🙂

    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)

    Godere,
    Patrick

  2. 3

    Io uso un po ‘ hack utilizzando Fornitore Di Wrapper tecniche. Ho un codice in c# che riscrivere la parola magica in SQL con FTS ricerca per MS SQL (è possibile regolare per qualsiasi server che ti piace).

    se si dispone di contesto classe MyEntities, creare sottoclasse come

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

    E quindi utilizzarlo come questo:

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

    Un finale leggermente migliore metodo di (prende il rank in vigore) utilizzando 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);
  4. 0

    Ho cercato di risolvere il problema esatto. Mi piace scrivere la mia logica SQL nel mio LINQtoSQL, ma ho bisogno di un modo per fare Ricerca a Testo integrale. adesso sto usando solo le funzioni SQL e quindi chiamando le funzioni definite dall’utente sulla stessa linea della query linq. non sono sicuro se questo è il modo più efficace. cosa ne pensate voi ragazzi?

  5. -3

    Si può solo fare qualcosa di simile a questo

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

    Notare che il nome del Tag.Contiene anche il SQLMethods.Come fare un uso

    using System.Data.Linq.SqlClient;

    per ottenere l’accesso a che SQLMethods.

    • Che .contiene traduce in un LIKE ‘%TAG%, che non è ottimale.
    • LIKE non è un FULLTEXT di ricerca.
  6. -4

    dswatik – il motivo per cui vogliono testo completo della ricerca è che .contiene traduce

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

    Che ignora tutti gli indici ed è orribile su un grande tavolo.

Lascia un commento