当前位置:Gxlcms > asp.net > asp.net 网页动态查询条件的实现

asp.net 网页动态查询条件的实现

时间:2021-07-01 10:21:17 帮助过:31人阅读

场景

最近有一个需求,会在 mongodb 中插入各种类型的数据,算是记录业务日志的数据库吧。
因为业务对象类型都不同,所以插入的数据格式也完全不同。
除此之外,还需要提供一个查询界面,可以搜索数据。
插入数据没任何问题,但是查询就…

查询设计方案

首先想到的是让用户直接输入 mongodb 查询语法,类似 json 格式。但是使用者虽然也是开发,可都不熟悉这个语法,所以放弃了。
第二个想法是让用户输入 SQL 语句,然后转换… 结果以失败而告终。
最后,看到了 iTunes 智能播放列表的交互设计:


这里,你可以插入一个条件,也可以插入一组条件(相当于插入了一个括号,括号内是许多条件)。

图中的表达式可以认为是: Score > 3 && Type == "Music" && Author == "" && ( Author == "" && Author == "" && Author == "")

也就是说,这样的交互完全可以实现各种嵌套逻辑。

数据结构
为了设计出这样的结构,肯定要先好好想一下数据结构。

分析后感觉,这里其实就两种类型,一个可以认为是 QueryGroup,一个可以认为是 QueryItem。

代码如下:
代码如下:
  1. <br>public class QueryGroup <br>{ <br>public GroupType GroupType { get; set; } <br>public List<QueryItem> Items { get; set; } <br>public List<QueryGroup> Groups { get; set; } <br>} <br><br>public class QueryItem <br>{ <br>public string Name { get; set; } <br>public QuerySymbol OperatorType { get; set; } <br>public string Value { get; set; } <br>public DataType ValueType { get; set; } <br>} <br> <br>QueryGroup 包含了一组查询条件,也包含了一组子 QueryGroup,另外还有一个重要的属性 GroupType ,代表这组数据的逻辑关系是 And 还是 Or。也就是上述界面中的“任何”和“任意”选项。 <br><br>QueryItem 内部属性分别是字段名、逻辑操作类型(等于、不等于、大于…)、和属性类型(整数、文本…)。 <br><br>设计完数据结构后会有几个难点: <br><br>1.前端交互怎么设计? <br>2.如何传给后端? <br>3.后端得到数据后如何转换成查询表达式? <br>那下面就一个个来攻克吧! <br><br>前端设计交互 <br><br><img src="https://img.gxlcms.com//Uploads-s/new/2019-09-19-201919/ui.png"><br><br>这里用的是 bootstrap ,界面非常好看! <br><br>先来看看前端设计方案吧,上面是动态条件,下面是一些固定的条件。 <br><br>这里的结构和上面的数据结构一致,把 html 分两类,QueryGroup 和 QueryItem。 <br><br>分别放在两个隐藏的 div 中,当做模版使用。 <br><br>代码如下: <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br><div style="display: none;"> <br><div class="query-group-template"> <br><div class="query-group well"> <br><div class="query-title"> <br><span class="help-inline">匹配以下</span> <br><select class="input-small group-type"> <br><option value="1">全部</option> <br><option value="2">任何</option> <br></select> <br><span class="help-inline">规则:</span> <br><button type="button" class="btn btn-mini btn-success add-query-item" title="增加一个条件"> <br><i class="icon-plus icon-white"></i> <br></button> <br><button type="button" class="btn btn-mini btn-info add-query-group" title="增加一组条件"> <br><i class="icon-th-list icon-white"></i> <br></button> <br><button type="button" class="btn btn-mini btn-danger delete-query-group" title="删除这组条件"> <br><i class="icon-minus icon-white"></i> <br></button> <br></div> <br></div> <br></div> <br><div class="query-item-template"> <br><div class="query-item"> <br><input type="text" value="" placeholder="字段名" title="字段名" class="property-name" /> <br><select class="input-mini operate-type" title="条件"> <br><option value="1">==</option> <br><option value="2">!=</option> <br><option value="3">></option> <br><option value="4">>=</option> <br><option value="5"><</option> <br><option value="6"><=</option> <br><option value="7">LK</option> <br></select> <br><input type="text" class="query-value" value="" placeholder="值" title="值" /> <br><select class="input-medium value-type"> <br><option value="3">String</option> <br><option value="1">Int</option> <br><option value="2">Double</option> <br><option value="4">DateTime</option> <br></select> <br><button type="button" class="btn btn-mini btn-danger delete-query-item" title="删除条件"> <br><i class="icon-minus icon-white"></i> <br></button> <br></div> <br></div> <br></div> <br> <br>这里其实不难,最关键的地方其实是各个按钮的事件了。 <br>仔细看一下,一共有4个按钮: <br>上面三个分别是:增加一行条件、增加一组条件、删除本组条件。 <br>单个条件右边一个是:删除此条件。 <br>这里逻辑其实非常简单: <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>$('#queryContainer').append($('.query-group-template>.query-group').clone()) <br>$('#queryContainer>.query-group').first().find('.delete-query-group').remove(); <br>$('button.add-query-item').live('click', function () { <br>$(this).parent().parent().append($('.query-item-template>.query-item').clone()); <br>return false; <br>}); <br>$('button.add-query-group').live('click', function () { <br>$(this).parent().parent().append($('.query-group-template>.query-group').clone()); <br>return false; <br>}); <br>$('button.delete-query-group').live('click', function () { <br>if (!$(this).parent().parent().parent().hasClass('query-group')) { return false; } <br>$(this).parent().parent().remove(); <br>return false; <br>}); <br>$('button.delete-query-item').live('click', function () { <br>$(this).parent().remove(); <br>return false; <br>}); <br> <br>另外,看代码前两行,第一次加载的时候别忘了先加一组条件,并且把默认组的“删除本组条件”这个按钮去掉吧。 <br>前端数据处理 <br>界面交互真的很简单,但是怎么把这个数据传给后端呢? <br>把表单一个个字段取出来传过去?那后端要哭了… 完全是乱七八糟的一堆数据。 <br>那… 既然查询条件的结构是非常清晰的,为什么不能先变成 javascript 中的对象呢? <br>然后,把这个对象序列化… <br>再然后,把 json 传给后端… <br>最后,后端定义同样结构的类型,然后反序列化… <br>也就是说,在这个交互的过程中,只需要把表单数据实例化成 javascript 中的对象即可! <br>那我先来定义两个对象(注意字段名一定要和后端一样): <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>function QueryGroup() { <br>this.GroupType = 0; <br>this.Items = []; <br>this.Groups = []; <br>} <br>function QueryItem() { <br>this.Name = ''; <br>this.OperatorType = 0; <br>this.Value = ''; <br>this.ValueType = 0; <br>} <br> <br>实例化成对象的方法也非常简单,需要用到递归,基本逻辑是: <br>对最外层 QueryGroup 内部的对象循环一次,如果是 QueryItem 就指着取值,如果还是 QueryGroup 就递归调用此方法。 <br>代码如下: <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>function GetQueryGroup(group) { <br>group = $(group); <br>var queryGroup = new QueryGroup(); <br>queryGroup.GroupType = parseInt(group.find('.group-type').val()); <br>var queryItems = group.children('.query-item'); <br>for (var k = 0; k < queryItems.length; k++) { <br>var queryItem = new QueryItem(); <br>queryItem.Name = $(queryItems[k]).find('.property-name').val(); <br>queryItem.OperatorType = parseInt($(queryItems[k]).find('.operate-type').val()); <br>queryItem.Value = $(queryItems[k]).find('.query-value').val(); <br>queryItem.ValueType = parseInt($(queryItems[k]).find('.value-type').val()); <br>queryGroup.Items.push(queryItem); <br>} <br>var childGroups = group.children('.query-group'); <br>for (var k = 0; k < childGroups.length; k++) { <br>queryGroup.Groups.push(GetQueryGroup(childGroups[k])); <br>} <br>return queryGroup; <br>} <br> <br>最后,表单是表单提交,最终会生成一个对象,把这个对象序列化成 json 然后编码一下: <br>encodeURIComponent(JSON.stringify(item)) <br>后端数据处理 <br>后端数据处理主要分两个部分:反序列化、转换成查询条件。 <br>数据结构在上面已经定义过了,只要字段名和 json 中的一样,就可以直接反序列化。 <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>var json = Uri.UnescapeDataString(Request["query"]); <br>var item = JsonConvert.DeserializeObject<QueryGroup>(json); <br> <br>两行代码,它就变成 .net 中的对象了! <br>最后,生成查询条件其实也非常简单,也是一个方法,递归调用即可,基本逻辑和前段把表单数据实例化的过程很像。 <br>我在 QueryGroup 中扩展了一个方法,其中 ICriteria 和 IMongoQuery 结构类似,用过 mongodb 的同学当它是 IMongoQuery 即可,它只是包了一层,最终也是生成 IMongoQuery。 <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>public class QueryGroup <br>{ <br>public GroupType GroupType { get; set; } <br>public List<QueryItem> Items { get; set; } <br>public List<QueryGroup> Groups { get; set; } <br>public ICriteria ToICriteria() <br>{ <br>ICriteria result = null; <br>foreach (var criteria in GetICriteriaList()) <br>{ <br>if (result == null) <br>{ <br>result = criteria; <br>continue; <br>} <br>if (GroupType == Model.GroupType.AndAlse) <br>{ <br>result = result.Add(criteria); <br>continue; <br>} <br>if (GroupType == Model.GroupType.OrElse) <br>{ <br>result = result.Or(criteria); <br>continue; <br>} <br>} <br>return result; <br>} <br>private List<ICriteria> GetICriteriaList() <br>{ <br>var list = new List<ICriteria>(); <br>foreach (var item in Items) <br>{ <br>list.Add(new Criteria(item.Name, item.OperatorType, new QueryValue(item.ValueType, item.Value, FieldHierarchyLevel.Child))); <br>} <br>foreach (var group in Groups) <br>{ <br>list.Add(group.ToICriteria()); <br>} <br>return list; <br>} <br>} <br> <br>得到查询条件对象后,直接调用相关查询方法即可。 <br><br>后记 <br>本场景中用的是 mongodb ,所以最终转换出来的是 mongodb 查询对象。其实,如果是转换 SQL 也是非常方便的。 <br><br>另外,稍微复杂一点,转换成 .net 中的表达式树也是木有问题的! <br><br>最后附上 gif 的 Demo <br><br><img src="https://img.gxlcms.com//Uploads-s/new/2019-09-19-201919/2012102613515939.gif"><br><br>作者:Dozer</li><li> </li><li> </li></ol></pre></li></ol></pre></li></ol></pre></li></ol></pre></li></ol></pre></li></ol></pre>

人气教程排行