RavenDB中的子串搜索
我有一组Idea
类型的对象
public class Idea { public string Title { get; set; } public string Body { get; set; } }
我想通过substring搜索这个对象。 例如,当我有标题“ 想法 ”的对象时,我希望在输入“ idea ”的任何子字符串时找到它: i,id,ide,idea,d,de,dea,e,ea,a 。
我正在使用RavenDB来存储数据。 搜索查询看起来像这样:
var ideas = session .Query() .Where(x => x.Query.Contains(query)) .As() .ToList();
索引如下:
public class IdeaByBodyOrTitle : AbstractIndexCreationTask { public class IdeaSearchResult { public string Query; public Idea Idea; } public IdeaByBodyOrTitle() { Map = ideas => from idea in ideas select new { Query = new object[] { idea.Title.SplitSubstrings().Concat(idea.Body.SplitSubstrings()).Distinct().ToArray() }, idea }; Indexes.Add(x => x.Query, FieldIndexing.Analyzed); } }
SplitSubstrings()
是一个扩展方法,它返回给定字符串的所有不同的子串:
static class StringExtensions { public static string[] SplitSubstrings(this string s) { s = s ?? string.Empty; List substrings = new List(); for (int i = 0; i < s.Length; i++) { for (int j = 1; j x.Trim()).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToArray(); } }
这不起作用。 特别是因为RavenDB没有识别SplitSubstrings()
方法,因为它在我的自定义程序集中。 如何使这项工作,基本上如何强制RavenDB识别这种方法? 除此之外,我的方法是否适合这种搜索(通过子字符串搜索)?
编辑
基本上,我想在此搜索上构建自动完成function,因此需要快速。
顺便说一句:我正在使用RavenDB – Build#960
您可以使用以下方法跨多个字段执行子字符串搜索:
(1)
public class IdeaByBodyOrTitle : AbstractIndexCreationTask { public IdeaByBodyOrTitle() { Map = ideas => from idea in ideas select new { idea.Title, idea.Body }; } }
在这个网站上你可以检查:
“默认情况下,RavenDB为所有内容使用名为LowerCaseKeywordAnalyzer的自定义分析器。(…)每个字段的默认值为Stores中的FieldStorage.No和Indexes中的FieldIndexing.Default。”
因此,默认情况下,如果您检查raven客户端中的索引术语,它会显示如下:
Title Body ------------------ ----------------- "the idea title 1" "the idea body 1" "the idea title 2" "the idea body 2"
基于此,可以构造通配符查询:
var wildquery = string.Format("*{0}*", QueryParser.Escape(query));
然后与.Where
和.Where
结构一起使用(使用OR运算符):
var ideas = session.Query() .Where(x => x.Title.In(wildquery) || x.Body.In(wildquery));
(2)
或者,您可以使用纯lucene查询:
var ideas = session.Advanced.LuceneQuery() .Where("(Title:" + wildquery + " OR Body:" + wildquery + ")");
(3)
您也可以使用.Search
表达式,但如果要搜索多个字段,则必须以不同方式构建索引:
public class IdeaByBodyOrTitle : AbstractIndexCreationTask { public class IdeaSearchResult { public string Query; public Idea Idea; } public IdeaByBodyOrTitle() { Map = ideas => from idea in ideas select new { Query = new object[] { idea.Title, idea.Body }, idea }; } } var result = session.Query() .Search(x => x.Query, wildquery, escapeQueryOptions: EscapeQueryOptions.AllowAllWildcards, options: SearchOptions.And) .As();
摘要:
还要记住*term*
相当昂贵,尤其是领先的通配符。 在这篇文章中,您可以找到更多关于它的信息。 据说,领先的通配符lucene对索引进行全面扫描,因此可以大大降低查询性能。 Lucene在内部存储其索引(实际上是字符串字段的术语)按字母顺序排序,并从左到右“读取”。 这就是为什么搜索尾随通配符的速度很快而前导通配符速度慢的原因。
因此,可以使用x.Title.StartsWith("something")
,但这显然不会搜索所有子字符串。 如果您需要快速搜索,可以将要搜索的字段的“索引”选项更改为“已分析”,但它不会再搜索所有子字符串。
如果子字符串查询中有空格键 ,请检查此问题以获得可能的解决方案。 有关建议,请查看http://architects.dzone.com/articles/how-do-suggestions-ravendb 。
这似乎是RavenDB快速子字符串搜索的重复
这里没有提到的答案是使用名为NGram的定制Lucene分析器
如果其他人遇到这个问题。 Raven 3有一个Search()
扩展方法,允许子字符串搜索。
几个陷阱:
- 请特别注意底部的“查询转义”部分
- 我没有看到它在任何地方提到,但它只对我有用,如果
Search()
被直接添加到Query()
(即它们之间没有任何Where()
,OrderBy()
等)
希望这可以节省一些人的挫败感。
我设法使用以下代码在内存中执行此操作:
public virtual ActionResult Search(string term) { var clientNames = from customer in DocumentSession.Query() select new { label = customer.FullName }; var results = from name in clientNames.ToArray() where name.label.Contains(term, StringComparison.CurrentCultureIgnoreCase) select name; return Json(results.ToArray(), JsonRequestBehavior.AllowGet); }
这样就省去了使用Contains方法搜索字符串的RavenDB方法的麻烦,如Daniel Lang的post所述 。
Contains
扩展方法是这样的:
public static bool Contains(this string source, string toCheck, StringComparison comp) { return source.IndexOf(toCheck, comp) >= 0; }