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