在Linq/ODataController中使用全文搜索

本文关键字:文搜索 搜索 Linq ODataController | 更新日期: 2023-09-27 18:27:14

我正在开发一个通过OData提供数据的应用程序。我使用ASP.Net和ODataControllers通过EF进行查询——数据由SQLServer数据库支持。

在可视化这些数据的前端网站上,用户可以搜索结果——在前端动态创建$过滤器,并发送OData请求(允许服务器端过滤)。

在支持最终通过OData提供的数据的数据库表上,启用了全文搜索,但它出现在管道OData filter->Linq query->SQL query中,使用LIKE搜索而不是全文Contains()方法。

有没有任何人知道的方法可以以一种相当优雅的方式使用全文功能?

据推测,我可以使用自定义IODataPathHandler和/或IODataPathTemplateHandler和/或其他一些东西来拦截管道中的点,但如果可能的话,我宁愿尽量避免这种情况。

有什么建议吗?

在Linq/ODataController中使用全文搜索

OData的contains函数用于执行简单的子字符串匹配。OData规范定义了用于全文搜索的$search查询选项,但Web API当前不支持$search。(这是一个悬而未决的问题。)

您的最佳选择可能是自定义查询选项(例如,/Customers?fulltextsearch=contains(Name, 'Arianne')),但您必须编写所有代码来解析该选项,等等。

如果您决定将OData contains映射到T-SQL CONTAINS,那么您将需要截取Linq到实体的转换。查看现有ContainsTranslator的源代码并进行反向工作。

为此使用拦截器和自定义EnableQueryAttribute:

  1. 如本文所述定义一个FtsInterceptor类,并将其添加到您的上下文DbInterception.Add(new FtsInterceptor())
  2. 定义EnableQueryAttribute类的子类,并覆盖ApplyQuery方法,为OData contains函数的所有参数添加FullTextPrefix(-FTSPREFIX-):

    public class FullTextSearchAttribute : EnableQueryAttribute
    {
        public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
        {
            if (queryOptions.Filter == null)
               return queryOptions.ApplyTo(queryable);
            const string pattern = "contains''([%20]*[^%27]*[%20]*,[%20]*%27(?<Value>[^%27]*)";
            var matchEvaluator = new MatchEvaluator(match =>
            {
                var value = match.Groups["Value"].Value;
                return match.Value.Replace($"%27{value}", $"%27-FTSPREFIX-{value}");
            });
            var request = new HttpRequestMessage(HttpMethod.Get, 
                      Regex.Replace(queryOptions.Request.RequestUri.AbsoluteUri,
                          pattern,
                          matchEvaluator,
                          RegexOptions.IgnoreCase));
            return new ODataQueryOptions(queryOptions.Context, request).ApplyTo(queryable);
        }
    }
    
  3. 使用代码中的属性:

    [FullTextSearchAttribute]
    public IQueryable<YourDomainClass> Get()
    {
        //Query 
    }