1

我开始使用 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*由于性能问题,不应使用 using。

所以我最终在 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*

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

4

1 回答 1

0

RavenDB使用 Lucene 进行搜索,它针对搜索词而不是子字符串进行了优化。它使用分析器来定义字符串中存在哪些术语。

使用任何内置分析器,当您使用像“hello world”这样的字符串时,术语是“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 会导致更大的索引。这是为了获得更快的查询响应时间而增加磁盘使用量之间的权衡。它只应在绝对必要的情况下使用。

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

于 2013-06-27T15:58:49.193 回答