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

初学者使用 Elasticsearch 和 C# 构建 CRUD APP 的教程 - 第一部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (10投票s)

2015年9月15日

CPOL

6分钟阅读

viewsIcon

81685

Elasticsearch和C#集成。

引言

在这篇分为两部分的文章中,我们将讨论 Elasticsearch API 的基础知识。

在第 1 部分中,我们将介绍其结构、命令、工具,并使用标准设置使其运行。我们还将创建一个简单的 Windows 窗体应用程序,以演示 CRUD 操作,并一路展示 Elasticsearch 的一些出色功能。

在第二部分中,我们将探讨 Elasticsearch 和 .NET 之间的集成。

背景

如果您对本文感兴趣,我假设您对 Lucene 有一些经验,或者至少听说过它。它显然是最近业界的热门话题。

但是,如果您从未听说过它,或者您有疑问,我建议您在 Code Project 上查看一下。那里有很多关于它的好文章。由于 Lucene 是 Elasticsearch 的核心技术,因此在玩 Elastic 之前了解它的工作原理非常重要。

我使用 Lucene 已经一年了,我个人使用 Elastic 的经验始于几个月前,当时我们公司决定将我们的 BI 核心从“纯”Lucene 迁移到 Elastic。我的主要信息来源来自其官方网站。

Elasticsearch 到底是什么?

正如我之前提到的,Elastic 运行在 Lucene 存储之上。它允许我们节省大量创建标准功能的工作,例如:索引、查询、聚合以及跨服务器分发物理索引文件。

对于那些使用过多个框架甚至创建了自己的类集来处理“纯”Lucene 的人来说,这是一个令人兴奋的消息。它确实是一个高效的 API。

入门

那么,让我们开始“有趣”的部分吧(我相信我不是唯一一个不喜欢环境设置的开发人员!)。首先,您需要安装并处理一些设置

1) 依赖项

幸运的是只有一个,Elasticsearch 需要最新版本的 Java。您应该从 Java 官方网站安装最新版本。

2) API

您可以从 Elasticsearch 的网站下载最新版本。

3) 配置

在启动 Elastic 服务器之前,您必须在 [安装路径]\config\elasticsearch.yml 文件中更改这些设置

  • 取消注释并选择一个没有空格的名称

    cluster.name: new_name

  • 取消注释并选择一个没有空格的名称

    node.name: new_node_name

  • 取消注释并输入“true”作为值

    bootstrap.mlockall: true

  • 取消注释并输入“127.0.0.1”作为值

    network.host: 127.0.0.1

  • 这两个设置默认不存在,您可以将它们粘贴到文件末尾

    script.inline: on

    script.engine.groovy.inline.aggs: on

4) 运行它!

启动服务器很简单

  • 以管理员身份打开命令提示符。
  • 进入您安装 Elastic 的文件夹。
  • 进入 bin 文件夹。
  • 键入 elasticsearch.bat 并按 Enter

您可以通过在浏览器中测试以下 URL 来检查它是否存活

https://:9200/_cluster/health?pretty。

5) 用于查询的 IDE

为了测试您全新的存储,您绝对需要一个好的 IDE。幸运的是,Elastic.org 提供了它。

我一直在测试其他 IDE,但 Marvel 和 Sense 是目前最好的。此步骤应在 Elastic 服务器运行后完成,安装命令是

[Installation Path]\bin> plugin -i elasticsearch/marvel/latest

然后,您可以通过浏览器访问这些工具

Marvel(健康监控器)

https://:9200/_plugin/marvel/kibana/index.html#/dashboard/file/marvel.overview.json

Sense(查询 IDE)

https://:9200/_plugin/marvel/sense/index.html

编写 Elastic 命令

好了,如果您已经熬过了安装阶段,那么有趣的部分现在开始了!

您现在应该已经意识到,Elastic 是一个 RESTFul API,因此其命令完全基于 Json。这是个好消息,因为它在当今业界被广泛使用。

粗略地说,我这里展示的是与我们在关系数据库中创建的内容的并行。考虑到这一点,让我们从我们的“create database”和“create table”语句开始。

映射

映射是我们告诉 Elastic 如何创建“表”的方式。通过映射,您将定义文档的结构、字段的类型等。

我们将处理假设的(尽管不是很有创意!)实体“Customer”。因此,您必须在 Sense IDE 中编写的命令是

PUT crud_sample
{
  "mappings": {
    "Customer_Info" : {
      "properties": { 
        "_id":{
         "type": "long"
        },
        "name":{
          "type": "string",
          "index" : "not_analyzed"
        },
        "age":{
          "type": "integer"
        },
        "birthday":{
          "type": "date",
          "format": "basic_date"
        },
        "hasChildren":{
          "type": "boolean"
        },
        "enrollmentFee":{
          "type": "double"
        }
      }
    }    

您可以使用此命令测试映射是否正常

GET /crud_sample/_mapping

结果,您应该得到

假设我们忘记创建了一个字段。没问题,您可以通过以下方式添加它

PUT /crud_sample/_mapping/Customer_Info
{
  "properties" : {
    "opinion" : {
     "type" : "string",
	 "index" : "not_analyzed"
    }
  }
}

您可以使用之前的命令检查它的运行情况。

插入一行

插入新行(索引是正确的术语)非常简单

PUT /crud_sample/Customer_Info/1
{
  "age" : 32,
  "birthday": "19830120",
  "enrollmentFee": 175.25,
  "hasChildren": false,
  "name": "PH",
  "opinion": "It's Ok, I guess..."
}

您可以通过此命令检查它

GET /crud_sample/Customer_Info/_search

然而,逐行插入可能会有点痛苦。幸运的是,我们可以使用批量加载,像这样

POST /crud_sample/Customer_Info/_bulk
{"index": { "_id": 1 }}
{"age" : 32, "birthday": "19830120", "enrollmentFee": 175.25, 
 "hasChildren": false, "name": "PH", "opinion": "It's cool, I guess..." }
{"index": { "_id": 2 }}
{"age" : 32, "birthday": "19830215", "enrollmentFee": 175.25, 
 "hasChildren": true, "name": "Marcel", "opinion": "It's very nice!" }
{"index": { "_id": 3 }}
{"age" : 62, "birthday": "19530215", "enrollmentFee": 205.25, 
 "hasChildren": false, "name": "Mayra", "opinion": "I'm too old for that!" }
{"index": { "_id": 4 }}
{"age" : 32, "birthday": "19830101", "enrollmentFee": 100.10, 
 "hasChildren": false, "name": "Juan", "opinion": "¿Qué tal estás?" }
{"index": { "_id": 5 }}
{"age" : 30, "birthday": "19850101", "enrollmentFee": 100.10, 
 "hasChildren": true, "name": "Cezar", "opinion": "Just came for the food..." }
{"index": { "_id": 6 }}
{"age" : 42, "birthday": "19730101", "enrollmentFee": 50.00, 
 "hasChildren": true, "name": "Vanda", "opinion": "Where am I again?" }
{"index": { "_id": 7 }}
{"age" : 42, "birthday": "19730101", "enrollmentFee": 65.00, 
 "hasChildren": false, "name": "Nice", "opinion": "What were u saying again?" }
{"index": { "_id": 8 }}
{"age" : 22, "birthday": "19930101", "enrollmentFee": 150.10, 
 "hasChildren": false, "name": "Telks", "opinion": "Can we go out now?" }
{"index": { "_id": 9 }}
{"age" : 32, "birthday": "19830120", "enrollmentFee": 175.25, 
 "hasChildren": false, "name": "Rafael", "opinion": "Should be fine..." }

现在,如果您运行 search 语句,您将看到 9 个匹配项,这几乎是我们目前得到的所有内容。但是,如果您想检查某个特定的客户,您只需在 URL 末尾添加其 ID 即可

GET crud_sample/Customer_Info/3

更新

Elastic 足够智能,可以根据语句中提供的“id”来判断您是添加新文档还是更新它。例如,假设您需要更改客户编号 3 的“意见”

POST /crud_sample/Customer_Info/3/_update
{
  "doc": {
    "opinion": "I'm really too old for it."
  }
}

Deleting(删除中)

您必须像处理任何数据库一样谨慎对待这些命令。这里的主要选项是

删除整个存储

delete crud_sample

删除特定客户

delete crud_sample/Customer_Info/1

查询

在查询方面,Elastic 功能极其强大。我将介绍我们的 CRUD 应用程序所需的基本功能。
在运行以下查询示例之前,如果您添加一些额外的行/文档可能会很有趣。

查找精确值

GET /crud_sample/Customer_Info/_search
{
    "query" : {
        "filtered" : { 
            "query" : {
                "match_all" : {} 
            },
            "filter" : {
                "term" : { 
                    "opinion" : "It's cool, I guess..."
                }
            }
        }
    }
}

您应该得到与查询匹配的“opinion”文档作为响应

组合布尔过滤器

GET /crud_sample/Customer_Info/_search
{
   "query" : {
      "filtered" : { 
         "filter" : {
            "bool" : {
              "must" : {
                 "term" : {"hasChildren" : false} 
              },
              "must_not": [ 
                { "term": { "name": "PH"  }},
                { "term": { "name": "Felix"  }}
              ],
              "should" : [
                 { "term" : {"age" : 30}}, 
                 { "term" : {"age" : 31}}, 
                 { "term" : {"age" : 32}} 
              ]
           }
         }
      }
   }
}

请注意,我们在这里组合了三个子句

  • "must": 查询必须出现在匹配的文档中。
  • "must_not": 查询不能出现在匹配的文档中。
  • "should": 查询应该出现在匹配的文档中,但不是强制性的。

查找多个精确值

GET /crud_sample/Customer_Info/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "terms" : { 
                    "age" : [22, 62]
                }
            }
        }
    }
}

如上所示,Elastic 允许为同一字段提供多个值。

范围查询

在此示例中,我们获取所有注册费在 10100 之间的文档

GET /crud_sample/Customer_Info/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "range" : {
                    "enrollmentFee" : {
                        "gte" : 10,
                        "lt"  : 100
                    }
                }
            }
        }
    }
}

现在,使用生日的日期范围

GET /crud_sample/Customer_Info/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "range" : {
                    "birthday" : {
                        "gt" : "19820101",
                        "lt" : "19840101"
                    }
                }
            }
        }
    }
}

并结合日期和数字字段

GET /crud_sample/Customer_Info/_search
{
  "query" : {
    "filtered" : {
        "filter" : {
          "bool": { 
            "must": [
                {"range": {"enrollmentFee": { "gte": 100, "lte": 200 }}},
                {"range": {"birthday": { "gte": "19850101" }}}
            ]
          }
        }
     }
  }
}

聚合

对于大数据分析,聚合是锦上添花。在 BI 项目所需的所有步骤之后,这就是您开始从大量数据中发现意义的时刻。

聚合使您能够即时计算和汇总有关当前查询的数据。

它们可用于各种任务,例如:动态计数、平均值、最小值和最大值、百分位数等。

粗略比较,我们实体 Customer 在 SQL Server 中的计数聚合将是

Select Count(id) From customer

让我们用我们的索引来做一些示例

计数名称

GET /crud_sample/Customer_Info/_search?search_type=count
{
  "aggregations": {
    "my_agg": {
      "terms": {
        "field": "name",
         "size": 1000
      }
    }
  }
}

您应该得到作为响应

获取最低注册费

GET /crud_sample/Customer_Info/_search?search_type=count
{
    "aggs" : {
        "min_price" : { "min" : { "field" : "enrollmentFee" } }
    }
}

语法非常相似,基本上聚合关键字会改变。在本例中,我们使用“min”。

获取平均年龄

GET /crud_sample/Customer_Info/_search?search_type=count
{
    "aggs" : {
        "avg_grade" : { "avg" : { "field" : "age" } }
    }
}

现在,计算平均值...

获取多值聚合

GET /crud_sample/Customer_Info/_search?search_type=count
{
    "aggs" : {
        "grades_stats" : { "extended_stats" : { "field" : "enrollmentFee" } }
    }
}

这是一个有用的资源,运行它您将获得多个聚合。

嵌套聚合

GET /crud_sample/Customer_Info/_search?search_type=count
{
   "aggs": {
      "colors": {
         "terms": {
            "field": "hasChildren"
         },
         "aggs": { 
            "avg_age": { 
               "avg": {
                  "field": "age" 
               }
            }
         }
      }
   }
}

最后是嵌套聚合。这非常有趣,上面的语句按字段“hasChildren”对聚合进行分组,并在其值(TrueFalse)中找到平均年龄

结论

本文的目的是演示如何设置 Elasticsearch 并学习如何编写基本语句。所有这些概念都将应用于我们的 CRUD 应用程序。

我们的主要目标是将所有这些与 .NET 结合起来,我们将在下一篇(也是最后一篇)文章中实现。

历史

  • 2015 年 9 月 15 日:初始版本
© . All rights reserved.