【问题标题】:Searching for a product even if code is misspelled即使代码拼写错误也能搜索产品
【发布时间】:2012-06-12 10:12:09
【问题描述】:

好吧,伙计们,我在这里遇到了麻烦。

基本上我遇到的问题是,当人们在搜索某些东西时,他们经常会错误输入代码,因为他们经常会涉及多个 0,例如在搜索时:K00000WEFLZ 他们会错误输入 0,然后是产品结果什么也没返回,我基本上是想尝试让它搜索检查搜索是否在字母“K”之后包含一定数量的 0(因为 K 总是有 10+ 个 ID 号和至少 4 个-5 0's),如果是,则在搜索操作期间将其替换为“*”,并且仍然允许您找到产品,无论他们输入的问题有多么错误。

我知道我将不得不创建一个自定义类并为此覆盖默认值(但是其中很多无法访问/是私有的),因为无法更改默认搜索模式,因为这将为每个人更改它我只希望它用于这个特定的网站。

我也不能在开头或结尾使用通配符,因为它有一个庞大的目录,它会将里程与许多结果相匹配。

据我所知,这是处理默认逻辑类搜索的代码:

protected virtual IList<Product> CreateCustomCollection()
{
        var list = new List<Product>();

        switch (mode)
        {
            case ProductRepeaterMode.Search:

                if (Page.Request.QueryString["search"] != null && Page.Request.QueryString["search"].Length != 0)
                {
                    bool[] customString = new bool[5] { SearchCustomString1, SearchCustomString2, SearchCustomString3, SearchCustomString4, SearchCustomString5 };
                    IList<Product> results = Fabric.ObjectProvider.Get<IProductSearchCommand>().Search(Page.Request.QueryString["search"], out searchWords, IncludeSkus, IsPublicFacing, customString, CoreHttpModule.Session);

                    var retailOrder = WebStoreManager.CurrentOrder as IRetailOrder;
                    var accountOrder = WebStoreManager.CurrentOrder as IAccountOrder;

                    IList<Product> productsToRemove = new List<Product>();
                    IList<Product> productsToAdd = new List<Product>();

                    foreach (var product in results)
                    {
                        if (hideRestrictedProducts)
                        {
                            if (retailOrder != null)
                            {
                                if (!product.CanBePurchasedByRetailCustomer || product.AgentOnly)
                                    productsToRemove.Add(product);
                            }
                            else
                            {
                                if (accountOrder != null)
                                {
                                    var add = false;

                                    if (product.CanBePurchasedOnAccount)
                                        add = true;

                                    if (product.AgentOnly)
                                    {
                                        if (accountOrder.Agent != null)
                                            add = true;
                                        else
                                            add = false;
                                    }

                                    if (!add)
                                        productsToRemove.Add(product);
                                }
                            }
                        }

                        // Replace SKUs with lines
                        if (resolveSkusToLines)
                        {
                            var sku = product.Role as SkuProductRole;
                            if (sku != null)
                            {
                                productsToRemove.Add(product);
                                if (sku.Owner != null && sku.Owner.Product != null)
                                {
                                    var line = sku.Owner.Product;
                                    if (!results.Contains(line) && !productsToAdd.Contains(line))
                                        productsToAdd.Add(line);
                                }
                            }
                        }
                    }

                    foreach (Product product in productsToAdd)
                    {
                        results.Add(product);
                    }

                    foreach (Product product in productsToRemove)
                    {
                        results.Remove(product);
                    }

                    foreach (var result in results)
                        list.Add(result);
                }
                break;
        }
        return list;
    }

【问题讨论】:

  • 我正在考虑可能将搜索字符串分解为一个字符数组,然后检查数据库中与该字符数组最相似的字符串,但是我觉得这样做效率极低
  • 我也可以这样做,所以如果有说... 2+ 0,我将它们全部替换为通配符。
  • 我正在研究使用正则表达式来检测和替换它
  • 您可能想考虑使用 Levenshtein 距离算法来比较字符串的相似性?您会在此 here 上找到一个 SO 问题。
  • 唯一的问题是它必须用于每一个根本没有效率的产品,不过谢谢你的评论。

标签: c#


【解决方案1】:

模糊逻辑,必须喜欢它。我这样做的方式是从搜索字符串本身构建一个正则表达式,它并没有说明它应该如何完成,但我会尽我最大的努力。

将正则表达式构建器视为自定义构建的压缩操作。从您的字符数组开始并从中构建正则表达式搜索,每当您在一行中找到 2 个相同的字符时,将第二个字符(并忽略第二个字符之外的任何其他字符)替换为“+”字符,然后使用正则表达式运行生成的搜索而不是精确的字符串匹配。

K00000WEFLZ 会变成K0+WEFLEZ,并匹配 K、1 个或多个 0、WEFLEZ。该算法需要对任何重复的字符执行此操作,因此可能会有点傻。像KK000WWLEFF22 这样的东西会出现K+0+W+LEF+2+。搜索字符串并没有那么好,并且可能匹配很多您不想要的东西......但有效。或者您可以将其限制为仅替换 0 或 0。等等等等……无论哪种效果最好。

我推荐的另一种方式是实时过滤。但它的用处更多地取决于预期的正常功能。用户输入值会更常见,还是更常见的是他们从其他地方复制/粘贴它。在第二种情况下,实时过滤完全没用。否则...重新过滤每个 keyPress 或 TextChanged 事件的列表。至少那时他们可能会知道整个列表何时消失,因为输入了一个额外的 0。

编辑 - 添加代码示例

private string RegStringZipper(string searchString)
    {
        StringBuilder sb = new StringBuilder();
        char lastChar = new char();
        bool plusFlag = false;
        foreach (char c in searchString)
        {
            if (sb.Length == 0)
            {
                sb.Append(c);
                lastChar = c;
            }
            else
            {
                if (lastChar == c)
                {//we have a repeating character
                    //Note: Here is also where if you only wanted to filter a specific character, like 0, you would check for it.
                    if (!plusFlag)
                    {//we have not already added the +
                        sb.Append('+');
                        plusFlag = true;
                    }
                    //else do nothing, skip the characer
                }
                else
                {
                    sb.Append(c);
                    plusFlag = false;
                    lastChar = c;
                }
            }
        }
        return sb.ToString();
    }

至于我将把它放在你的代码中的什么地方......这真的取决于搜索功能的实际工作方式,它不是我以前玩过的东西......说到这个......如果它工作他们看起来它可能会起作用,在上面的代码中用 '+' 换成 '*'....

if (Page.Request.QueryString["search"] != null && Page.Request.QueryString["search"].Length != 0)
            {
                bool[] customString = new bool[5] { SearchCustomString1, SearchCustomString2, SearchCustomString3, SearchCustomString4, SearchCustomString5 };
                string SearchString = RegStringZipper(Page.Request.QueryString["search"]);
                //please note, given that I dont know what FabricProvider.Search works on, I dont actually know that this works as intended.
                IList<Product> results = Fabric.ObjectProvider.Get<IProductSearchCommand>().Search(SearchString, out searchWords, IncludeSkus, IsPublicFacing, customString, CoreHttpModule.Session);

【讨论】:

  • 后续有趣的问题:如果有多个匹配项(即相关性分数),有关如何对 RegEx 结果进行排序的任何建议?
  • 你知道吗....我不知道。也许将原始搜索字符串的字符串长度与提供的结果进行比较......但如果这些是产品 ID,那么逻辑表明它们可能都将是相同的长度。获得“相关性”将非常复杂。其他人对此有什么想法,我很想听听。
  • 这就是解决方案。你想匹配一个模式。正则表达式建立在模糊逻辑之上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-03
  • 2021-10-05
  • 2021-04-09
  • 1970-01-01
  • 2011-05-02
  • 2013-03-12
  • 2011-05-24
相关资源
最近更新 更多