第四节:基于Elastic.Clients.Elasticsearch进行索引库和文档的操作
一. 初始化配置
1 事先准备
(1) 类库: Elastic.Clients.Elasticsearch 8.18.3
(2) 初始化注册client
builder.Services.AddSingleton<ElasticsearchClient>(sp =>
{
var configuration = sp.GetRequiredService<IConfiguration>();
var settings = new ElasticsearchClientSettings(new Uri("http://11.11.11.80:9200"))
//.Authentication(new BasicAuthentication("elastic", "your_password")) //账号密码
//.DefaultIndex("t_book") //默认索引库名称, 推荐crud的时候显示传入
.ServerCertificateValidationCallback((sender, cert, chain, errors) => true); // 开发环境忽略证书
return new ElasticsearchClient(settings);
});
builder.Services.AddScoped<BookService>();
(3) 实体准备 (后面章节全部使用这个实体)
public class Book
{
public string id { get; set; }
public string title { get; set; }
public string category { get; set; }
public string author { get; set; }
public decimal price { get; set; }
public DateTime published_date { get; set; }
public List<string> tags { get; set; } = new();
//高亮显示(不需要在ES创建字段)
public string highlightTitle { get; set; }
}
二. 索引库操作
1. 初始化索引
2 删除索引
public class BookService
{
private readonly ElasticsearchClient _client;
private readonly string indexName = "t_book"; //索引库名称,必须显式声明,只能小写
public BookService(ElasticsearchClient client)
{
_client = client;
}
/*****************************************下面是索引库相关操作******************************************/
#region 01-创建索引(带分词器配置)
/// <summary>
/// 01-创建索引(带分词器配置)
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task CreateIndexAsync()
{
var exists = await _client.Indices.ExistsAsync(indexName);
if (exists.Exists) return;
var response = await _client.Indices.CreateAsync(indexName, c => c
.Settings(s => s.NumberOfShards(1).NumberOfReplicas(0))
.Mappings(m => m
.Properties<Book>(p => p
.Keyword(k => k.id)
.Text(t => t.title, f => f.Analyzer("ik_max_word")) // 中文分词(需安装IK插件)
.Text(t => t.author, f => f.Analyzer("ik_smart")) // 中文分词(需安装IK插件)
.Keyword(t=>t.category)
.FloatNumber(n => n.price)
.Date(d => d.published_date)
.Keyword(k => k.tags) // 标签不分词,用于精确查询
)
)
);
if (!response.IsValidResponse)
throw new Exception($"创建索引失败: {response.DebugInformation}");
}
#endregion
#region 02-删除索引
/// <summary>
/// 02-删除索引
/// </summary>
/// <returns></returns>
public async Task DeleteIndexAsync()
{
await _client.Indices.DeleteAsync(indexName);
}
#endregion
}
三. 文档基本CRUD
1 文档的CRUD
BookService代码
public class BookService
{
private readonly ElasticsearchClient _client;
private readonly string indexName = "t_book"; //索引库名称,必须显式声明,只能小写
public BookService(ElasticsearchClient client)
{
_client = client;
}
/*****************************************下面是文档基本相关操作******************************************/
#region 01-单个新增文档
/// <summary>
/// 01-单个新增文档
/// </summary>
/// <param name="book"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<string> AddBookAsync(Book book)
{
//https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/getting-started#_getting_documents
var response = await _client.IndexAsync(book, x => x.Index(indexName)); //官方写法
//var response = await _client.IndexAsync(book); //这种写法必须初始化连接的时候打开.DefaultIndex("t_book"),显然不灵活
if (!response.IsValidResponse)
throw new Exception($"添加文档失败: {response.DebugInformation}");
return response.Id;
}
#endregion
#region 02-批量新增文档
/// <summary>
/// 02-批量新增文档
/// </summary>
/// <param name="books"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task BulkAddBooksAsync(IEnumerable<Book> books)
{
var response = await _client.IndexManyAsync(books, indexName);
if (!response.IsValidResponse)
{
var errors = response.Items
.Where(item => !item.IsValid)
.Select(item => $"文档 {item.Id}: {item.Error?.Reason}")
.ToList();
throw new Exception($"批量添加失败: {string.Join("\n", errors)}");
}
}
#endregion
#region 03-删除文档
/// <summary>
/// 03-删除文档
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<bool> DeleteBookAsync(string id)
{
var response = await _client.DeleteAsync<Book>(indexName, id);
return response.IsSuccess();
}
#endregion
#region 04-更新文档
/// <summary>
/// 04-更新文档
/// </summary>
/// <param name="book"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task UpdateBookAsync(Book book)
{
var response = await _client.UpdateAsync<Book, Book>(indexName, book.id, u => u.Doc(book));
if (!response.IsValidResponse)
throw new Exception($"更新失败: {response.DebugInformation}");
}
#endregion
#region 05-根据ID查询文档
/// <summary>
/// 05-根据ID查询文档
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<Book> GetBookByIdAsync(string id)
{
var response = await _client.GetAsync<Book>(id, idx => idx.Index(indexName));
if (!response.IsValidResponse || !response.Found)
return null;
return response.Source;
}
#endregion
}
控制器中代码
查看代码
#region 01-添加书籍
/// <summary>
/// 01-添加书籍
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> AddBook()
{
//var bookList = new List<Book>
//{
// new() {
// Id = "1",
// Title = "深入理解Elasticsearch:分布式搜索与数据分析",
// Author = "张三",
// Price = 69.99m,
// PublishedDate = DateTime.Now.AddMonths(-3),
// Tags = ["技术", "数据库", "搜索"]
// },
// new() {
// Id = "2",
// Title = ".NET Core微服务架构与实践",
// Author = "李四",
// Price = 79.50m,
// PublishedDate = DateTime.Now.AddMonths(-1),
// Tags = ["编程", "微服务", "架构"]
// },
// new() {
// Id = "3",
// Title = "搜索引擎优化实战",
// Author = "王五",
// Price = 55.00m,
// PublishedDate = DateTime.Now.AddDays(-15),
// Tags = ["SEO", "网络营销"]
// }
//};
//逐个添加
//List<string> idList = [];
//foreach (var book in bookList)
//{
// var id = await _bookService.AddBookAsync(book);
// idList.Add(id);
//}
var bookList = GenerateTestBooks(100); //模拟100条数据插入
//批量添加
await _bookService.BulkAddBooksAsync(bookList);
return Json(new { status = "ok", msg = "添加成功" });
}
#endregion
#region 02-删除书籍
/// <summary>
/// 02-删除书籍
/// </summary>
/// <param name="id">编号Id</param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> DeleteBook(string id)
{
var success = await _bookService.DeleteBookAsync(id);
if (success)
{
return Json(new { status = "ok", msg = "删除成功" });
}
return Json(new { status = "error", msg = "删除失败" });
}
#endregion
#region 03-获取书籍
/// <summary>
/// 03-获取书籍
/// </summary>
/// <param name="id">编号</param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult<Book>> GetBook(string id)
{
var book = await _bookService.GetBookByIdAsync(id);
return Json(new { status = "ok", msg = "获取成功", data = book });
}
#endregion
#region 04-更新书籍
/// <summary>
/// 04-更新书籍
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> UpdateBook()
{
Book newBook = new()
{
id = "1",
title = "深入理解Elasticsearch:分布式搜索与数据分析",
author = "ypf",
price = 69.99m,
published_date = DateTime.Now.AddMonths(-3),
tags = ["技术", "数据库", "搜索"]
};
await _bookService.UpdateBookAsync(newBook);
return Json(new { status = "ok", msg = "获取成功", });
}
#endregion
#region 手动生成数据
[NonAction]
public List<Book> GenerateTestBooks(int count = 100)
{
var books = new List<Book>();
var random = new Random();
var currentId = 1;
// 书籍类别
var categories = new[]
{
"计算机科学", "人工智能", "数据科学", "软件开发", "网络技术",
"云计算", "前端开发", "后端开发", "移动开发", "数据库",
"系统架构", "信息安全", "编程语言", "算法", "设计模式",
"项目管理", "用户体验", "游戏开发", "嵌入式系统", "量子计算"
};
// 书籍标题组件
var titlePrefixes = new[] { "深入理解", "精通", "实战", "高级", "现代", "分布式", "云原生", "高效" };
var titleTechs = new[] { "Elasticsearch", "微服务", "Kubernetes", "Docker", "React", "Vue", "Angular", "机器学习", "人工智能", "大数据" };
var titleSuffixes = new[] { "原理与实践", "开发指南", "权威指南", "核心原理", "实战手册", "最佳实践", "系统设计", "应用开发" };
// 作者相关
var firstNames = new[] { "张", "王", "李", "赵", "刘", "陈", "杨", "黄", "周", "吴" };
var lastNames = new[] { "伟", "芳", "娜", "敏", "静", "丽", "强", "磊", "军", "洋", "勇", "艳", "杰", "涛" };
// 标签库
var allTags = new[]
{
"技术", "编程", "数据库", "前端", "后端", "云计算", "架构",
"微服务", "容器化", "DevOps", "大数据", "AI", "机器学习",
"网络安全", "移动开发", "Web开发", "算法", "设计模式",
"性能优化", "测试驱动", "敏捷开发", "领域驱动", "开源",
"Java", "C#", "Python", "JavaScript", "Go", "Rust"
};
// 生成书籍
for (int i = 0; i < count; i++)
{
// 生成随机类别
var category = categories[random.Next(categories.Length)];
// 生成随机标题(基于类别)
var title = $"{titlePrefixes[random.Next(titlePrefixes.Length)]}{category}中的{titleTechs[random.Next(titleTechs.Length)]}{titleSuffixes[random.Next(titleSuffixes.Length)]}";
// 30%的几率添加副标题
if (random.NextDouble() < 0.3)
{
title += $": {titlePrefixes[random.Next(titlePrefixes.Length)]}{titleTechs[random.Next(titleTechs.Length)]}应用";
}
// 生成作者
var author = $"{firstNames[random.Next(firstNames.Length)]}{lastNames[random.Next(lastNames.Length)]}";
// 20%的几率添加第二作者
if (random.NextDouble() < 0.2)
{
author += $", {firstNames[random.Next(firstNames.Length)]}{lastNames[random.Next(lastNames.Length)]}";
}
// 生成价格(基于类别)
var basePrice = category switch
{
"人工智能" or "量子计算" => 80,
"数据科学" or "云计算" => 70,
_ => 50
};
var price = Math.Round(basePrice + (random.NextDouble() * 40), 2);
// 10%的几率是高价书
if (random.NextDouble() < 0.1)
{
price = Math.Round(price * 1.5, 2);
}
// 生成出版日期(过去5年内)
var publishedDate = DateTime.Now.AddYears(-5).AddDays(random.Next(5 * 365));
// 生成标签(基于类别)
var tags = new List<string> { category };
// 添加技术标签
tags.AddRange(allTags
.OrderBy(x => random.Next())
.Take(random.Next(2, 5)));
// 确保标签不重复
tags = tags.Distinct().ToList();
// 添加书籍
books.Add(new Book
{
id = currentId++.ToString(),
title = title,
category = category,
author = author,
price = (decimal)price,
published_date = publishedDate,
tags = tags
});
}
// 添加一些特定书籍(用于测试分类和搜索)
books.AddRange(new[]
{
new Book
{
id = "search-1",
title = "Elasticsearch 权威指南:分布式搜索与大数据分析实战",
category = "数据科学",
author = "张明",
price = 89.99m,
published_date = DateTime.Now.AddMonths(-6),
tags = new List<string> { "Elasticsearch", "搜索", "大数据", "分布式系统" }
},
new Book
{
id = "search-2",
title = "实战Elastic Stack:日志管理与数据分析",
category = "数据科学",
author = "李技术",
price = 75.50m,
published_date = DateTime.Now.AddMonths(-3),
tags = new List<string> { "Elasticsearch", "Kibana", "日志分析", "监控" }
},
new Book
{
id = "search-3",
title = "搜索引擎核心技术详解",
category = "计算机科学",
author = "王搜索",
price = 68.00m,
published_date = DateTime.Now.AddMonths(-9),
tags = new List<string> { "搜索引擎", "算法", "信息检索", "自然语言处理" }
},
new Book
{
id = "search-4",
title = ".NET Core中使用Elasticsearch进行高效搜索",
category = "软件开发",
author = "陈开发者",
price = 62.99m,
published_date = DateTime.Now.AddMonths(-2),
tags = new List<string> { ".NET", "C#", "Elasticsearch", "集成" }
},
new Book
{
id = "search-5",
title = "大数据搜索与日志分析:基于Elastic Stack",
category = "数据科学",
author = "赵数据",
price = 82.00m,
published_date = DateTime.Now.AddMonths(-12),
tags = new List<string> { "大数据", "Elasticsearch", "Logstash", "数据分析" }
},
new Book
{
id = "category-1",
title = "人工智能在医疗诊断中的应用",
category = "人工智能",
author = "吴智能",
price = 95.00m,
published_date = DateTime.Now.AddMonths(-4),
tags = new List<string> { "AI", "医疗", "深度学习", "图像识别" }
},
new Book
{
id = "category-2",
title = "量子计算基础与算法实现",
category = "量子计算",
author = "郑量子",
price = 110.00m,
published_date = DateTime.Now.AddMonths(-8),
tags = new List<string> { "量子", "算法", "物理", "计算科学" }
}
});
return books;
}
#endregion
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。

浙公网安备 33010602011771号