全文索引中的关键词搜索导致搜索结果无法返回

本文关键字:搜索结果 返回 关键词 搜索 全文索引 | 更新日期: 2023-09-27 18:13:23

我们正在对公司名称字段使用全文索引搜索。

我们使用EF作为数据层,我被要求不要使用存储过程。

这是我的数据访问层的方法:

public Task<List<Company>> SearchByName(string searchText)
{
        return DataContext.Company.SqlQuery(
            "select CompanyId AS Id, * from Company.Company AS c where contains(c.Name, @SearchText)",
            new SqlParameter("@SearchText", ParseSearchTextForMultiwordSearch(searchText)))
        .ToListAsync();
}

我们想在搜索中拆分单词,然后将它们连接在一起进行and搜索。这意味着像"我的公司"这样的查询实际上是根据索引搜索"我的"answers"公司"。

这段代码为上面的select查询合并术语。

public string ParseSearchTextForMultiwordSearch(string searchText)
{
    var words = GetValidSearchTerms(searchText);
    var quotedWords = words.Select(x => string.Format("'"{0}*'"", x));
    return string.Join(" AND ", quotedWords);
}

一切都很好,直到你开始添加"关键字"。到目前为止,我们已经计算出和、或、或不包含在搜索中返回0个结果。没有错误,只是没有结果。

这是我们将某些单词"列入黑名单"的方法,因此它们被排除在搜索查询之外。

private static List<string> GetValidSearchTerms(string searchText)
{
    //AND and OR are keywords used by SQL Server Full Text Indexing.
    var blacklist = new string[] {
        "and",
        "or",
        "not"
    };
    //Filter them out here
    var words = searchText.Split(' ');
    var validWords = words.Where(x => !blacklist.Contains(x));
    return validWords.ToList();
}

问题是,我们刚刚发现了另一个"关键字"似乎导致了这个问题。"Do"不会产生任何结果。我可以把它添加到黑名单中,但随着这个东西的增长,我开始觉得处理它的方式是错误的。

有更好的方法来处理这个问题吗?

编辑:

其他几个场景

如果我根本不处理搜索字符串,那么搜索单词"not"将导致错误"Null或空全文谓词"。

同样的场景,只是按原样应用字符串,如果我创建一个公司"company Do Not Delete",任何具有Do或Not的字符串版本都返回0结果。

全文索引中的关键词搜索导致搜索结果无法返回

自从我发布这个问题以来已经有一段时间了,经过几次迭代,我想出了一些适合我们需求的搜索逻辑。

首先,我们有一个业务规则,它要求搜索包含一个&号。全文索引似乎省略了&,使得返回的结果不正确。因此,我必须对任何&搜索使用类似语句。

我留下我的代码做做如上所述,它解析出单词的黑名单,并尝试包含搜索。如果由于任何原因失败,我将执行FREETEXT搜索。

public async Task<List<Company>> SearchByName(string searchText)
{
    var results = new List<Company>();
    if (string.IsNullOrWhiteSpace(searchText))
        return results;
    if (searchText.IndexOf("&") >= 0)
    {
        var likeQuery = string.Format("%{0}%", searchText);
        results = await DataContext.Company.SqlQuery("SELECT CompanyId AS Id, IsEligible AS IsReadOnly, *" +
                                "  FROM Company.Company AS con" +
                                "  WHERE con.Name LIKE @SearchText",
            new SqlParameter("@SearchText", likeQuery))
            .ToListAsync();
    }
    else
    {
        var terms = ParseSearchTextForMultiwordSearch(searchText);
        if (string.IsNullOrWhiteSpace(terms))
            return results;
        // SqlQuery does not take any column mappings into account (https://entityframework.codeplex.com/workitem/233)
        // So we have to manually map the columns in the select statement
        var sqlQueryFormat = "SELECT CompanyId AS Id, IsEligible AS IsReadOnly, *" +
                                "  FROM Company.Company AS con" +
                                "  WHERE {0}(con.Name, @SearchText)";
        var sqlQuery = string.Format(sqlQueryFormat, "CONTAINS");
        var errored = false;
        try
        {
            results = await DataContext.Company.SqlQuery(sqlQuery,
            new SqlParameter("@SearchText", terms))
            .ToListAsync();
        }
        catch
        {
            //catch the error but do nothing with it
            errored = true;
        }
        //when the contains search fails due to some unknown error, use Freetext as a backup
        if (errored)
        {
            sqlQuery = string.Format(sqlQueryFormat, "FREETEXT");
            results = await DataContext.Company.SqlQuery(sqlQuery,
            new SqlParameter("@SearchText", terms))
            .ToListAsync();
        }
    }
    return results;
}