65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在 .NET 应用程序中使用 Elasticsearch 实现全文搜索

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2020 年 5 月 6 日

CC (BY-ND 3.0)

3分钟阅读

viewsIcon

25302

了解如何使用 NEST 在 C# 中读取和写入具有自定义全文查询的文档。

为什么选择 Elastic Search?

Elasticsearch 是一个分布式、开源的搜索引擎,可以管理各种数据。但是,为什么 Elasticsearch 是全文搜索的最佳解决方案呢?

2009 年,当我在论文中不得不为数据推荐系统实现搜索算法时,我开始尝试全文搜索。这次经历非常具有教育意义,但从头开始是一场恶战。我第一次有机会使用一个库时,我尝试了 Apache Lucene 来实现全文搜索。随着应用程序架构变得复杂(例如,需要共享索引数据的多个服务器),Solr 提供了一个可扩展的解决方案。它是一个基于 API 的开源搜索引擎(基于 Lucene)。在使用这些库之后,我使用了 Elasticsearch,它现在是市场领导者。它有一个本地免费版本,并且可以在云端使用。它可以从简单的安装扩展到巨大的环境。所以问题是:为什么不呢?

示例应用程序

我成功地在生产环境和我的 开源无头 CMS RawCMS 上使用了 Elasticsearch。为了展示 Elasticsearch 的工作原理,我创建了一个示例应用程序,其中实现了两个主要功能:

  • 创建索引并将数据添加到其中
  • 使用全文查询读取数据

示例代码可在 GitHub 上找到。要运行和测试它,只需下载、编译并执行

dotnet ElasticSearchTest.dll create -f divina_commedia.txt -h https://:9300
> Index created in 30338ms with 14006 element.

dotnet ElasticSearchTest.dll search -i divinacommediatxt 
                                    -h https://:9300 -q "dante AND virgi*"
> Searching for $dante AND virgi*
> "Dante, perché Virgilio se ne vada,
> "Dante, perché Virgilio se ne vada,

控制台应用程序使用库 ConsoleLineParser 以一种人性化的方式解析输入。所以我只是添加了两个类用于动词属性,以及控制台动词和要运行的代码之间的映射。

private static void Main(string[] args)
{
    object x = CommandLine.Parser.Default.ParseArguments<CreateOptions, SearchOptions>(args)
      .MapResult(
        (CreateOptions opts) => DoCreate(opts),
        (SearchOptions opts) => DoSearch(opts),
        errs => 1);
}

基于用户输入的动词(搜索或创建),将启动相应的过程并传递参数。

如何编写文档

编写部分非常简单。事实上,对于 Elasticsearch,有两种选择。使用低级框架,您可以获得 elastic API 的封装实现。这有助于避免手动绑定和组合 JSON 负载。但是,如果您想处理数据,一个不错的选择是 NEST。NEST 是高级框架,如果您是 ORM 方面的专家,这应该不足为奇。

您只需要为要保存的文档创建类,并使用注释定义如何保存属性,然后调用保存 API。

我不认为这太复杂,而且听起来更像一个普通的例程。这是类定义的代码片段。在本例中,我为每个文档只使用一个字段,其中包含一行文本。

public class LogDocument
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public string Body { get; set; }
}

下一步是创建索引。在这一步中,我们将索引与类关联起来。这可以手动完成,指定存储设置,或使用自动映射。在我们的示例中,“Id”字段会自动映射到文档的唯一标识符。

client.Indices.Create(indexName, c => c
                .Map<LogDocument>(m => m
                    .AutoMap<LogDocument>()
                )
            );

最后,我们有了代码中最愚蠢的部分:写入数据。因为我们想将所有诗句的行保存到许多文档中,每行一个文档,所以我们只有一个迭代。

  string[] lines = File.ReadAllLines(filepath);

  int items = 0;
  Parallel.ForEach(lines, (line) =>
  {
      if (!string.IsNullOrWhiteSpace(line))
      {
          client.CreateDocument<LogDocument>(new LogDocument()
          {
              Body = line.Trim()
          });
          items++;
      }
  });

请注意,通过 API 使用外部系统,我们可以使用并行结构来提高性能。

如何查询文档

这部分非常简单,而且非常清晰——至少我希望如此。访问文档的基本方法是使用常规的流畅 LINQ 语法。这是基本用法,我更倾向于将此演示集中在未得到充分记录的全文数据搜索用例上。

Elastic 允许使用原始查询在字段数据上查找。为此,您可以使用 Search 方法的正确重载,配置原始查询

var searchResponse = client.Search<LogDocument>(s => s
                        .Size(10)
                        .Query(q => q.QueryString(

                            qs => qs.Query(searchStr)
                            .AllowLeadingWildcard(true)
                            )
                        )
                    );

var docs = searchResponse.Documents;

带走什么

Elasticsearch 是领先的搜索引擎解决方案。它为应用程序提供了丰富的功能,例如全文搜索或文档索引。它可以用作服务或本地部署。无论哪种情况,对于基本用法,它都非常容易配置。

NEST 框架允许我们像通过 LINQ 访问简单数据库一样存储和访问 Elasticsearch,这使一切变得非常简单。

对于希望让最终用户编写查询的复杂场景,您可以使用原始查询并将结果映射到类。

资源

历史

  • 2020 年 5 月 6 日:初始版本
© . All rights reserved.