A “Composable” Full Text Search with a Code First Model

前端 未结 2 1021
醉梦人生
醉梦人生 2020-12-08 11:55

UPDATE 18 Sep 2013

It looks like there isn\'t an easy way to do this. I\'m holding out for a solution that involves some extension to Entity Framewo

相关标签:
2条回答
  • 2020-12-08 12:29

    I had this same issue recently: EF 5 Code First FTS Queriable

    Let me extend that post.

    • Your first option was mine first as well - using SqlQuery I also needed to do more filtering, so instead of always writing full sql I used QueryBuilder, to which I made some changes and added more functions to fit my needs(I could upload it somewhere if needed): QueryBuilder

    • After I have found another idea which I implemented. Someone already mention it here, and that is to use SqlQuery that will return HashSet of Ids and that you can use it in EF queries with Contains. This is better but not most optimal since you need 2 queries and Id list in memory. Example:

          public IQueryable<Company> FullTextSearchCompaniesByName(int limit, int offset, string input, Guid accountingBureauId, string orderByColumn)
      {
          FtsQueryBuilder ftsQueryBuilder = new FtsQueryBuilder();
      
          ftsQueryBuilder.Input = FtsQueryBuilder.FormatQuery(input);
          ftsQueryBuilder.TableName = FtsQueryBuilder.GetTableName<Company>();
          ftsQueryBuilder.OrderByTable = ftsQueryBuilder.TableName;
          ftsQueryBuilder.OrderByColumn = orderByColumn;
          ftsQueryBuilder.Columns.Add("CompanyId");
      
          if (accountingBureauId != null && accountingBureauId != Guid.Empty)
              ftsQueryBuilder.AddConditionQuery<Guid>(Condition.And, "" , @"dbo.""Company"".""AccountingBureauId""", Operator.Equals, accountingBureauId, "AccountingBureauId", "");
      
          ftsQueryBuilder.AddConditionQuery<bool>(Condition.And, "", @"dbo.""Company"".""Deleted""", Operator.Equals, false, "Deleted", "");
      
          var companiesQuery = ftsQueryBuilder.BuildAndExecuteFtsQuery<Guid>(Context, limit, offset, "Name");
          TotalCountQuery = ftsQueryBuilder.Total;
          HashSet<Guid> companiesIdSet = new HashSet<Guid>(companiesQuery);
          var q = Query().Where(a => companiesIdSet.Contains(a.CompanyId));
          return q;
      }
      
    • However EF 6 now has something called Interceptors that can be used to implement queriable FTS, and it is pretty simple and generic(last post): EF 6 Interceptors for FTS. I have tested this and it works fine.

    !! REMARK: EF Code First, even with version 6, does not support Custom Stored Procedures. There are only some for predefined CUD operations if I understood it well: Code First Insert/Update/Delete Stored Procedure Mapping, so it can't be done with it.

    Conclusion: if you can use EF 6 go for third options, is gives all you need. If you are stucked with EF 5 or less, second option is better then first but not most optimal.

    0 讨论(0)
  • 2020-12-08 12:41

    It is not perfect, but you can accomplish what you are after with 2 calls to the database. The first call would retrieve a list of matching key's from CONTAINSTABLE and then the second call would be your composable query utilizing the IDs that you returned from the first call.

    //Get the Keys from the FTS
    var ids = context.Database.SqlQuery<int>( 
              "Select [KEY] from CONTAINSTABLE([SomeEntity], [BigText], @searchTerm)", 
              new object[] { new SqlParameter("@searchTerm", searchTerm) });
    
    //Use the IDs as an initial filter on the query
    var composablequery = context.SomeEntities.Where(d => ids.Contains(d.Id));
    
    //add on whatever other parameters were captured to the 'composablequery' variable
    composablequery = composablequery.Where(.....)
    
    0 讨论(0)
提交回复
热议问题