Elasticsearch快速精通(.net)


简介

Elasticsearch 是一个开源的、分布式的搜索与分析引擎,基于 Apache Lucene 构建,专为处理大规模数据的实时全文搜索、结构化搜索、日志分析、指标监控等场景而设计。它使用 倒排索引 作为核心数据结构,并通过 RESTful API 提供简单易用的交互方式。

核心特性

  1. 分布式架构
    数据自动分片(shard)并分布到集群中的多个节点。
    支持水平扩展,轻松应对 PB 级数据。
    内置高可用:副本(replica)机制保证数据容错和查询负载均衡。
  2. 近实时搜索(Near Real-Time, NRT)
    文档写入后默认约 1 秒内可被搜索到(可配置刷新间隔)。
    适用于日志、监控、用户行为分析等对时效性要求较高的场景。
  3. 强大的全文检索能力
    基于 Lucene,支持:
    分词(中文需配合插件如 IK Analyzer)
    布尔查询、短语匹配、模糊搜索、通配符
    相关性打分(TF-IDF、BM25 等)
    高亮显示匹配内容
  4. 结构化与非结构化数据支持
    可索引 JSON 格式的文档,字段类型灵活(text、keyword、date、geo_point 等)。
    支持动态映射(dynamic mapping)或显式定义 schema(mapping)。
  5. 聚合分析(Aggregations)
    类似 SQL 的 GROUP BY、统计、直方图、地理围栏等。
    用于构建仪表盘、业务洞察、异常检测等。
  6. RESTful API + JSON
    所有操作通过 HTTP 接口完成,返回 JSON 格式结果。
    易于与各种语言(Java、Python、C# 等)集成。

1.倒排索引

什么是倒排索引?如何理解数据库索引和倒排索引的区别?

在我们平时使用的数据库中,索引就是一个key-value的Map,记录着每条数据的键值(key)和内容,我们只要知道键值(key)就能快速找到我们想要的数据,但是当我们不知道键值(key)时,就需要全表扫描,非常消耗性能。生活中常见的例子就是字典,我们可以根据目录(key的列表)快速找到我们需要查找的数据,单根据内容查找每个出现的地方就需要逐行逐页;
而倒排索引,则是将原有的key-value关系倒置,通过分词,把value拆解成一个个词项(term)作为Key,而value则替换成包含词项(term)的key的集合(还包含其他内容)。

示例

假设有以下三个文档:
Doc1: “小明爱吃西瓜和桃子”
Doc2: “小美爱吃西瓜和草莓”
Doc3: “小月喜欢喝奶茶”
构建倒排索引如下(简化版):

词项 文档ID
小明 [1]
小美 [2]
小月 [3]
喜欢 [3]
爱吃 [1,2]
西瓜 [1,2]
桃子 [1]
草莓 [2]
奶茶 [3]
[1, 2]
[3]

这样的设计就造成elasticsearch和关系型数据库完全不同的应用场景,关系数据库依靠索引和事务机制,主要用于结构化数据的存储场景,而elasticsearch则依靠倒排索引,成为了全文检索,内容分析场景中的利器。


2.分片,副本,路由和分析器

(1)、分片(Shard)—— 数据的水平切分
数据拆分:将一个索引(Index)的数据拆分成多个物理单元,分布到集群的不同节点上,对于大量数据,增加分片能优化查询速度,分散负载。
并行查询:查询可同时在多个分片上执行,提升吞吐量。
类型:
主分片(Primary Shard):存储实际数据
副本分片(Replica Shard):主分片的拷贝(不参与写,由主分片同步)

(2)副本(Replica)—— 高可用与读扩展
高可用:当某个节点宕机,副本可接管服务,避免数据丢失。
提升读性能:搜索和聚合请求可由主分片或任意副本分片处理,实现负载均衡。
增强吞吐量:更多副本来承担并发查询压力

(3)路由(Routing)——分片的地址簿
路由控制:控制文档写入/读取时落在哪个主分片上,默认情况下,ES 使用文档的_id作为路由值,通过哈希计算目标分片,在读写时可显示指定routing值

PUT /orders/_doc/1001?routing=user_id_666
{
  "product": "apple",
  "user_id": "user_id_666"
}

注意:指定routing,可以增加分片内的关联性和进行租户的数据隔离,如使用帖子的postid作为routing值存取评论,能使帖子的所有评论都在一个分片下,但是可能会造成分片过大,数据倾斜。

(4)分析器——文本智能处理引擎
将原始文本进行过滤,拆分,记录,构建索引的词项(term)
三大组件
一. Character Filter
在分词前处理字符,如:去除特殊表情符号和标签
二. Tokenizer(分词器)
核心组件,负责切分文本
三. Token Filter
对分词结果进一步处理,如:大小写转换

自定义分析器:
自定义名称为my_chinese_analyzer的分析器,type为custom的analyzer,tokenizer使用tokenizer,将所有英文字符转为小写

PUT /news_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_chinese_analyzer": {
          "type": "custom",
          "tokenizer": "ik_max_word",
          "filter": ["lowercase"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "my_chinese_analyzer"
      }
    }
  }
}

扩展:
如何查看分析器的分词结果
语法:

POST /_analyze
{
  "analyzer": "分析器名称",
  "text": "要分析的文本"
}

查询:

POST /_analyze
{
  "analyzer": "ik_max_word",
  "text": "湖南欢迎您"
}

结果:

{
    "tokens":[
        {
            "token":"湖南",
            "start_offset":0,
            "end_offset":2,
            "type":"CN_WORD",
            "position":0
        },
        {
            "token":"欢迎您",
            "start_offset":2,
            "end_offset":5,
            "type":"CN_WORD",
            "position":1
        },
        {
            "token":"欢迎",
            "start_offset":2,
            "end_offset":4,
            "type":"CN_WORD",
            "position":2
        },
        {
            "token":"您",
            "start_offset":4,
            "end_offset":5,
            "type":"CN_CHAR",
            "position":3
        }
    ]
}

3.数据类型

类型 说明
text 用于全文搜索,会被分词器(analyzer)处理。适用于正文、描述等。
keyword 用于精确值匹配、聚合、排序。不分词,保留原值。常用于标签、状态、ID 等。
long 64 位整数(-2⁶³ ~ 2⁶³-1)
integer 32 位整数
short 16 位整数
byte 8 位整数
double 64 位浮点数
float 32 位浮点数
half_float 16 位浮点
scaled_float 基于 long 的浮点数,通过缩放因子存储(如 price: { “type”: “scaled_float”, “scaling_factor”: 100 } 表示 19.99 存为 1999)
date 支持多种格式(ISO8601、Unix 时间戳 ms/s 等)。默认以毫秒存储为 long
date_nanos 纳秒级时间(Elasticsearch 7.0+),更高精度,但占用更多空间。
boolean 接受 true/false
binary 二进制Base64 编码的字符串,仅存储
geo_point 经纬度点
geo_shape 复杂地理形状(多边形、线、圆等),用于地理围栏、区域查询
ip 存储 IPv4 或 IPv6 地址,支持范围查询(如 192.168.0.0/16)

特殊类型

  1. 对象类型(object)
    以json形式存储对象,通过"."进行访问,如address.city

  2. 嵌套类型(nested)
    集合存储对象,需要通过nested 进行构建索引和查询

"comments": {
  "type": "nested",
  "properties": {
    "content": { "type": "text" }
  }
}

4.连接认证和索引构建(mapping)

(1)连接和认证

  1. 用户名/密码(Basic Auth)
  2. API Key
  3. TLS/SSL 证书

(2)构建索引

PUT /comments    //索引名称
{
  "settings": {
    "number_of_shards": 2,   //两个分片
    "number_of_replicas": 1,    //一个副本
    "mappings": {
      "properties": {
        "userId": { "type": "keyword" },
        "articleId": { "type": "keyword" },
        "content": {
          "type": "text",
          "analyzer": "my_ik_index",//索引时ik_max_word
          "search_analyzer": "my_ik_search"//搜索时用 ik_smart
        },
        "createdAt": { "type": "date" },
        "rating": { "type": "integer" }
    }
  }
}

使用.net客户端:

var mapping = await client.Indices.CreateAsync<CommentModel>(op => op.Index("comments")
   .Settings(s => s.NumberOfShards(2).NumberOfReplicas(1))
   .Mappings(op =>
   {
       op.Properties(p => p
           .Keyword(n => n.UserId, t => t.Index(true))
           .Keyword(n => n.ArticleId)
           .Text(t => t.Content, key => key.Analyzer("ik_max_word").SearchAnalyzer("ik_smart"))
           .Date(t => t.CreatedAt)
           .IntegerNumber(t => t.Rating)
           );
   }));

5.增删改查


nuget包: Elastic.Clients.Elasticsearch

(1)插入数据
插入文档由两种方式,分别时指定id的put请求和自动生成id的post请求,在插入时我们可以在url后面拼接?refresh=wait_for(控制刷新)或?routing=user123(控制路由)或?op_type=create(指定创建,返回409表示数据已存在)

http请求:

PUT /comments/_doc/1
{
  "user_id": "U1001",
  "article_id": "A2001",
  "content": "这篇文章写得非常好,内容详实且通俗易懂!",
  "created_at": "2025-11-26T14:30:00Z",
  "rating": 5
}

POST /comments/_doc
{
  "user_id": "U1001",
  "article_id": "A2001",
  "content": "这篇文章写得非常好,内容详实且通俗易懂!",
  "created_at": "2025-11-26T14:30:00Z",
  "rating": 5
}

使用.net客户端(如果id存在会进行覆盖更新):

var comment = new Comment
{
    UserId = "U1001",
    ArticleId = "A2001",
    Content = "这篇文章非常棒!",
    CreatedAt = DateTime.UtcNow,
    Rating = 5
};

//指定_id
await client.IndexAsync<CommentModel>(comment, idx => idx
    .Index("comments")
    .Id("1") // 可选,不指定则自动生成,建议给定id
);
//不指定_id
await client.IndexAsync<CommentModel>(comment);

(2)删除数据
http请求:

DELETE /comments/_doc/1
POST /comments/_delete_by_query
{
  "query": {
    "term": {
      "userId": "U999"
    }
  }
}

使用.net客户端:

//删除
await client.DeleteAsync<CommentModel>("1", idx => idx.Index("comments"));

// 删除并等待刷新
await client.DeleteAsync<CommentModel>("comments","1",s=>s.Refresh(Refresh.WaitFor));
//根据搜索条件删除
await client.DeleteByQueryAsync<CommentModel>("comments",d => 
    .Query(q => q.Term(t => t.Field(f => f.UserId).Value("U999")))
);

(3)修改数据
在 Elasticsearch 中文档是不可改变的如果想要更新现有的文档,需要重建索引或者进行替换,而更新文档实际进行了以下几步:
从旧文档构建 JSON==》更改该 JSON==》删除旧文档==》索引一个新文档

http请求:

POST /comments/_update/1
{
  "doc": {
    "rating": 4,
    "content": "文章不错,但有些地方可以更深入。"
  }
}

.net客户端:

var comment = new Comment
{
    UserId = "U1001",
    ArticleId = "A2001",
    Content = "制作不易,一键三连!",
    CreatedAt = DateTime.UtcNow,
    Rating = 5
};
await client.UpdateAsync<CommentModel, CommentModel>("comments", "1", u => u.Doc(comment));

(4)查询数据
一,id查询
http请求:

//_source控制返回字段
GET /comments/_doc/1?_source=content

.net 客户端:

var fields = new Field[] { "content" };

await client.GetAsync<CommentModel>("1", idx => idx.Index("comments").SourceIncludes(fields));

await client.GetAsync<CommentModel>("comments", "1");

(5)批量操作Bulk(官方推荐)
在bulk请求中,可以指定多种类型的操作:

‌index‌:索引一个新文档。如果文档已存在,则默认情况下会替换旧文档。
‌create‌:类似于index,但如果文档已存在,则不会执行操作。
‌update‌:更新一个已存在的文档。
‌delete‌:删除一个文档。

每条操作由两行json组成
第一行指定需要进行的操作,如"_index",“_id”,"_type"等元数据

第二行指定文档内容(仅 index/create/update 需要)

POST /_bulk
{ "index" : { "_index" : "comments", "_id" : "1" } }
{ "userId": "U1001", "articleId": "A2001", "content": "很好!", "createdAt": "2025-11-26T14:30:00Z", "rating": 5 }
{ "index" : { "_index" : "comments", "_id" : "2" } }
{ "userId": "U1002", "articleId": "A2001", "content": "有帮助", "createdAt": "2025-11-27T09:00:00Z", "rating": 4 }
{ "update": { "_index": "comments", "_id": "1" } }
{ "doc": { "rating": 4.5 } }
{ "delete": { "_index": "comments", "_id": "3" } }

(6)扩展:
es的写入机制和持久化
1,写入或者删除请求到达es,进行路由计算,根据哈希计算找到对应的主分片
2,(主分片)数据被写入到内存缓冲区,此时数据不可查询;
同时写入Translog 文件(磁盘)
3,副本分片进行上一步操作,并返回ack给主分片
4,一秒钟后(默认),进行refresh,生成segment,清空缓冲区,写入到文件缓充区中,此时数据才可搜索。
4,Translog 文件到达触发点,如30分钟一次或者文件大小到达阈值,记录条数到达阈值,触发Commit,持久化到磁盘,清空TransLog文件。

如果过程中出现宕机,机器重启后可通过读取Translog 文件的数据写入到内存缓冲区和文件缓存区。

注意:对应一些需要实时的新增或者更新场景,在新增或者更新场景中请设置进行refresh=wait_for,阻塞方法,等待refresh操作,refresh=true性能风险太大。

PUT /comments/_doc/1?refresh=wait_for
{
  "user_id": "U1001",
  "article_id": "A2001",
  "content": "这篇文章写得非常好,内容详实且通俗易懂!",
  "created_at": "2025-11-26T14:30:00Z",
  "rating": 5
}

6.搜索类型(__search)

(1)match 分词匹配查询
对文本进行全文匹配,会经过 search_analyzer 分词,常用参数analyzer(控制搜索分析器),operator(控制多个词之前的布尔逻辑OR/AND),fuzziness(模糊匹配),prefix_length(模糊匹配是,前缀匹配长度),minimum_should_match(operator为OR是,最小匹配数量)。

POST /comments/_search
{
  "query": {
    "match": {
      "content": "人工智能"
    }
  }
}

(2)match_phrase 短语查询
要求词语按顺序且相邻出现

POST /comments/_search
{
  "query": {
    "match_phrase": {
      "content": "发展迅速"
    }
  }
}

(3)multi_match 多字段分词匹配查询
在多个字段中同时搜索

POST /comments/_search
{
  "query": {
    "multi_match": {
      "query": "1001",
      "fields": ["content", "article_id"]
    }
  }
}

(4)query_string 表达式查询
支持 AND/OR/NOT、通配符、正则等方式对内容进行搜索
例:查询content中同时出现apple和world的数据

POST /comments/_search
{
 "query": {
    "query_string": {
      "default_field": "content",
      "query": "(apple)AND(world)"
    }
  }
}

(5)prefix 前缀查询

POST /comments/_search
{
  "query": {
    "prefix": {
      "content": {
        "value": "big"
      }
    }
  }
}

(6)wildcard 通配符查询(不推荐)
? 用来匹配一个任意字符 * 用来匹配多个任意字符

POST /comments/_search
{
  "query": {
    "wildcard": {
      "content": {
        "value": "new*"
      }
    }
  }
}

(7)ids id查询

POST /comments/_search
{
  "query": {
    "ids": {
      "values": ["1", "2"]
    }
  }
}

(8)fuzzy 模糊查询(不推荐,中文支持太差)
重要参数:
编辑距离(fuzziness):默认情况搜索关键词长度为2 不允许存在模糊,索关键词长度为3~5 允许一次模糊,搜索关键词长度为5以上 允许最大模糊数2个(控制召回率,编辑距离太高会导致大量数据被查询)
是否允许交换(transpositions):控制是否允许相邻字符交换,处理拼写错误
前缀长度(prefix_length):控制匹配精度
最大候模糊选词(max_expansions):最多生成多少个模糊候选词(控制性能)

POST /comments/_search
{
  "query": {
    "fuzzy": {
      "user_id": {
        "value": "U1O01",
        "fuzziness": "AUTO",
        "prefix_length": 1,
        "max_expansions": 50 
      }
    }
  }
}

(9)match_bool_prefix分词前缀查询
match_bool_prefix是一种结合布尔查询和前缀匹配的查询方式,适用于需要同时满足多个精确匹配和前缀匹配条件的场景(自 Elasticsearch 7.0 起推荐使用 match_bool_prefix 替代 match_phrase_prefix,因为前者性能更好、更灵活。)

例:

POST /comments/_search
{
  "query": {
    "match_bool_prefix": {
      "content": {
        "value": "紫霞仙"
      }
    }
  }
}

对"紫霞仙"进行match_bool_prefix查询,es会将"紫霞仙"进行分词=》“紫霞”,"仙"两个词,对第一个词进行term精确查询,对最后一个词进行prefix前缀查询。
类似构成下面的请求

POST /comments/_search
{
  "query": {
    "bool" : {
      "should": [
        { "term": { "value": "紫霞" }},
        { "prefix": { "value": "仙"}}
      ]
    }
  }
}

(10)match_all 查询所有

GET /comments/_search
{
   "query": { "match_all": {} }
}  

(11)bool 布尔查询和其四个子句(must,filter,should,must_not)

子句 作用 是否影响评分 类比 SQL
must 必须匹配(AND) WHERE a AND b
filter 必须匹配(AND),具有缓存机制 WHERE a AND b
should 至少满足 N 个(OR) WHERE a OR b
must_not 必须不匹配(NOT) WHERE NOT c
POST /comments/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "content": "人工智能" } }
      ],
      "filter": [
        { "term": { "userId": "U1001" } },
        { "range": { "rating": { "gte": 4 } } }
      ],
      "should": [
        { "match": { "content": "机器学习" } }
      ],
      "must_not": [
        { "term": { "rating": 5 } }
      ],
      "minimum_should_match": 1  //必须满足一个
    }
  }
}

(12)term单值查询和terms多值查询

POST /comments/_search
{
  "query": {
    "term": {
      "userId": "U1001"
    }
  }
}

POST /comments/_search
{
  "query": {
    "terms": {
      "articleId": ["A2001", "A2002", "A2003"]
    }
  }
}

(13)范围查询
gt:大于
gte:大于等于
lt:小于
lte:小于等于

POST /comments/_search
{
  "query": {
    "range": {
      "rating": {
        "gte": 4,
        "lte": 5
      }
    }
  }
}

(14)exists 存在性查询
查找没有填写的字段,类似ISNULL

POST /comments/_search
{
  "query": {
    "bool": {
      "must_not": {
        "exists": {
          "field": "rating"
        }
      }
    }
  }
}

查找填写内容的字段

POST /comments/_search
{
  "query": {
    "exists": {
      "field": "rating"
    }
  }
}

(15)from size 分页查询
from默认0,size默认10,使用search进行搜索时,请注意!

GET /comments/_search
{
   "size": 10, 
   "from": 0
}

(16)nested嵌套查询
针对索引字段类型为nested的数据进行查询的方式
path指定nested类型字段名称
query指定搜索条件

GET /employees/_search
{
  "query": {
    "nested": {
      "path": "skills",
      "query": {
        "term": { "skills.language": "Python" }
      }
    }
  }
}

7.聚合(aggs)

elasticsearch聚合主要有两个概念
桶(Buckets):满足特定条件的文档的集合
指标(Metrics):对桶内的文档进行统计计算
桶在概念上类似于 SQL 的分组(GROUP BY),而指标则类似于 COUNT() 、 SUM() 、 MAX() 等统计方法

以官方的例子扩展解析,如何快速理解写法:

GET /cars/transactions/_search
{
   "size" : 0,
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": { 
            "avg_price": { 
               "avg": {
                  "field": "price" 
               }
            },
            "color_count": {
              "value_count": {
                "field": "color"
              }
            }
         }
      }
   }
}

我们把例子类比为sql==》

select color as colors, vag(price) as avg_price,count(color) as color_count from transactions group by color

(1)首先size是告诉es,返回实际数据条数=0,这样就不会额外返回数据.
(2)其次aggs就是我们聚合的定义,首先要理解每一个aggs代表了一次计算,首先进行的就是外层aggs的terms(sql中的group by)构建了根据颜色分类的桶(Buckets)
(3)最后,内层aggs中有两个子句,分别是
“avg_price”: { “avg”: { “field”: “price” } } → 计算该颜色下所有文档的平均价格
“color_count”: { “value_count”: { “field”: “color” } } → 统计该颜色的文档数量
(4)后续获取数据,也是从aggregations.+桶名(colors)的集合中获取数据

返回内容:

{
...
   "aggregations": {
      "colors": {
         "buckets": [
            {
               "key": "red",
               "doc_count": 4,
               "color_count": {
                  "value": 4
               },
               "avg_price": { 
                  "value": 32500
               }
            },
            {
               "key": "blue",
               "doc_count": 2,
               "color_count": {
                  "value": 2
               },
               "avg_price": {
                  "value": 20000
               }
            },
            {
               "key": "green",
               "doc_count": 2,
               "color_count": {
                  "value": 2
               },
               "avg_price": {
                  "value": 21000
               }
            }
         ]
      }
   }
...
}

.net客户端:

await esClient.SearchAsync<T>(s =>
                s.Index("transactions").Size(0)
                .Aggregations(a =>
                    a.Terms("colors", t => t.Field(f => f.color)
                    .Aggregations(child =>
                         child.ValueCount("color_count", vc => vc.Field(f => f.color))
                              .Avg("avg_price",avg=>avg.Field(f=>f.price))
                        )
                    )
                )
            );

直方图支持(histogram和date_histogramauto_date_histogram)

聚合类型 适用字段 间隔控制 典型场景
histogram 数值(price, age) 固定数值(interval: 100) 价格分布、分数段统计
date_histogram 日期 日历 or 固定时间(day, 1h) 日活、销售趋势、日志分析
auto_date_histogram 日期 自动选择(目标桶数) 通用时间分析

http请求:

GET /cars/transactions/_search
{
   "size" : 0,
   "aggs": {
      "sales": {
         "date_histogram": {
            "field": "sold",
            "interval": "month", 
            "format": "yyyy-MM-dd" 
         },
         "aggs":{
            "revenue": {
               "sum": { 
                 "field" : "price"
               }
             }
         }
      }
   }
}

.net客户端:

await esClient.SearchAsync<T>(s=>s.Index("transactions").Size(0)
            .Aggregations(a=>
                a.DateHistogram("sales",h=>
                    h.Field(f=>f.sold)
                        .CalendarInterval(CalendarInterval.Month)
                        .Format("yyyy-MM-dd")
                                                .Aggregations(child=>child.Sum("revenue",sum=>sum.Field(f=>f.price)))

                    )
                )
            );

8.脚本(script)

脚本(Script) 是一种强大的功能,使用 Painless 语言,允许你在查询、聚合、更新等操作中 动态计算字段值、修改文档、定义复杂逻辑,但是需要注意使用脚本可能会影响性能,非特殊需要不建议使用。

script有两个字段分别是script_score(计算文档得分,影响_score)和script(过滤文档)
source指定脚本内容
params指定参数
lang指定脚本类别

以下介绍常用的三种脚本用法

例1:查询价格加运费大于200的商品

GET /products/_search
{
  "query": {
    "bool": {
      "filter": {
        "script": {
          "script": {
            "source": "doc['price'].value + doc['shipping'].value > params.threshold",
            "params": {
              "threshold": 200
            }
          }
        }
      }
    }
  }
}

.net客户端:

var script = new InlineScript
{
    Source = "doc['price'].value + doc['shipping'].value > params.threshold",
    Params = new Dictionary<string, object>
                            {
                                { "threshold", 200 },
                            },
    Language = "painless"
};

var res = await esClient.SearchAsync<T>(s=>
    s.Index("products")
    .Query(q=>
        q.Bool(b=>
            b.Filter(f=>
                f.Script(s=>s.Script(new Script(script)))
            )
        )
    )
);

例2:更改指定用户的名称

POST /posts/_update_by_query
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "userId": {
              "value": 1001
            }
          }
        }
      ]
    }
  },
  "script": {
    "source": "ctx._source.userName = params.userName;",
    "lang": "painless",
    "params": {
      "userName": "Alice"
    }
  }
}

.net客户端:

await client.UpdateByQueryAsync("posts",d => d
    .Query(q => q
        .Bool(b => b
            .Filter(f => f
                .Term(t => t
                    .Field(fld => fld.UserId)
                    .Value(1001)
                )
            )
        )
    )
    .Script(new Script(new InlineScript
    {
        Source = $@"
            ctx._source.userName = params.userName;",
        Params = new Dictionary<string, object>
        {
            { "userName", "Alice" }
        },
        Language = "painless"
    }))
);

例3:修改内容的排序逻辑,如热值计算

POST /posts/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source":"long likes = doc['likeCount'].value;long comments = doc['commentCount'].value;return Math.log10(1.0 + likes) * 1.6 + Math.log10(1.0 + comments) * 2.5",
        "lang": "painless"
      }
    }
  },
  "sort": [ { "_score": "desc" } ],
  "size": 20
}

.net客户端:

            var script = new InlineScript
            {
                Source = "long likes = doc['likeCount'].value;long comments = doc['commentCount'].value;return Math.log10(1.0 + likes) * 1.6 + Math.log10(1.0 + comments) * 2.5",
                Params = new Dictionary<string, object>{},
                Language = "painless"
            };

            var res = await esClient.SearchAsync<ClientPostModel>(s=>
                s.Index("material_clientpost")
                .Query(q=>
                    q.ScriptScore(ss=>
                        ss.Query(qq=>
                            qq.MatchAll()
                            ).Script(new Script(script))
                        )
                )
            );

9.官网和工具

中文官网地址:https://www.elastic.co/guide/cn/elasticsearch/guide/current/running-elasticsearch.html
es-client(浏览器插件)
地址:https://www.u-tools.cn/plugins/detail/es-client/index.html

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐