最近写了一个Elastic Search查询DSL的生成器(依赖Json.net)。代码如下
static class QueryBuilder { public static object Build(JToken template, object paraData) { return Build(template, JObject.FromObject(paraData)); } public static object Build(JToken template, JObject para) { var output = FillPara(template, para); RemoveNullPara(output); if (output.Type == JTokenType.Null) return null; return output; } static JToken FillPara(JToken template, JObject para) { var output = template.DeepClone(); foreach (var valueNode in output.AllChildren().OfType<JValue>()) { var key = valueNode.Value<string>(); if (key?.StartsWith('@') == true) { var paraValue = para.Property(key[1..], StringComparison.OrdinalIgnoreCase) ?.Value; var property = valueNode.Parent as JProperty; property.Value = paraValue?.DeepClone(); } } return output; } static void RemoveNullPara(JToken token) { foreach (var child in token.AllChildren().ToArray()) { if (child.IsNull()) remove(child); } } static void remove(JToken token) { var parent = token.Parent; if (parent == null) { return; } if (parent is JProperty p) { token = parent; } token.Remove(); } static bool IsNull(this JToken token) { if (token.Type == JTokenType.Null) return true; if (token is JValue) return false; return token.Children().FirstOrDefault() == null; } public static IEnumerable<JToken> AllChildren(this JToken token) { var children = token switch { JArray array => array, JObject obj => obj.Properties().Select(i => i.Value), _ => Enumerable.Empty<JToken>() }; foreach (var child in children.SelectMany(AllChildren)) { yield return child; } yield return token; } }
使用方式类似于dapper,要配套一个Json模板来使用,基本就是elastic search的查询语法,知识将需要注入的参数改为用@开头的占位字符串。
{
"query": {
"bool": {
"must": [
{ "term": { "Status": "@status" } },
{ "term": { "Name": "@name" } }
]
}
}
}
填充参数,会自动生成对应的json文件(ES的查询body)。
QueryBuilder.Build(template, new {status = "ok", name="jim"});
执行效果如下:
{
"query": {
"bool": {
"must": [
{ "term": { "Status": "ok" } },
{ "term": { "Name": "jim" } }
]
}
}
}
支持各种json类型,可以直接配置es查询模板。并且,当属性没有传入时,会自动将其从查询条件中去掉,非常方便,例如:
QueryBuilder.Build(template, new {status = "ok"});
生成结果如下:
{
"query": {
"bool": {
"must": [
{ "term": { "Status": "ok" } }
]
}
}
}
这个方式比较适合于查询条件随着输入参数动态增删,并且查询条件经常需要修改的场景: 当查询条件变化时,只需要修改模板即可,不需要修改代码,并且可读性非常好。也能根据参数自适应查询条件的增删。