簡體   English   中英

JQGrid 高級搜索 - 我們可以同時使用“AND”和“OR”運算符嗎?

[英]JQGrid Advance Search - Could we use "AND" and "OR" operators at the same time?

我正在使用 JQGrid 高級搜索功能multipleSearch: true, multipleGroup: true

我也在使用 Asp.net MVC 和經典的 ado.net + 存儲過程。

每當用戶在 JGRID 搜索數據時,我都會將這個搜索條件作為參數值傳遞給存儲過程。 如...

Select * 
From tableName 
Where @WhereClauseDynamic

所以我創建了“Where Clause Generator”Class。

[ModelBinder(typeof(GridModelBinder))]
public class JqGrid_Setting_VewModel
{
    public bool IsSearch { get; set; }
    public int PageSize { get; set; }
    public int PageIndex { get; set; }
    public string SortColumn { get; set; }
    public string SortOrder { get; set; }
    public string Where { get; set; }
}

public class WhereClauseGenerator
{
    private static readonly string[] FormatMapping = {
        " ({0} = '{1}') ",               // "eq" - equal
        " ({0} <> {1}) ",                // "ne" - not equal
        " ({0} < {1}) ",                 // "lt" - less than
        " ({0} <= {1}) ",                // "le" - less than or equal to
        " ({0} > {1}) ",                 // "gt" - greater than
        " ({0} >= {1}) ",                // "ge" - greater than or equal to
        " ({0} LIKE '{1}%') ",           // "bw" - begins with
        " ({0} NOT LIKE '{1}%') ",       // "bn" - does not begin with
        " ({0} LIKE '%{1}') ",           // "ew" - ends with
        " ({0} NOT LIKE '%{1}') ",       // "en" - does not end with
        " ({0} LIKE '%{1}%') ",          // "cn" - contains
        " ({0} NOT LIKE '%{1}%') "       // "nc" - does not contain
    };

    public string Generator(Filter _Filter)
    {
        var sb = new StringBuilder();            

        foreach (Rule rule in _Filter.rules)
        {
            if (sb.Length != 0)
                sb.Append(_Filter.groupOp);

            sb.AppendFormat(FormatMapping[(int)rule.op], rule.field, rule.data);
        }

        return sb.ToString();
    }
}

public class GridModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        try
        {
            var request = controllerContext.HttpContext.Request;
            var serializer = new JavaScriptSerializer();
            var _WhereClauseGenerator = new WhereClauseGenerator();

            var _IsSearch = bool.Parse(request["_search"] ?? "false");
            var _PageIndex = int.Parse(request["page"] ?? "1");
            var _PageSize = int.Parse(request["rows"] ?? "10");
            var _SortColumn = request["sidx"] ?? "";
            var _SortOrder = request["sord"] ?? "asc";
            var _Where = request["filters"] ?? "";

            return new JqGrid_Setting_VewModel
            {
                IsSearch = _IsSearch,
                PageIndex = _PageIndex,
                PageSize = _PageSize,
                SortColumn = _SortColumn,
                SortOrder = _SortOrder,
                Where = (_IsSearch == false || string.IsNullOrEmpty(_Where)) ? string.Empty : _WhereClauseGenerator.Generator(serializer.Deserialize<Filter>(_Where))
            };

        }
        catch
        {
            return null;
        }
    }
}

[DataContract]
public class Filter
{
    [DataMember]
    public GroupOp groupOp { get; set; }
    [DataMember]
    public List<Rule> rules { get; set; }
}

[DataContract]
public class Rule
{
    [DataMember]
    public string field { get; set; }
    [DataMember]
    public Operations op { get; set; }
    [DataMember]
    public string data { get; set; }
}

public enum GroupOp
{
    AND,
    OR
}

public enum Operations
{
    eq, // "equal"
    ne, // "not equal"
    lt, // "less"
    le, // "less or equal"
    gt, // "greater"
    ge, // "greater or equal"
    bw, // "begins with"
    bn, // "does not begin with"
    //in, // "in"
    //ni, // "not in"
    ew, // "ends with"
    en, // "does not end with"
    cn, // "contains"
    nc  // "does not contain"
}

通過使用上面的代碼,當我這樣搜索時一切都是正確的

{
"groupOp":"AND",
"rules":[{"field":"Seminar_Code","op":"eq","data":"MED01"},
         {"field":"Seminar_Code","op":"eq","data":"CMP05"}],"groups":[]      
}

 sb.ToString() // Output vlaue
 " (Seminar_Code = 'MED01') AND (Seminar_Code = 'CMP05') "

所以,這是完全正確的。

但是當涉及到更復雜的搜索查詢時......

{
"groupOp":"AND",
"rules":[{"field":"Seminar_Code","op":"eq","data":"MED01"},
     {"field":"Seminar_Code","op":"eq","data":"CMP05"}],

     "groups":[{
            "groupOp":"OR",
            "rules": [{"field":"Seminar_Code","op":"eq","data":"CMP01"}],"groups":[]}]              
}

sb.ToString() // Actual Output value is like that below
" (Seminar_Code = 'MED01') AND (Seminar_Code = 'CMP05') "

但我所期望的是像下面這樣的..

" ((Seminar_Code = 'MED01') AND (Seminar_Code = 'CMP05')) OR ( Seminar_Code = 'CMP01' ) "

那么我怎樣才能正確地做到這一點呢?

JQGrid是否支持“AND”+“OR”的多組運算? 這是同時只支持一個運營商嗎? 我們可以同時使用“AND”和“OR”運算符嗎?

每一個建議將不勝感激。

首先我應該提到我發現你使用的代碼很危險。 您構造要在 SELECT 中使用的 WHERE 構造,並使用信任輸入數據。 您可以收到 SQL 注入問題。 您應該更安全地編寫代碼。 您需要轉義包含LIKE的運算符中使用的所有[%_

此外,我建議您使用帶參數的 SELECT。 代替

Seminar_Code LIKE 'MED01%'

您可以使用

Seminar_Code LIKE (@p1 + '%')

並使用SqlCommand.Parameters來定義@p1的值和您使用的其他參數。

現在我試着回答你的主要問題。 您使用的Filter class 的定義不在輸入中使用groups部分。 您應該將Filter class 擴展為類似

public class Filter {
    public GroupOp groupOp { get; set; }
    public List<Rule> rules { get; set; }
    public List<Filter> groups { get; set; }
}

您還應該擴展WhereClauseGenerator.Generator方法的代碼以分析groups部分。 我建議您另外使用更接近標准名稱轉換的名稱。 如果您將_Filter之類的名稱用於變量而不是 class 的私有成員,則會使代碼難以理解。 此外,class WhereClauseGenerator可以是 static( public static class WhereClauseGenerator )和方法Generator

添加對Filter groups部分的支持的最簡單代碼可以是

public static string Generator (Filter filters) {
    var sb = new StringBuilder ();

    if (filters.groups != null && filters.groups.Count > 0)
        sb.Append (" (");

    bool firstRule = true;
    if (filters.rules != null) {
        foreach (var rule in filters.rules) {
            if (!firstRule)
                sb.Append (filters.groupOp);
            else
                firstRule = false;

            sb.AppendFormat (FormatMapping[(int)rule.op], rule.field, rule.data);
        }
    }

    if (filters.groups != null && filters.groups.Count > 0) {
        sb.Append (") ");

        foreach (var filter in filters.groups) {
            if (sb.Length != 0)
                sb.Append (filter.groupOp);
            sb.Append (" (");
            sb.Append (Generator (filter));
            sb.Append (") ");
        }
    }

    return sb.ToString ();
}

更新:我必須修改上面的代碼才能為更復雜的filters輸入產生正確的結果:

public class Filter {
    public GroupOp groupOp { get; set; }
    public List<Rule> rules { get; set; }
    public List<Filter> groups { get; set; }
}

public class Rule {
    public string field { get; set; }
    public Operations op { get; set; }
    public string data { get; set; }
}

public enum GroupOp {
    AND,
    OR
}

public enum Operations {
    eq, // "equal"
    ne, // "not equal"
    lt, // "less"
    le, // "less or equal"
    gt, // "greater"
    ge, // "greater or equal"
    bw, // "begins with"
    bn, // "does not begin with"
    //in, // "in"
    //ni, // "not in"
    ew, // "ends with"
    en, // "does not end with"
    cn, // "contains"
    nc  // "does not contain"
}

public static class WhereClauseGenerator {
    private static readonly string[] FormatMapping = {
        "({0} = '{1}')",               // "eq" - equal
        "({0} <> {1})",                // "ne" - not equal
        "({0} < {1})",                 // "lt" - less than
        "({0} <= {1})",                // "le" - less than or equal to
        "({0} > {1})",                 // "gt" - greater than
        "({0} >= {1})",                // "ge" - greater than or equal to
        "({0} LIKE '{1}%')",           // "bw" - begins with
        "({0} NOT LIKE '{1}%')",       // "bn" - does not begin with
        "({0} LIKE '%{1}')",           // "ew" - ends with
        "({0} NOT LIKE '%{1}')",       // "en" - does not end with
        "({0} LIKE '%{1}%')",          // "cn" - contains
        "({0} NOT LIKE '%{1}%')"       // "nc" - does not contain
    };

    private static StringBuilder ParseRule(ICollection<Rule> rules, GroupOp groupOp) {
        if (rules == null || rules.Count == 0)
            return null;

        var sb = new StringBuilder ();
        bool firstRule = true;
        foreach (var rule in rules) {
            if (!firstRule)
                // skip groupOp before the first rule
                sb.Append (groupOp);
            else
                firstRule = false;

            sb.AppendFormat (FormatMapping[(int)rule.op], rule.field, rule.data);
        }
        return sb.Length > 0 ? sb : null;
    }

    private static void AppendWithBrackets (StringBuilder dest, StringBuilder src) {
        if (src == null || src.Length == 0)
            return;

        if (src.Length > 2 && src[0] != '(' && src[src.Length - 1] != ')') {
            dest.Append ('(');
            dest.Append (src);
            dest.Append (')');
        } else {
            // verify that no other '(' and ')' exist in the b. so that
            // we have no case like src = "(x < 0) OR (y > 0)"
            for (int i = 1; i < src.Length - 1; i++) {
                if (src[i] == '(' || src[i] == ')') {
                    dest.Append ('(');
                    dest.Append (src);
                    dest.Append (')');
                    return;
                }
            }
            dest.Append (src);
        }
    }

    private static StringBuilder ParseFilter(ICollection<Filter> groups, GroupOp groupOp) {
        if (groups == null || groups.Count == 0)
            return null;

        var sb = new StringBuilder ();
        bool firstGroup = true;
        foreach (var group in groups) {
            var sbGroup = ParseFilter(group);
            if (sbGroup == null || sbGroup.Length == 0)
                continue;

            if (!firstGroup)
                // skip groupOp before the first group
                sb.Append (groupOp);
            else
                firstGroup = false;

            sb.EnsureCapacity (sb.Length + sbGroup.Length + 2);
            AppendWithBrackets (sb, sbGroup);
        }
        return sb;
    }

    public static StringBuilder ParseFilter(Filter filters) {
        var parsedRules = ParseRule (filters.rules, filters.groupOp);
        var parsedGroups = ParseFilter (filters.groups, filters.groupOp);

        if (parsedRules != null && parsedRules.Length > 0) {
            if (parsedGroups != null && parsedGroups.Length > 0) {
                var groupOpStr = filters.groupOp.ToString();
                var sb = new StringBuilder (parsedRules.Length + parsedGroups.Length + groupOpStr.Length + 4);
                AppendWithBrackets (sb, parsedRules);
                sb.Append (groupOpStr);
                AppendWithBrackets (sb, parsedGroups);
                return sb;
            }
            return parsedRules;
        }
        return parsedGroups;
    }
}

現在您可以使用 static ParseFilter方法 static class WhereClauseGenerator類的

var filters = request["filters"];
string whereString = request["_search"] && !String.IsNullOrEmpty(filters)
    ? WhereClauseGenerator.ParseFilter(serializer.Deserialize<Filter>(filters))
    : String.Empty;

請不要忘記 SQL 注入的問題仍然存在。 在我不知道您使用哪種數據庫訪問之前,我無法修復它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM