使用Linq和EF过滤标记化的搜索结果

时间:2011-08-10 16:25:53

标签: asp.net-mvc linq c#-4.0 entity-framework-4

我正在寻找一种基于标记化查询字符串创建过滤系统的方法,以返回农场列表。

过滤机制有望足够灵活,我可以按任何顺序提供令牌以返回结果。

搜索规则如下:

  州:WA作物:香蕉

会为我提供WAbanana所有农场的过滤列表。

  

作物:香蕉州:WA

应该返回相同的结果。

  

城市:奥尔巴尼作物:香蕉

会为我提供Albanybanana所有农场的过滤列表。

提供的每个值都可以用引号括起来,以便对空格分隔值进行分组。 e.g

  城市:“巴克山”作物:香蕉

会为我提供Mount Barkerbanana所有农场的过滤列表。

此外,任何非标记化查询都只会在服务器Details属性中查找,并使用引号组合多个单词查询再次返回服务器列表。

--------------------------------------- EDIT ------- -------------------------------------

我当前使用谓词的搜索系统编码如下。这很长(抱歉),这是我的第一次尝试,虽然我希望这可以被某种灵魂重构。

非常感谢提前:

    public ActionResult Search(string query, int? page)
    {
        IQueryable<Farm> farms = this.ReadOnlySession.All<Farm>();

        if (!String.IsNullOrWhiteSpace(query))
        {
            // http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder
            var predicate = PredicateBuilder.True<Farm>();

            // We want to replace the spaces in quoted values here so we can split by space later.
            // http://stackoverflow.com/questions/2148587/regex-quoted-string-with-escaped-quotes-in-c
            Regex quoted = new Regex(@"""[^""\\]*(?:\\.[^""\\]*)*""");

            foreach (var match in quoted.Matches(query))
            {
                query = query.Replace(match.ToString(), match.ToString().Replace(' ', '-'));
            }

            // Tidy up the query to remove "".
            string[] splitQuery = HttpUtility.UrlDecode(query).Replace("\"", "").Split(' ');
            Dictionary<string, string> tokenDictionary = new Dictionary<string, string>();

            // Loop through our string[] and create a dictionary. Guids used to allow multiple keys
            // of the same value.
            Parallel.ForEach(splitQuery, subQuery =>
            {
                string[] tempArray = subQuery.Split(':');

                if (tempArray.Length == 2)
                {
                    tokenDictionary.Add(String.Format("{0}:{1}", tempArray[0], Guid.NewGuid()), tempArray[1]);
                }
                else
                {
                    tokenDictionary.Add(String.Format("description:{0}", Guid.NewGuid()), subQuery);
                }
            });

            // Loop through the dictionary and create our predicate.
            foreach (KeyValuePair<string, string> item in tokenDictionary)
            {
                string value = item.Value.Replace('-', ' ');
                string key = item.Key.Split(':')[0].ToUpperInvariant();

                switch (key)
                {
                    case "CROP":
                        value = Utilities.CreateSlug(value, OzFarmGuideConfig.RemoveDiacritics);
                        predicate = predicate.And(x => x.Crops.Any(y => value.Equals(y.Slug, StringComparison.OrdinalIgnoreCase)));
                        break;
                    case "STATE":
                        predicate = predicate.And(x => value.Equals(x.City.State.Name, StringComparison.OrdinalIgnoreCase));
                        break;
                    case "CITY":
                        value = Utilities.CreateSlug(value, OzFarmGuideConfig.RemoveDiacritics);
                        predicate = predicate.And(x => value.Equals(x.City.Slug, StringComparison.OrdinalIgnoreCase));
                        break;
                    default:
                        predicate = predicate.And(x => !String.IsNullOrWhiteSpace(x.Details) &&  x.Details.Contains(value));
                        break;
                }
            }

            farms = farms.Where(predicate).OrderByDescending(x => x.Rating)
                                          .ThenByDescending(x => x.RatingVotes);

            PagedList<Farm> pagedFarms = new PagedList<Farm>(farms, page.HasValue ? page.Value - 1 : 0, 5);


            return View(pagedFarms);
        }
        else
        {
            PagedList<Farm> pagedFarms = null;
            return View(pagedFarms);
        }
    }

1 个答案:

答案 0 :(得分:0)

只是一个猜测,问题会因引入DefaultIfEmpty()

而自行纠正
default:
    // This is not working at the mo. Getting a null exception when we try
    // to initialise PagedList.
    predicate = predicate.And(x => x.Details.DefaultIfEmpty().Contains(value));
    break;