4

私は現在存在する世界で「最も素晴らしい」解析ライブラリを使用しています。パイパシング。当面の問題は、指定されたSQL文字列からPyMongoディクショナリを生成することです(selectステートメントの場合)。私が使用している文法定義は次のとおりです。

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などの構文は有効な文法定義です。これを使用して、「Select * from collection_1 where(Sal = 1000 or Sal = 5000)AND Car> 2」のような文字列を解析できます。問題は、解析されるwhere部分が次のようになることです。

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

私がそれを何かsqlishに翻訳したいのであれば、これは問題ありません。しかし、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)

前もって感謝します。その他の情報が必要な場合はお知らせください。

4

1 に答える 1

3

はい、解析アクションはこの種のプロジェクトにぴったりです。また、さまざまな優先順位の操作を括弧で囲むことができる式を評価しようとしている場合、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']}}}

まだいくつかの調整が必要ですが、これでさらにうまくいくことを願っています。

于 2012-11-03T05:32:11.413 に答える