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





5.00/5 (10投票s)
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 允许为同一字段提供多个值。
范围查询
在此示例中,我们获取所有注册费在 10
到 100
之间的文档
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
”对聚合进行分组,并在其值(True
或 False
)中找到平均年龄
结论
本文的目的是演示如何设置 Elasticsearch 并学习如何编写基本语句。所有这些概念都将应用于我们的 CRUD 应用程序。
我们的主要目标是将所有这些与 .NET 结合起来,我们将在下一篇(也是最后一篇)文章中实现。
历史
- 2015 年 9 月 15 日:初始版本