如何在RavenDB中使用与Linq“contains”类似的东西搜索多个术语?

时间:2013-06-27 09:47:54

标签: c# ravendb

我开始使用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*结果?

这个问题的正确方法/解决方案应该是什么?

1 个答案:

答案 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会导致更大的索引。这是在增加磁盘使用量之间进行权衡以获得更快的查询响应时间。它应该只在绝对必要的地方使用。

在大多数情况下,您最好不要进行全字搜索,也不要进行“开始搜索” - 不需要特殊分析。