我开始使用RavenDB几天了,而且我已经陷入困境,我认为应该很容易执行。
我想要做的是搜索以获取属性Title
中所有用户输入的字词的产品列表。
一个例子:
product/1 -> title: "my awesome product"
product/2 -> title: "super product asd"
如果我搜索“prod per”,我预计只会出现第二个产品。
在我的脑海里,我会做这样的事情
public IList<Product> GetBySearchTerms(string searchTerms, int pageIndex, int pageSize, out int totalItems)
{
pageIndex--;
if (pageIndex < 0)
pageIndex = 0;
IList<Product> result = new List<Product>();
var query = var query = session.Query<Product>().Statistics(out stats);
var termsList = searchTerms.Split(' ', StringSplitOptions.RemoveEmptyEntries);
foreach (var term in termsList)
query = query.Where(x => x.Title.Contains(term));
if (pageSize > 0)
result = query.Skip(pageIndex * pageSize).Take(pageSize).ToList();
else
result = query.ToList();
totalItems = stats.TotalResults;
return result;
}
经过一番挖掘后,我发现第一个问题出现在Contains
方法中。由于搜索在RavenDB中的行为方式,因此未实现/支持它。
我应该使用Search
方法,但我还读到由于性能问题,不应使用*term*
。
所以我最终在RavenDB中创建了一个像这样的
的索引Name: ProductSearchByName
Map: from doc in docs.Products select new { Title = doc.Title }
代码
public IList<Product> GetBySearchTerms(string searchTerms, int pageIndex, int pageSize, out int totalItems)
{
pageIndex--;
if (pageIndex < 0)
pageIndex = 0;
IList<Product> result = new List<Product>();
RavenQueryStatistics stats;
var query = session.Query<Product>("ProductSearchByName").Statistics(out stats);
query = searchTerms
.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(query, (q, term) => q.Search(x => x.Title, "*" + term + "*", options: SearchOptions.And, escapeQueryOptions: EscapeQueryOptions.AllowAllWildcards));
if (pageSize > 0)
result = query.Skip(pageIndex * pageSize).Take(pageSize).ToList();
else
result = query.ToList();
totalItems = stats.TotalResults;
return result;
}
此搜索可以满足我的需要,但我担心有关使用通配符的所有警告。
有没有办法在不使用Contains
的情况下获得*term*
结果?
这个问题的正确方法/解决方案应该是什么?
答案 0 :(得分:0)
RavenDB uses Lucene for it's searching,针对搜索字词进行了优化,而不是子字符串。它使用分析器来定义字符串中存在的术语。
Using any of the built-in analyzers,当你拿一个像“hello world”这样的字符串时,术语是“你好”和“世界”。仅创建两个索引条目。如果您在末尾搜索通配符(例如he*
),它仍然可以按顺序扫描索引并匹配这些条款。但是当你在开头放置一个通配符时,例如*old
,那么它必须扫描整个索引才能响应。
在绝大多数用例中,完整的子字符串搜索是过度的。但是如果你想在不破坏性能的情况下启用它,那么诀窍就是使用一个从子串创建术语的分析器。这是在NGram Analyzer中实现的。因此,使用NGram分析的同样“hello world”将创建一个带有以下术语的索引:
d
e
el
ell
ello
h
he
hel
hell
hello
l
ld
ll
lo
llo
o
or
orl
orld
r
rl
rld
w
wo
wor
worl
world
现在,当您搜索子字符串时,它们都在索引中预定义,并且可以更容易匹配。
可以想象,使用NGram会导致更大的索引。这是在增加磁盘使用量之间进行权衡以获得更快的查询响应时间。它应该只在绝对必要的地方使用。
在大多数情况下,您最好不要进行全字搜索,也不要进行“开始搜索” - 不需要特殊分析。