ElasticSearch NEST操作(3)详细介绍

目录(?)[+]


NEST 是 Elasticsearch 的官方高级 .NET 客户端,提供了强类型的 DSL(领域特定语言)来与 Elasticsearch 交互。以下是 NEST 中常用的核心类及其用途,按功能分类整理:


1. 客户端与连接类#

  • ElasticClient
    所有操作的入口类,用于执行索引、搜索、更新、删除等操作。

    var settings = new ConnectionSettings(new Uri("http://localhost:9200"));
    var client = new ElasticClient(settings);
    
  • ConnectionSettings
    配置 Elasticsearch 连接参数(如节点地址、认证、超时时间等)。

    var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
        .DefaultIndex("my-index");
    

2. 索引管理类#

1. CreateIndexDescriptor(索引创建描述符)#

用于定义索引的映射、设置(如分片数、副本数)。

方法 作用
.AutoMap<T>() 根据类 T 的属性自动推断字段类型
.NumberOfShards 主分片数量(创建后不可修改)
.NumberOfReplicas 副本数量(可动态调整)
.Analysis 配置自定义分词器、过滤器和 tokenizer
var createIndexResponse = client.Indices.Create("my-index", c => c
    .Map<MyDocument>(m => m.AutoMap())
    .Settings(s => s.NumberOfShards(3))
);

更详细:

var createResponse = client.Indices.Create("products", c => c
    // 1. 映射配置
    .Map<Product>(m => m
        .AutoMap()  // 自动映射 Product 类的属性
        .Properties(p => p
            .Text(t => t.Name(n => n.Name).Analyzer("ik_max_word")) // 指定分词器
            .Number(n => n.Name(n => n.Price).Type(NumberType.Double)) // 明确指定数值类型
            .Date(d => d.Name(n => n.CreatedAt).Format("yyyy-MM-dd")) // 自定义日期格式
        )
    )
    // 2. 索引设置
    .Settings(s => s
        .NumberOfShards(3)        // 主分片数
        .NumberOfReplicas(1)      // 副本数
        .Analysis(a => a          // 自定义分析器
            .Analyzers(an => an
                .Custom("my_analyzer", ca => ca
                    .Tokenizer("standard")
                    .Filters("lowercase", "asciifolding")
                )
            )
        )
    )
);

2. IndexSettingsDescriptor(索引设置描述符)#

配置索引的分词器、分析器等设置。

专门用于配置索引的高级设置,如分词器、存储优化等。

常见配置场景

  • 自定义分析器:定义复杂的分词规则。
  • 静态配置:如 refresh_interval 控制索引刷新频率。
  • 合并策略:优化段合并行为。

示例

var updateSettingsResponse = client.Indices.UpdateSettings("logs", s => s
    .Settings(settings => settings
        .Analysis(a => a
            .TokenFilters(tf => tf
                .Synonym("my_synonyms", sf => sf
                    .SynonymsPath("analysis/synonym.txt") // 从文件加载同义词
                )
            )
            .Analyzers(an => an
                .Custom("my_custom_analyzer", ca => ca
                    .Tokenizer("whitespace")
                    .Filters("lowercase", "my_synonyms")
                )
            )
        )
        .RefreshInterval("30s")          // 每30秒刷新一次索引
        .Merge(m => m
            .Policy(p => p
                .SegmentsPerTier(24)     // 控制段合并策略
            )
        )
    )
);

关键参数说明

配置项 作用
.Analysis 定义分词器、过滤器(如同义词、停用词)
.RefreshInterval 索引刷新间隔(影响近实时搜索性能)
.Merge.Policy 控制 Lucene 段合并策略,优化写入性能

3. 其他常见索引操作#

删除索引

var deleteResponse = client.Indices.Delete("old_index");

获取索引信息

var getResponse = client.Indices.Get("products");
var settings = getResponse.Indices["products"].Settings;

关闭/打开索引

// 关闭索引(禁止写入/搜索)
client.Indices.Close("products");

// 打开索引
client.Indices.Open("products");

别名管理

client.Indices.PutAlias("products", "current_products");

最佳实践建议#

  1. 分片数量:根据数据量和硬件资源合理设置(通常单个分片 10-50GB)。
  2. 副本数量:生产环境至少 1 个副本,保障高可用。
  3. 动态映射:通过 dynamic: strict 避免字段自动映射导致污染。
  4. 分析器测试:使用 Analyze API 提前验证分词效果。

通过合理使用索引管理 API,可以显著优化 Elasticsearch 的写入性能、查询效率和资源利用率。


3. 文档操作类#

1 IndexRequest<T>IndexDescriptor<T> 核心功能#

用于向 Elasticsearch 索引(插入或更新)单个文档

  • 插入新文档:若文档 ID 不存在,直接创建新文档。
  • 更新文档:若文档 ID 已存在,默认会覆盖旧文档(类似 PUT 操作)。
  • 自动映射:文档字段类型根据 T 类的属性自动推断(需提前创建索引或启用动态映射)。

基础用法示例

1. 直接索引文档(自动处理 ID)

使用 IndexDocument 快捷方法,自动从对象中提取 Id 属性作为文档 ID

var document = new MyDocument 
{ 
    Id = 1, 
    Name = "Test Document",
    Timestamp = DateTime.UtcNow
};

// 自动使用 document.Id 作为文档 ID
var response = client.IndexDocument(document);

HTTP 请求

POST /my-index/_doc/1 HTTP/1.1
Host: localhost:9200
Content-Type: application/json
Accept: application/json

{
  "id": 1,
  "name": "Test Document",
  "timestamp": "2023-10-05T12:34:56.789Z"
}
2. 显式指定操作参数

使用 Index 方法并通过 IndexDescriptor<T> 精细控制索引行为

var response = client.Index(new IndexRequest<MyDocument>(document)
{
    Index = "my-index",      // 指定目标索引名称
    Id = document.Id,        // 显式设置文档 ID
    Routing = "user-123",    // 按用户ID路由分片
    Refresh = Refresh.True,  // 操作后立即刷新索引(确保实时可见)
    Version = 5              // 乐观并发控制(仅当文档版本为5时更新)
});

关键参数详解

通过 IndexDescriptor<T> 可配置以下核心参数:

方法/参数 说明
.Id(id) 手动指定文档 ID(默认从对象的 Id 属性获取)
.Index("index-name") 指定文档存储的索引名称(若未设置,使用类名小写化,如 mydocument
.Routing("route-key") 设置路由键,控制文档存储在哪个分片
.Version(version) 乐观锁控制,仅当文档当前版本匹配时更新
.Refresh(Refresh.True) 操作后立即刷新索引(默认false,若需实时查询需开启,但影响性能)
.Timeout("30s") 等待分片响应的超时时间(防止长时间阻塞)
.OpType(OpType.Create) 强制仅创建文档(若ID已存在则失败,避免覆盖)
.Pipeline("my-pipeline") 指定预处理管道(如数据转换、字段增强)

高级用法场景

1. 条件索引(避免覆盖)

使用 OpType.Create 强制仅创建新文档,若 ID 存在则抛出异常:

var response = client.Index(document, i => i
    .Id(document.Id)
    .OpType(OpType.Create)  // 类似于 PUT with _create
);
2. 乐观并发控制

通过版本号或时间戳确保并发更新安全:

// 使用版本号控制
var response = client.Index(document, i => i
    .Version(document.Version) // 文档当前版本号(从ES读取时获取)
    .VersionType(VersionType.External) // 版本由外部系统管理
);

// 使用时间戳控制
var response = client.Index(document, i => i
    .IfSequenceNumber(10)   // 仅当序列号匹配时更新
    .IfPrimaryTerm(2)
);
3. 动态索引名称

根据数据内容动态选择索引(如按时间分片):

string monthlyIndex = $"logs-{DateTime.UtcNow:yyyy-MM}";
var response = client.Index(document, i => i
    .Index(monthlyIndex)
);

最佳实践建议

  1. ID 生成策略

    • 业务ID:若文档有唯一业务标识(如订单号),优先手动指定 Id
    • 自动生成:无业务ID时,可省略 Id,Elasticsearch 会自动生成唯一 ID(GUID)。
  2. 路由优化

    • 对高频查询场景,通过 Routing 将相同路由键的文档存于同一分片,提升查询效率。
  3. 性能调优

    • 避免频繁刷新:批量导入时设置 Refresh(false) 减少刷新开销。
    • 合理分片:根据数据规模设计分片数,避免单个分片过大(推荐 10-50GB)。
  4. 错误处理

    • 捕获 ElasticsearchClientException 处理版本冲突、索引不存在等异常。
    • 使用重试机制应对网络波动或临时故障。

完整示例

var document = new Product 
{
    Id = "p-100",
    Name = "Elasticsearch Guide",
    Price = 45.99,
    Category = "books"
};

var indexResponse = client.Index(document, i => i
    .Index("products")
    .Id(document.Id)
    .Routing(document.Category)  // 按分类路由分片
    .Timeout("10s")
    .Refresh(Refresh.WaitFor)    // 等待刷新完成后再返回
    .Pipeline("product-enricher") // 通过管道处理数据
);

if (indexResponse.IsValid)
{
    Console.WriteLine($"文档索引成功!版本:{indexResponse.Version}");
}
else
{
    Console.WriteLine($"错误:{indexResponse.DebugInformation}");
}

通过 IndexRequest<T>IndexDescriptor<T>,开发者可以灵活控制文档的索引行为,结合 Elasticsearch 的版本控制、路由策略和性能优化选项,构建高效可靠的数据写入逻辑。

2 BulkDescriptor#

批量操作(索引、更新、删除)的容器。

client.Bulk(b => b
    .Index("my-index")
    .IndexMany(documents)
);

3 UpdateRequest<T> / UpdateDescriptor<T>#

更新文档内容或脚本操作。


4. 查询与过滤类#

  • QueryContainer / QueryContainerDescriptor<T>
    构建查询条件(如 termmatchbool 查询)。

    var searchResponse = client.Search<MyDocument>(s => s
        .Query(q => q
            .Match(m => m.Field(f => f.Name).Query("test"))
        )
    );
    
  • SearchRequest<T> / SearchDescriptor<T>
    定义搜索请求的参数(分页、排序、高亮等)。

  • BoolQuery
    组合多个查询条件(mustshouldmust_not)。

(由于技术原因,联网搜索暂不可用)

以下是关于 Elasticsearch 查询组件 QueryContainer / QueryContainerDescriptor<T>SearchRequest<T> / SearchDescriptor<T>BoolQuery 的详细说明及示例:


1. QueryContainer 与 QueryContainerDescriptor#

作用

  • QueryContainer:表示一个查询条件的容器,可以包含任意类型的查询(如 MatchQueryTermQueryBoolQuery 等)。
  • QueryContainerDescriptor<T>:通过 Fluent API 构建查询条件的描述符,最终生成 QueryContainer

核心功能

  • 构建单个查询条件(如 MatchTermRange)。
  • 组合多个查询条件(通过 BoolQuery)。
  • 支持链式调用语法。

示例

1.1 基本查询(Match Query)

var searchResponse = client.Search<MyDocument>(s => s
    .Query(q => q
        .Match(m => m
            .Field(f => f.Name)    // 指定字段(自动映射为小写 `name`)
            .Query("test")         // 搜索关键词
            .Operator(Operator.And) // 关键词逻辑关系(AND 或 OR)
        )
    )
);

生成的 JSON 查询

{
  "query": {
    "match": {
      "name": {
        "query": "test",
        "operator": "and"
      }
    }
  }
}

1.2 Term 精确匹配

var query = new QueryContainerDescriptor<MyDocument>()
    .Term(t => t
        .Field(f => f.Status)  // 字段名 `status`
        .Value("published")    // 精确匹配值
    );

var searchResponse = client.Search<MyDocument>(s => s.Query(_ => query));

生成的 JSON 查询

{
  "query": {
    "term": {
      "status": {
        "value": "published"
      }
    }
  }
}

1.3 多条件组合

使用 QueryContainer 组合多个条件:

QueryContainer combinedQuery = new QueryContainerDescriptor<MyDocument>()
    .Match(m => m.Field(f => f.Title).Query("elasticsearch"))
    && new QueryContainerDescriptor<MyDocument>()
        .Range(r => r.Field(f => f.Price).GreaterThan(100));

var searchResponse = client.Search<MyDocument>(s => s.Query(_ => combinedQuery));

生成的 JSON 查询

{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "elasticsearch" } },
        { "range": { "price": { "gt": 100 } } }
      ]
    }
  }
}

2. SearchRequest 与 SearchDescriptor#

作用

  • SearchRequest<T>:定义搜索请求的配置(分页、排序、高亮、聚合等)。
  • SearchDescriptor<T>:通过 Fluent API 构建搜索请求的描述符。

核心功能

  • 分页(From/Size)。
  • 排序(Sort)。
  • 高亮(Highlight)。
  • 聚合(Aggregations)。
  • 源过滤(Source)。

示例

2.1 分页与排序

var searchResponse = client.Search<MyDocument>(s => s
    .Query(q => q.MatchAll())           // 匹配所有文档
    .From(0)                            // 从第0条开始(第一页)
    .Size(10)                           // 返回10条结果
    .Sort(sort => sort
        .Descending(f => f.Price)       // 按价格降序
        .Ascending(f => f.CreatedAt)    // 按创建时间升序
    )
);

生成的 JSON 请求

{
  "query": { "match_all": {} },
  "from": 0,
  "size": 10,
  "sort": [
    { "price": "desc" },
    { "createdAt": "asc" }
  ]
}

2.2 高亮显示

var searchResponse = client.Search<MyDocument>(s => s
    .Query(q => q
        .Match(m => m.Field(f => f.Description).Query("search engine"))
    )
    .Highlight(h => h
        .Fields(f => f
            .Field(fd => fd.Description)
            .PreTags("<em>")
            .PostTags("</em>")
        )
    )
);

生成的 JSON 请求

{
  "query": {
    "match": {
      "description": "search engine"
    }
  },
  "highlight": {
    "fields": {
      "description": {
        "pre_tags": ["<em>"],
        "post_tags": ["</em>"]
      }
    }
  }
}

3. BoolQuery#

作用

用于组合多个查询条件的逻辑关系:

  • Must:所有条件必须满足(AND 逻辑)。
  • Should:至少满足一个条件(OR 逻辑)。
  • MustNot:必须不满足条件(NOT 逻辑)。
  • Filter:过滤条件(不参与相关性评分,仅过滤)。

示例

3.1 复杂逻辑组合

var searchResponse = client.Search<MyDocument>(s => s
    .Query(q => q
        .Bool(b => b
            .Must(
                m => m.Match(mq => mq.Field(f => f.Title).Query("elasticsearch")),
                m => m.Range(r => r.Field(f => f.Price).GreaterThan(100))
            )
            .Should(
                s => s.Term(t => t.Field(f => f.Category).Value("books")),
                s => s.Term(t => t.Field(f => f.Category).Value("tutorials"))
            )
            .MustNot(
                mn => mn.Term(t => t.Field(f => f.Status).Value("deleted"))
            )
            .Filter(
                f => f.DateRange(d => d.Field(f => f.CreatedAt).GreaterThan("2023-01-01"))
            )
        )
    )
);

生成的 JSON 查询

{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "elasticsearch" } },
        { "range": { "price": { "gt": 100 } } }
      ],
      "should": [
        { "term": { "category": "books" } },
        { "term": { "category": "tutorials" } }
      ],
      "must_not": [
        { "term": { "status": "deleted" } }
      ],
      "filter": [
        { "range": { "createdAt": { "gt": "2023-01-01" } } }
      ]
    }
  }
}

3.2 过滤与评分的分离

var searchResponse = client.Search<MyDocument>(s => s
    .Query(q => q
        .Bool(b => b
            .Must(
                m => m.Match(mq => mq.Field(f => f.Description).Query("fast"))
            )
            .Filter(
                f => f.Term(t => t.Field(f => f.IsPublic).Value(true)),
                f => f.Range(r => r.Field(f => f.Rating).GreaterThan(4))
            )
        )
    )
);

生成的 JSON 查询

{
  "query": {
    "bool": {
      "must": [
        { "match": { "description": "fast" } }
      ],
      "filter": [
        { "term": { "isPublic": true } },
        { "range": { "rating": { "gt": 4 } } }
      ]
    }
  }
}

4 总结#

  • QueryContainerQueryContainerDescriptor<T> 用于构建单个或组合查询条件。
  • SearchRequest<T>SearchDescriptor<T> 用于定义搜索的全局参数(分页、排序、高亮等)。
  • BoolQuery 是组合复杂查询逻辑的核心工具,支持 mustshouldmust_notfilter

通过灵活使用这些组件,可以实现从简单到复杂的搜索需求,并优化查询性能和结果相关性。

MultiSearchDescriptor 是 NEST 框架中用于构建 多搜索请求 的类。它允许你在一次 API 调用中发送多个独立的搜索请求,并一次性获取所有搜索结果。这种方式可以显著减少网络开销,特别适合需要同时执行多个查询的场景。


5 MultiSearchDescriptor 多个搜索合并一个请求#

  • 批量执行搜索请求:将多个搜索请求打包成一个请求发送到 Elasticsearch。
  • 减少网络开销:避免多次 HTTP 请求的开销。
  • 统一管理搜索结果:所有搜索结果会在一个响应中返回,便于后续处理。

MultiSearchDescriptor 的使用场景

  1. 同时查询多个索引或类型。
  2. 执行多个独立的查询条件(如不同字段、不同过滤条件)。
  3. 需要减少网络请求次数以提高性能。

MultiSearchDescriptor 的基本用法

以下是一个简单的示例,展示如何使用 MultiSearchDescriptor 执行多个搜索请求:

var client = new ElasticClient(new ConnectionSettings(new Uri("http://localhost:9200")));

// 定义多个搜索请求
var multiSearchResponse = client.MultiSearch(ms => ms
    .Search<MyDocument>("first-search", s => s
        .Query(q => q.Match(m => m.Field(f => f.Name).Query("test")))
    )
    .Search<MyDocument>("second-search", s => s
        .Query(q => q.Term(t => t.Field(f => f.Category).Value("electronics")))
    )
);

// 获取每个搜索请求的结果
var firstSearchResponse = multiSearchResponse.GetResponse<MyDocument>("first-search");
var secondSearchResponse = multiSearchResponse.GetResponse<MyDocument>("second-search");

MultiSearchDescriptor 的主要方法

  • Search<T>
    添加一个搜索请求,指定搜索的名称和查询条件。

    .Search<MyDocument>("search-name", s => s
        .Query(q => q.MatchAll())
    )
    
  • GetResponse<T>
    MultiSearchResponse 中获取指定名称的搜索请求结果。

    var response = multiSearchResponse.GetResponse<MyDocument>("search-name");
    

MultiSearchDescriptor 的响应

  • MultiSearchResponse
    包含所有搜索请求的响应结果。
    • 通过 GetResponse<T> 方法获取指定名称的搜索结果。
    • 可以通过索引或名称访问每个独立的 ISearchResponse<T>

MultiSearchDescriptor 的完整示例

以下是一个更完整的示例,展示如何定义多个搜索请求并处理结果:

var client = new ElasticClient(new ConnectionSettings(new Uri("http://localhost:9200")));

// 定义多个搜索请求
var multiSearchResponse = client.MultiSearch(ms => ms
    .Search<MyDocument>("search-by-name", s => s
        .Query(q => q.Match(m => m.Field(f => f.Name).Query("test")))
        .Size(10)
    )
    .Search<MyDocument>("search-by-category", s => s
        .Query(q => q.Term(t => t.Field(f => f.Category).Value("electronics")))
        .Size(5)
    )
);

// 检查是否成功
if (multiSearchResponse.IsValid)
{
    // 获取第一个搜索请求的结果
    var searchByNameResponse = multiSearchResponse.GetResponse<MyDocument>("search-by-name");
    if (searchByNameResponse.IsValid)
    {
        var documents = searchByNameResponse.Documents;
        Console.WriteLine($"Found {documents.Count} documents by name.");
    }

    // 获取第二个搜索请求的结果
    var searchByCategoryResponse = multiSearchResponse.GetResponse<MyDocument>("search-by-category");
    if (searchByCategoryResponse.IsValid)
    {
        var documents = searchByCategoryResponse.Documents;
        Console.WriteLine($"Found {documents.Count} documents by category.");
    }
}
else
{
    Console.WriteLine("MultiSearch request failed.");
}

MultiSearchDescriptor 的优点

  1. 性能优化:减少网络请求次数,降低延迟。
  2. 代码简洁:将多个搜索请求集中管理,便于维护。
  3. 灵活性:支持不同类型的搜索请求(如不同索引、不同查询条件)。

注意事项

  • 每个搜索请求的名称必须唯一,否则会覆盖之前的请求。
  • 如果某个搜索请求失败,不会影响其他请求的执行。
  • 需要确保 Elasticsearch 集群能够处理批量请求的负载。

通过 MultiSearchDescriptor,你可以高效地执行多个搜索请求,并统一管理结果,非常适合复杂查询场景。


5. 聚合类#

  • AggregationContainer / AggregationContainerDescriptor<T>
    构建聚合分析(如 termsdate_histogram)。

    var response = client.Search<MyDocument>(s => s
        .Aggregations(a => a
            .Terms("group_by_field", t => t.Field(f => f.Category))
        )
    );
    
  • TermsAggregation / DateHistogramAggregation
    具体聚合类型的实现。

在 Elasticsearch 的 NEST 客户端中,使用 AggregationContainer 和对应的描述器(如 TermsAggregation/DateHistogramAggregation)可以灵活构建聚合分析。以下是不同场景下的实现示例:


1. Terms 聚合(按字段分组统计)#

按某个字段的值分组,统计每个分组的文档数量:

var response = client.Search<MyDocument>(s => s
    .Aggregations(a => a
        .Terms("group_by_category", t => t
            .Field(f => f.Category) // 指定分组字段
            .Size(10)               // 返回前10个分组
            .Order(o => o.CountDescending()) // 按文档数降序排序
        )
    )
);

// 提取聚合结果
var termsAgg = response.Aggregations.Terms("group_by_category");
foreach (var bucket in termsAgg.Buckets)
{
    Console.WriteLine($"Category: {bucket.Key}, Count: {bucket.DocCount}");
}

json

{
  "aggs": {
    "group_by_category": {
      "terms": {
        "field": "Category",
        "size": 10,
        "order": {
          "_count": "desc"
        }
      }
    }
  }
}

2. Date Histogram 聚合(按时间间隔分组)#

按时间间隔(如按月)分组统计文档数量:

var response = client.Search<MyDocument>(s => s
    .Aggregations(a => a
        .DateHistogram("group_by_month", dh => dh
            .Field(f => f.Timestamp)    // 指定时间字段
            .CalendarInterval(DateInterval.Month) // 按月分组
            .Format("yyyy-MM")          // 格式化日期输出
        )
    )
);

// 提取聚合结果
var dateHistogram = response.Aggregations.DateHistogram("group_by_month");
foreach (var bucket in dateHistogram.Buckets)
{
    Console.WriteLine($"Month: {bucket.Key}, Count: {bucket.DocCount}");
}

json

{
  "aggs": {
    "group_by_month": {
      "date_histogram": {
        "field": "Timestamp",
        "calendar_interval": "month",
        "format": "yyyy-MM"
      }
    }
  }
}

3. 嵌套聚合(子聚合)#

在分组后,对子组进行二次聚合(如计算平均值):

var response = client.Search<MyDocument>(s => s
    .Aggregations(a => a
        .Terms("group_by_category", t => t
            .Field(f => f.Category)
            .Aggregations(sa => sa
                .Average("avg_price", avg => avg.Field(f => f.Price)) // 子聚合:计算价格平均值
            )
        )
    )
);

// 提取主聚合和子聚合结果
var termsAgg = response.Aggregations.Terms("group_by_category");
foreach (var bucket in termsAgg.Buckets)
{
    var avgPrice = bucket.Average("avg_price");
    Console.WriteLine($"Category: {bucket.Key}, Avg Price: {avgPrice.Value}");
}

json

{
  "aggs": {
    "group_by_category": {
      "terms": {
        "field": "Category"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "Price"
          }
        }
      }
    }
  }
}

4. 多级聚合(复杂嵌套)#

结合多层聚合,例如先按时间分组,再按类别分组,最后计算统计指标:

var response = client.Search<MyDocument>(s => s
    .Aggregations(a => a
        .DateHistogram("group_by_month", dh => dh
            .Field(f => f.Timestamp)
            .CalendarInterval(DateInterval.Month)
            .Aggregations(sa => sa
                .Terms("subgroup_by_category", t => t
                    .Field(f => f.Category)
                    .Aggregations(ssa => ssa
                        .Sum("total_sales", sum => sum.Field(f => f.Sales))
                    )
                )
            )
        )
    )
);

// 解析多层聚合结果
var dateHistogram = response.Aggregations.DateHistogram("group_by_month");
foreach (var monthBucket in dateHistogram.Buckets)
{
    Console.WriteLine($"Month: {monthBucket.Key}");
    var termsAgg = monthBucket.Terms("subgroup_by_category");
    foreach (var categoryBucket in termsAgg.Buckets)
    {
        var totalSales = categoryBucket.Sum("total_sales");
        Console.WriteLine($"  Category: {categoryBucket.Key}, Sales: {totalSales.Value}");
    }
}

json

{
  "aggs": {
    "group_by_month": {
      "date_histogram": {
        "field": "Timestamp",
        "calendar_interval": "month"
      },
      "aggs": {
        "subgroup_by_category": {
          "terms": {
            "field": "Category"
          },
          "aggs": {
            "total_sales": {
              "sum": {
                "field": "Sales"
              }
            }
          }
        }
      }
    }
  }
}

关键类型说明#

  • TermsAggregation: 对应 Elasticsearch 的 terms 聚合,用于按字段值分组。
  • DateHistogramAggregation: 对应 date_histogram 聚合,用于按时间区间分组。
  • AggregationContainerDescriptor<T>: 通过链式调用配置聚合参数(如字段名、排序规则)。
  • AggregationDictionary: 响应中的聚合结果容器,通过聚合名称(如 group_by_category)获取结果。

通过组合这些聚合类型和方法,可以构建从简单到复杂的数据分析逻辑,满足多维度的统计需求。


6. 响应类#

  • ISearchResponse<T>
    搜索操作的响应结果,包含文档和聚合数据。

    var documents = searchResponse.Documents; // 获取匹配的文档
    var termsAgg = searchResponse.Aggregations.Terms("group_by_field");
    
  • IndexResponse / BulkResponse
    单个文档索引或批量操作的响应结果。


7. 映射与类型类#

  • TypeMappingDescriptor<T>
    定义索引的字段映射(类型、分词器、是否存储等)。

    client.Indices.Create("my-index", c => c
        .Map<MyDocument>(m => m
            .AutoMap() // 自动映射
            .Properties(p => p
                .Text(t => t.Name(n => n.Name).Analyzer("standard"))
            )
        )
    );
    
  • 属性注解
    通过属性标记字段映射(如 [Text(Name = "field_name")])。


8. 辅助工具类#

  • Id
    表示文档的唯一标识,支持多种类型(如 stringint)。

    var id = new Id(document);
    
  • Routing
    指定文档的路由值。

  • Nest.JsonNetSerializer
    集成 JSON.NET 序列化器,支持自定义序列化逻辑。


9. 异步操作#

所有方法均有异步版本(如 SearchAsyncIndexAsync),支持 async/await


示例场景

创建索引并索引文档
var client = new ElasticClient(new ConnectionSettings(new Uri("http://localhost:9200")));

// 创建索引
client.Indices.Create("my-index", c => c
    .Map<MyDocument>(m => m.AutoMap())
);

// 索引文档
var document = new MyDocument { Id = 1, Name = "Example" };
client.IndexDocument(document);
执行复杂搜索
var response = client.Search<MyDocument>(s => s
    .Query(q => q
        .Bool(b => b
            .Must(mu => mu.Match(m => m.Field(f => f.Name).Query("test"))))
    )
    .Aggregations(a => a
        .DateHistogram("by_month", dh => dh.Field(f => f.Date).CalendarInterval(DateInterval.Month))
    )
);

以上是 NEST 中常用的核心类,覆盖了索引管理、文档操作、搜索、聚合等主要功能。具体使用时需结合 Elasticsearch 的版本和 NEST 的文档调整语法。

作者:【唐】三三

出处:https://www.cnblogs.com/tangge/p/18697617

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   【唐】三三  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示