Elasticsearch快速精通(.net)
Elasticsearch 是一个开源的、分布式的搜索与分析引擎,基于 Apache Lucene 构建,专为处理大规模数据的实时全文搜索、结构化搜索、日志分析、指标监控等场景而设计。它使用 倒排索引 作为核心数据结构,并通过 RESTful API 提供简单易用的交互方式。
Elasticsearch快速精通(.net)
简介
Elasticsearch 是一个开源的、分布式的搜索与分析引擎,基于 Apache Lucene 构建,专为处理大规模数据的实时全文搜索、结构化搜索、日志分析、指标监控等场景而设计。它使用 倒排索引 作为核心数据结构,并通过 RESTful API 提供简单易用的交互方式。
核心特性
- 分布式架构
数据自动分片(shard)并分布到集群中的多个节点。
支持水平扩展,轻松应对 PB 级数据。
内置高可用:副本(replica)机制保证数据容错和查询负载均衡。- 近实时搜索(Near Real-Time, NRT)
文档写入后默认约 1 秒内可被搜索到(可配置刷新间隔)。
适用于日志、监控、用户行为分析等对时效性要求较高的场景。- 强大的全文检索能力
基于 Lucene,支持:
分词(中文需配合插件如 IK Analyzer)
布尔查询、短语匹配、模糊搜索、通配符
相关性打分(TF-IDF、BM25 等)
高亮显示匹配内容- 结构化与非结构化数据支持
可索引 JSON 格式的文档,字段类型灵活(text、keyword、date、geo_point 等)。
支持动态映射(dynamic mapping)或显式定义 schema(mapping)。- 聚合分析(Aggregations)
类似 SQL 的 GROUP BY、统计、直方图、地理围栏等。
用于构建仪表盘、业务洞察、异常检测等。- 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)
特殊类型
-
对象类型(object)
以json形式存储对象,通过"."进行访问,如address.city -
嵌套类型(nested)
集合存储对象,需要通过nested 进行构建索引和查询
"comments": {
"type": "nested",
"properties": {
"content": { "type": "text" }
}
}
4.连接认证和索引构建(mapping)
(1)连接和认证
- 用户名/密码(Basic Auth)
- API Key
- 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
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)