2012-04-07 16 views
2

私はJQGridアドバンス検索機能multipleSearch: true, multipleGroup: trueを使用しています。JQGrid Advance Search - 「AND」演算子と「OR」演算子を同時に使用できますか?

私はAsp.net MVCとclassic ado.net +ストアドプロシージャも使用しています。

JGRIDのユーザー検索データがある場合は、この検索条件をパラメータ値としてストアドプロシージャに渡します。このように...

Select * 
From tableName 
Where @WhereClauseDynamic 

私は "Where Clause Generator"クラスを作成しました。上部のコードを使用することにより

[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」のような複数のグループ操作をサポートしていますか?このサポートは同時に1人のオペレータだけですか? 「AND」と「OR」オペレータを同時に使用できますか?

すべての提案をいただければ幸いです。

+0

この質問は既に質問されています。http://stackoverflow.com/questions/9590307/how-to-create-sql-where-clause-from-jqgrid-multiplegroup-filter-condition-in-asp – Andrus

+0

@Frank:使用しているデータベースへのどのようなアクセスですか?例えば ​​'ExecuteReader'を持つ' SqlCommand'であれば、 'filters'のデータの代わりに' @p1'、 '@p2'、' @p3'などのパラメータを持つWHERE部分を生成することができます。 'filters'の解析中に配列' SqlParameter [] 'を埋めることができ、' SqlCommand.Parameters.AddRange'を使ってコマンドにパラメータを含めることができます。さらに、 'rule'と' group'の 'field'プロパティの値が選択したテーブル(またはクエリ)の列のリストにあることを確認する必要があります。 – Oleg

+0

@Andrus:[あなたの質問]について忘れました(http://stackoverflow.com/questions/9590307/how-to-create-sql-where-clause-from-jqgrid-multiplegroup-filter-condition-in-asp) 。私は後でそれを見てみましょう。 – Oleg

答えて

6

まず、あなたが使用するコードが危険であると言及する必要があります。 SELECTで使用するWHERE構文を構築し、trustの入力データを使用します。 SQLインジェクションの問題を受け取ることができます。コードをもっと安全に書くべきです。あなたは[%および_のすべてをLIKEを含む演算子で使用する必要があります。

また、パラメータを指定してSELECTを使用することをお勧めします。代わりに

Seminar_Code LIKE 'MED01%' 

のあなたは使用

Seminar_Code LIKE (@p1 + '%') 

し、使用@p1の値および他のパラメータを定義するためにSqlCommand.Parametersを使用することができます。

今私はあなたの主な質問に答えようとします。あなたが使用するFilterクラスの定義は、入力にgroups部分を使用しません。あなたはまた、groups一部を分析するためにWhereClauseGenerator.Generatorメソッドのコードを拡張する必要があり

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

のようなものにFilterクラスを拡張する必要があります。標準的な名前変換に近い名前を使用することをお勧めします。変数に_Filterのような名前を使用し、クラスのプライベートメンバーではない場合は、コードを誤解を招くようにします。さらに、クラスWhereClauseGeneratorは静的(public static class WhereClauseGenerator)であり、方法はGeneratorでもあります。

Filtergroups一部のサポートを追加する最も簡単なコードが

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; 
    } 
} 

ここでは、静的クラスWhereClauseGeneratorのstatic ParseFilterメソッドを使用できます。

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

SQLインジェクションの問題がまだ存在することを忘れないでください。あなたがどのような種類のデータベースアクセスを使用しているか分からない限り、私はそれを修正することはできません。

+0

この回答は、サブグループが詳細検索ダイアログで指定されている場合にのみ機能します。高度な検索ダイアログにsoubgroupやツールバーの検索が含まれていないと、filters.groupsがnullなので、クラッシュします。これを修正するには? – Andrus

+0

@Andrus:どのフィルタリングデータが問題に続くのですか?あなたが例を投稿したCoul?おそらくループ内での使用の前に 'if(filters.rules!== null)'と 'if(filters.groups!== null)'を追加するだけでいいでしょうか? – Oleg

+0

顧客名がツールバーを検索するために入力された場合、またはグループ化が詳細検索ダイアログで使用されていない場合は、フィルタ{{"groupOp": "AND"、 "rules":[{"field": "Customername"、 "op": "cn "、" data ":" ok-soft "}]}'が生成されます。この場合、どこで生成されるべきか。 ifを使用するとwhere句は生成されません。 – Andrus

関連する問題