我的搜索算法未返回预期结果

问题描述 投票:0回答:1

我的搜索算法没有产生预期的结果。

例如:数据库中有一个项目,名称为“Vitamin C,askorbinsyre”。算法在搜索“vi”、“it”、“in”、“vit”、“as”和“re”时发现这一点,但在搜索“ta”、“vita”、“ask”、“ yr”或“yre”!这对我来说很奇怪。

这是我的代码(服务的一部分

SearchService
):

public async Task<List<NutrientDisplayDTO>> Search(string searchTerm)
{
    var nutrientsResults = await SearchEntity<Nutrient>(searchTerm);
    // more entities here (all returned as a tuple inside a service response object,
    // but I have changed it to only do a single entity search here for brevity)

    return _mapper.Map<List<NutrientDisplayDTO>>(nutrientsResults);
}

private async Task<List<T>> SearchEntity<T>(string searchTerm) where T : class
{
    // The search component is replacing " " with "+"
    string[] searchWords = searchTerm
        .Split('+', StringSplitOptions.RemoveEmptyEntries)
        .ToArray();

    var filteredEntities = await _context.Set<T>()
        .ToListAsync();

    var predicate = BuildSearchPredicate<T>(searchWords);

    return filteredEntities
        .Where(predicate)
        .ToList();
}

private static Func<T, bool> BuildSearchPredicate<T>(string[] searchWords)
{
    return entity =>
    {
        var entityType = typeof(T);
        var propertyName = "Name";

        var property = entityType.GetProperty(propertyName);
        if (property == null)
            return false;

        var propertyValue = property.GetValue(entity);
        if (propertyValue == null || !(propertyValue is string))
            return false;

        var entityNameWords = ((string)propertyValue).ToLower().Split(' ');

        // Check if any word in the entity name matches any of the search words
        return searchWords.Any(searchWord =>
            entityNameWords.Any(entityWord =>
                entityWord.Contains(searchWord.ToLower())));
    };
}

属于搜索一部分的实体都实现了

ISearchableEntity
接口:

public interface ISearchableEntity
{
    string Name { get; }
}

我必须承认,我在这方面得到了很多人工智能帮助,但我并不真正理解任何谓词内容。

你能帮忙吗?

c# search
1个回答
0
投票

第一个

 _context.Set<T>()
    .ToListAsync();

将从数据库中获取所有行,并在内存中进行所有处理。这是典型的“坏事”。但大多数 SQL 数据库无法使用索引来检查字符串是否包含单词,因此无论如何都需要进行全表扫描才能完成您想做的事情。请记住,如果您有大量文本要搜索,您可能应该使用一些专门的搜索框架。

    var entityType = typeof(T);
    var propertyName = "Name";

    var property = entityType.GetProperty(propertyName);
    if (property == null)
        return false;

    var propertyValue = property.GetValue(entity);
    if (propertyValue == null || !(propertyValue is string))
        return false;

这使用了大量的反射,这是降低性能的另一种好方法。更好的方法是让调用者定义要使用的属性,即

private static Func<T, bool> BuildSearchPredicate<T>(Func<T, string> selector, string[] searchWords){
    return  entity =>
    {
    var stringToSeach = selector(entity);

下一期

var entityNameWords = ((string)propertyValue).ToLower().Split(' ');

如果您只是要检查任何实体单词是否包含任何搜索单词,我认为拆分字符串没有什么意义。只需在整个字符串上运行 contains 即可。

searchWords.Any(word => stringToSeach.Contains(word, StringComparison.CurrentCultureIgnoreCase));

请注意

StringComparison.CurrentCultureIgnoreCase
的用户,这通常比将字符串转换为较低值更好,因为您可以避免额外的内存分配。旧版本的 .Net 缺少这样的 Contains 方法,在某些情况下
IndexOf
可以用作替代方法。

我不会创建谓词函数,而是让该方法直接应用过滤,即类似

    public static IEnumerable<T> WherePropertyContainsAny<T>(IEnumerable<T> values, Func<T, string> selector, params string[] searchWords)
    {
        return values.Where(
            t =>
            {
                var str = selector(t);
                return searchWords.Any(word => str.Contains(word, StringComparison.CurrentCultureIgnoreCase));
            });
    }

为此类函数编写单元测试通常也是一个好主意,这样您就可以有效地检查问题并调试代码:

    [Test]
    public void Test()
    {
        var input = new []{"Vitamin C, askorbinsyre", "random string"};
        var numberOfMatches = WherePropertyContainsAny(input, s => s, "ta", "vita", "ask", "yr", "yre").Count();
        Assert.AreEqual(1, numberOfMatches);
    }
© www.soinside.com 2019 - 2024. All rights reserved.