【问题标题】:How to search multiple terms using something similar to Linq "contains" in RavenDB?如何在 RavenDB 中使用类似于 Linq“包含”的内容搜索多个术语?
【发布时间】:2013-06-27 09:47:54
【问题描述】:

我从几天开始就开始使用 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;
}

此搜索可以满足我的需要,但我担心有关使用通配符的所有警告。

有没有办法在不使用*term* 的情况下获得Contains 结果?

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

【问题讨论】:

    标签: c# ravendb


    【解决方案1】:

    RavenDB uses Lucene for it's searching,针对搜索词而非子字符串进行了优化。它使用 analyzers 来定义字符串中存在哪些术语。

    Using any of the built-in analyzers,当你取一个像“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 会产生更大的索引。这是为了获得更快的查询响应时间而增加磁盘使用量之间的权衡。它只应在绝对必要的情况下使用。

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

    【讨论】:

    • 感谢您的解释。我想我必须做一些测试,看看最终缓慢的性能是否会成为一个真正的问题。
    • 可能你只需要重新考虑是否必须有完整的子字符串匹配。许多用例可以通过全字匹配或开头匹配来解决。此外,如果您只是在寻找 .Contains 的替代品,您可以反转关系并使用 .In
    • 感谢您的见解。我需要与我的客户一起审查一些用例。我同意你的看法,用“term*”搜索每个单词就足够了
    猜你喜欢
    • 2014-03-13
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    • 1970-01-01
    • 1970-01-01
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多