背景
Elasticsearch升級到8.11了,對應的客戶端外掛也跟著升級,以為會相容Nest的語法,發現自己Too Young Too Simple!
沒辦法,只能去官網找,結果官網只有最基本的增、刪、改、查,只能繼續查資料,發現網上資料很少,避免大家少走彎路,這裡做個簡單的分享。
分享
1.ElasticsearchClient
var esSetting = _baseConfig.Es;
if (esSetting == null || string.IsNullOrEmpty(esSetting.EsHosts))
{
throw new Exception("未配置EsHosts");
}
//讀取ES叢集配置
var nodeUris = GetAllNodes(esSetting);
services.AddSingleton<ElasticsearchClient>(provider =>
{
//這裡用的StaticNodePool,單節點和叢集都支援,大家根據自己實際情況選擇
var pool = new StaticNodePool(nodeUris);
var settings = new ElasticsearchClientSettings(pool);
//如果設定了賬號密碼
if (!string.IsNullOrEmpty(esSetting.EsUser) && !string.IsNullOrEmpty(esSetting.EsPassword))
{
settings.Authentication(new BasicAuthentication(esSetting.EsUser, esSetting.EsPassword));
}
return new ElasticsearchClient(settings);
});
return services;
//settings.EsHosts示例:http://192.168.1.100:9200;http://192.168.1.101:9200;http://192.168.1.102:9200
private static List<Uri> GetAllNodes(EsConfig settings)
{
var nodes = settings.EsHosts.Trim();
string[] nodeshost = nodes.Split(';');
var nodUris = new List<Uri>();
for (int i = 0; i < nodeshost.Length; i++)
{
if (!string.IsNullOrEmpty(nodeshost[i]))
nodUris.Add(new Uri(nodeshost[i]));
}
return nodUris;
}
2.具體使用
2.1注入_client
public class ElasticSearchForInformalEssay : IElasticSearchForInformalEssay
{
private readonly string IndexName = "informal_essay_info";
private readonly ILogger<ElasticSearchForInformalEssay> _logger;
protected ElasticsearchClient _client { get; set; }
public ElasticSearchForInformalEssay(ILogger<ElasticSearchForInformalEssay> logger, ElasticsearchClient client)
{
_logger = logger;
_client = client;
}
}
2.2增加-增加跟Nest版本基本一致
/// <summary>
/// 新增單條資料
/// </summary>
/// <param name="InformalEssay"></param>
/// <returns></returns>
public OutPutResult AddIndex(informal_essay_info InformalEssay)
{
try
{
var resp = _client.Index<informal_essay_info>(InformalEssay, x => x.Index(IndexName));
if (resp.IsValidResponse)
{
return OutPutResult.ok();
}
else
{
return OutPutResult.error(resp.DebugInformation);
}
}
catch (Exception ex)
{
return OutPutResult.error(ex.Message);
}
}
/// <summary>
/// 新增多條資料
/// </summary>
/// <param name="InformalEssays"></param>
/// <returns></returns>
public OutPutResult BulkAddIndex(List<informal_essay_info> InformalEssays)
{
try
{
//var resp = _client.BulkAll<informal_essay_info>(InformalEssays, x => x.Index(IndexName).Size(1000));
var observableBulk = _client.BulkAll(InformalEssays, f => f
.MaxDegreeOfParallelism(8)
.BackOffTime(TimeSpan.FromSeconds(10))
.BackOffRetries(2)
.Size(1000)
.RefreshOnCompleted()
.Index(IndexName)
.BufferToBulk((r, buffer) => r.IndexMany(buffer))
);
var countdownEvent = new CountdownEvent(1);
var bulkAllObserver = new BulkAllObserver(
onNext: response =>
{
//_logger.LogInformation($"索引 {response.Page} 頁,每頁1000條,重試 {response.Retries} 次");
},
onError: ex =>
{
_logger.LogInformation("新增索引異常 : {0}", ex);
countdownEvent.Signal();
},
() =>
{
_logger.LogInformation("新增索引完成");
countdownEvent.Signal();
});
observableBulk.Subscribe(bulkAllObserver);
//var resp = _client.IndexMany(InformalEssays, IndexName);
return OutPutResult.ok();
}
catch (Exception ex)
{
_logger.LogInformation("批次操作異常:", ex);
return OutPutResult.error(ex.Message);
}
}
2.3刪除
/// <summary>
/// Id單條刪除
/// </summary>
/// <param name="InformalEssayId"></param>
/// <returns></returns>
public OutPutResult DeleteById(string InformalEssayId)
{
try
{
var resp = _client.Delete<informal_essay_info>(InformalEssayId, x => x.Index(IndexName));
if (resp.IsValidResponse)
{
return OutPutResult.ok();
}
else
{
return OutPutResult.error(resp.DebugInformation);
}
}
catch (Exception ex)
{
return OutPutResult.error(ex.Message);
}
}
/// <summary>
/// Ids批次刪除
/// </summary>
/// <param name="InformalEssayIds"></param>
/// <returns></returns>
public OutPutResult BulkDeleteById(string InformalEssayIds)
{
try
{
var ids = CommonHelpers.GetStringArray(InformalEssayIds).ToList();
List<FieldValue> terms = new();
ids.ForEach(x =>
{
terms.Add(x);
});
var resp = _client.DeleteByQuery<informal_essay_info>(IndexName, x => x.
Query(q => q.Terms(t => t.Field(f => f.id).Terms(new TermsQueryField(terms))))
.Refresh(true)
);
if (resp.IsValidResponse)
{
return OutPutResult.ok();
}
else
{
return OutPutResult.error(resp.DebugInformation);
}
}
catch (Exception ex)
{
return OutPutResult.error(ex.Message);
}
}
/// <summary>
/// 根據條件批次刪除
/// </summary>
/// <param name="keyword"></param>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
public OutPutResult BulkDeleteByQuery(string keyword, DateTime? start, DateTime? end)
{
try
{
var mustQueries = new List<Action<QueryDescriptor<informal_essay_info>>>();
if (start.HasValue || end.HasValue)
{
long startDate = CommonHelpers.DateTimeToTimeStamp(start ?? DateTime.MinValue);
long endDate = CommonHelpers.DateTimeToTimeStamp(end ?? DateTime.MaxValue);
mustQueries.Add(t => t.Range(r => r.NumberRange(n => n.Field(f => f.setdate).Gte(startDate).Lte(endDate))));
}
if (!string.IsNullOrEmpty(keyword))
{
mustQueries.Add(t => t.MultiMatch(m => m.Fields(
new Field("title", 10).And(new Field("content", 5)).And(new Field("author", 5))
).Operator(Operator.And).Query(keyword)));
}
Action<QueryDescriptor<informal_essay_info>> retQuery = q => q.Bool(b => b.Filter(mustQueries.ToArray()));
var resp = _client.DeleteByQuery<informal_essay_info>(IndexName, s => s
.Query(q => q.Bool(b => b.Must(retQuery)))
);
if (resp.IsValidResponse)
{
return OutPutResult.ok();
}
else
{
return OutPutResult.error(resp.DebugInformation);
}
}
catch (Exception ex)
{
return OutPutResult.error(ex.Message);
}
}
2.4修改
public OutPutResult UpdateIndex(informal_essay_info InformalEssay)
{
try
{
var resp = _client.Index<informal_essay_info>(InformalEssay, x => x.Index(IndexName).Refresh(Refresh.True));
if (resp.IsValidResponse)
{
return OutPutResult.ok();
}
else
{
return OutPutResult.error(resp.DebugInformation);
}
}
catch (Exception ex)
{
return OutPutResult.error(ex.Message);
}
}
/// <summary>
/// 批次修改
/// </summary>
/// <param name="InformalEssays"></param>
/// <returns></returns>
public OutPutResult BulkUpdateIndex(List<informal_essay_info> InformalEssays)
{
try
{
var observableBulk = _client.BulkAll(InformalEssays, f => f
.MaxDegreeOfParallelism(8)
.BackOffTime(TimeSpan.FromSeconds(10))
.BackOffRetries(2)
.Size(1000)
.RefreshOnCompleted()
.Index(IndexName)
.BufferToBulk((r, buffer) => r.IndexMany(buffer))
);
var bulkAllObserver = new BulkAllObserver(
onNext: response =>
{
_logger.LogInformation($"索引 {response.Page} 頁,每頁1000條,重試 {response.Retries} 次");
},
onError: ex =>
{
_logger.LogInformation("更新索引異常 : {0}", ex);
},
() =>
{
_logger.LogInformation("完成更新索引");
});
observableBulk.Subscribe(bulkAllObserver);
return OutPutResult.ok();
}
catch (Exception ex)
{
return OutPutResult.error(ex.Message);
}
}
/// <summary>
/// 根據條件修改(稿件釋出/取消釋出後,更新ES)
/// </summary>
/// <param name="id"></param>
/// <param name="status">1:釋出,0:取消釋出</param>
/// <returns></returns>
public OutPutResult UpdateInformalEssayInfoAfterPublishOrCancel(string id, int status = 1)
{
try
{
var sql = $"ctx._source.status = params.status;";
var scriptParams = new Dictionary<string, object> { { "status", status } };
var req = new UpdateByQueryRequest(IndexName)
{
Query = new TermQuery("id") { Field = "id", Value = id },
Script = new Script(new InlineScript() { Source = sql, Params = scriptParams }),
Refresh = true
};
var resp = _client.UpdateByQuery(req);
if (resp.IsValidResponse)
{
return OutPutResult.ok();
}
else
{
return OutPutResult.error(resp.DebugInformation);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "稿件釋出/取消釋出後,更新ES:");
return OutPutResult.error("稿件釋出/取消釋出後,更新ES:" + ex.Message);
}
}
2.5查詢
/// <summary>
/// id單條查詢
/// </summary>
/// <param name="InformalEssayId"></param>
/// <returns></returns>
public OutPutResult SerachById(string InformalEssayId)
{
try
{
var search = _client.Get<informal_essay_info>(InformalEssayId, s => s.Index(IndexName));
var data = search.Source;
return OutPutResult.ok().put("data", data);
}
catch (Exception ex)
{
return OutPutResult.error(ex.Message);
}
}
/// <summary>
/// ids多條查詢
/// </summary>
/// <param name="InformalEssayIds"></param>
/// <returns></returns>
public OutPutResult SerachByIds(string InformalEssayIds)
{
try
{
var ids = CommonHelpers.GetStringArray(InformalEssayIds).ToList();
List<FieldValue> terms = new();
ids.ForEach(x =>
{
terms.Add(x);
});
var search = _client.Search<informal_essay_info>(s => s.Index(IndexName).Query(
q => q.Terms(t => t.Field(f => f.id).Terms(new TermsQueryField(terms)))
));
var data = search.Documents.ToList();
return OutPutResult.ok().put("data", data);
}
catch (Exception ex)
{
return OutPutResult.error(ex.Message);
}
}
/// <summary>
/// 列表分頁查詢
///(不要說深度分頁效能問題,使用者說不用管,哈哈,就要全部查出來,慢點沒關係)
/// 其實嘗試過幾種方案,具體使用人員就覺得直接分頁最方便,最後直接max_result_window設定了1千萬
/// (使用者800萬左右的資料,三臺16核32GB的伺服器部署的ES叢集+Redis叢集,深度分頁1秒多,完全能接受)
/// </summary>
/// <param name="category"></param>
/// <param name="keyword"></param>
/// <param name="start"></param>
/// <param name="end"></param>
/// <param name="pageindex"></param>
/// <param name="rows"></param>
/// <param name="orderField"></param>
/// <param name="order"></param>
/// <returns></returns>
public OutPutResult SerachInformalEssays(string category, string keyword, DateTime? start, DateTime? end, int pageindex = 1, int rows = 20, int string orderField = "setdate", SortOrder order = SortOrder.Desc)
{
try
{
var mustQueries = new List<Action<QueryDescriptor<informal_essay_info>>>();
//狀態,0為正常,-1為刪除,1為釋出
mustQueries.Add(t => t.Term(f => f.status, 1));
if (!string.IsNullOrEmpty(category))
{
mustQueries.Add(t => t.MatchPhrase(f => f.Field(fd => fd.category).Query(category)));
}
if (!string.IsNullOrEmpty(keyword))
{
mustQueries.Add(t => t.MultiMatch(m => m.Fields(
new Field("title", 10).And(new Field("content", 5)).And(new Field("author", 5)).And(new Field("keyword", 5))
).Operator(Operator.And).Query(keyword)));
}
if (start.HasValue || end.HasValue)
{
long startDate = CommonHelpers.DateTimeToTimeStamp(start ?? DateTime.MinValue);
long endDate = 0;
if (end != null && end.HasValue)
{
string endtime = string.Format("{0:d}", end) + " 23:59:59";
end = Convert.ToDateTime(endtime);
endDate = CommonHelpers.DateTimeToTimeStamp(end ?? DateTime.MaxValue);
}
else
{
endDate = CommonHelpers.DateTimeToTimeStamp(end ?? DateTime.MaxValue);
}
mustQueries.Add(t => t.Range(r => r.NumberRange(n => n.Field("setdate").Gte(startDate).Lte(endDate))));
}
var search = _client.Search<informal_essay_info>(s => s
.Index(IndexName)
.Query(q => q.Bool(b => b.Must(mustQueries.ToArray())))
.From((pageindex - 1) * rows)
.Size(rows)
.Sort(s => s.Field(orderField, new FieldSort { Order = order }))
);
return OutPutResult.ok().put("total", search.Total).put("data", search.Documents);
}
catch (Exception ex)
{
_logger.LogError(ex, "查詢隨筆異常:");
return OutPutResult.error(ex.Message);
}
}
3.最後
有同事問為什麼不升級到最新版本,生產環境,用最新版本,嘿嘿,懂的都懂,除非一定要用什麼特性,就這樣,有問題大家補充