2012-11-02 73 views
3

我現在正在使用世界上現有的「awesomest」解析庫。 Pyparsing。現在的問題是從給定的SQL字符串中生成一個PyMongo字典(對於select語句)。我使用的語法DEF是以下幾點:Sql使用Pyparsing生成Pymongo

sql_stmt = (select_key_word + ('*' | column_list).setResultsName 
       ("columns") + form_key_word + table_name_list.setResultsName 
       ("collections") + 
       Optional(where_condition, "").setResultsName("where")) 

這裏select_key_word,column_list中等結構是有效的語法DEFS。並使用此我可以解析一個字符串,如「選擇*從collection_1哪裏(薩爾= 1000或薩爾= 5000)和汽車> 2」 我的問題是,其中部分被解析的是這樣的:

[[u'where', [u'(', [u'Sal', '=', u'1000'], 'or', [u'Sal', '=', u'5000'], u')'], 'and', [u'Car', '>', u'2']]] 

如果我想將它翻譯成某些東西,這很好。但在pymongo中的相同的有效表示會是這樣的:

{u'$or': [{u'$and': [{u'Sal': u'1000'}, {u'Sal': u'5000'}]}, {u'Car': {u'$gte': u'2'}}]} 

這就是我卡住的地方。有人可以給我一個方向嗎?在我看來,這setParseAction將是很長的路要走,但就是無法找出答案

的where_contidion的代碼是:提前

where_expr = Forward() 
and_keyword = get_conjunction_as_grammar("and") 
or_keyword = get_conjunction_as_grammar("or") 
in_operation = get_operation_as_grammar("in") 

column_value = get_real_number_as_grammar() | get_int_as_grammar() | \ 
       quotedString 
binary_operator = get_bin_op_as_grammar() 
col_name = get_column_name_as_grammar() 

where_condn = Group(
(col_name + binary_operator + column_value) | 
(col_name + in_operation + "(" + delimitedList(column_value) + ")") | 
("(" + where_expr + ")") 
) 
where_expr << where_condn + ZeroOrMore((and_keyword | or_keyword) 
             + where_expr) 

where_condition = Group(CaselessLiteral("where") + where_expr) 

感謝。如果您需要其他信息,請告訴我。

回答

2

是的,解析操作只是這種類型的項目。此外,如果您正試圖評估,可以有不同的優先級業務的括號嵌套的表達式,然後operatorPrecedence往往是一個方便快捷:

from pyparsing import * 

and_keyword = CaselessKeyword("and") 
or_keyword = CaselessKeyword("or") 
in_operation = CaselessKeyword("in") 

value = quotedString | Word(alphanums) 
comparisonOp = oneOf("= != > < >= <=") 
LPAR,RPAR = map(Suppress,"()") 
valueList = LPAR + delimitedList(value) + RPAR 
comparisonExpr = value + comparisonOp + value | value + in_operation + Group(valueList) 
def makePymongoComparison(tokens): 
    v1,op,v2 = tokens 
    if op != 'in': 
     if op != '=': 
      op = { 
       "!=" : "$ne", 
       ">" : "$gt", 
       "<" : "$lt", 
       ">=" : "$gte", 
       "<=" : "$lte", 
       }[op] 
      v2 = "{'%s': '%s'}" % (op, v2) 
     return "{'%s': '%s'}" % (v1, v2) 
    else: 
     return "{'%s': {'$in': [%s]}}" % (v1, ','.join("'%s'"%v for v in v2)) 
comparisonExpr.setParseAction(makePymongoComparison) 

def handleBinaryOp(op): 
    def pa(tokens): 
     return "{'$%s': %s}" % (op, ', '.join(tokens.asList()[0][::2])) 
    return pa 
handleAnd = handleBinaryOp("and") 
handleOr = handleBinaryOp("or") 
whereOperand = comparisonExpr 
where_expr = operatorPrecedence(whereOperand, 
    [ 
    (and_keyword, 2, opAssoc.LEFT, handleAnd), 
    (or_keyword, 2, opAssoc.LEFT, handleOr), 
    ]) 

where_condition = Group(CaselessLiteral("where") + where_expr) 

print where_expr.parseString("(Sal = 1000 or Sal=5000) AND Car>2")[0] 
print where_expr.parseString("(Sal = 1000 or Sal=5000) AND Car in (1,2,3)")[0] 

打印:

{'$and': {'$or': {'Sal': '1000'}, {'Sal': '5000'}}, {'Car': '{'$gt': '2'}'}} 
{'$and': {'$or': {'Sal': '1000'}, {'Sal': '5000'}}, {'Car': {'$in': ['1','2','3']}}} 

仍然需要一些調整,但我希望這會讓你更進一步。

+1

非常感謝!也感謝你寫這麼好的圖書館:)你真棒。 – SRC