쿼리에 합류했습니다. bauble (a program on github)을 채택했으며이 중 일부는 SQL 데이터베이스에 대한 쿼리를 지정하기위한 것입니다. 쿼리 언어는 실제로 세 가지 다른 언어 중 하나 (SQL 쿼리 에서처럼 필터링)를 다시 작성합니다.필터 sqlalchemy가 구문 분석 된 입력에 구성된
원저자가 pyparsing을 선택했기 때문에 필자는 pyparsing을 모른다는 것 외에는 그 선택을 검토 할 이유가 없습니다. 그리고 나는 항상 lex와 yacc를 재미있게 보았습니다 ... 그러나 나는 계속 pyparsing을 유지할 것입니다. 나는 그것을 배우고있다.
나는 주어진 쿼리를 인식하는 파서를 (다시) 써 왔고, 대부분의 문법 카테고리는 클래스로 번역된다. 나는 파싱 부분이 꽤 괜찮은데, 내가 붙어있는 부분은 필자가 pyparsing으로 생성 한 객체가 SQLAlchemy를 사용하여 데이터베이스를 쿼리 할 필요가 있다는 것입니다. 특히 조인 된 테이블의 특성을 기반으로 필터링 할 때 그렇습니다.
관련 대한 파싱 형식의 문법의 일부 :
query_expression = Forward()
identifier = Group(delimitedList(Word(alphas, alphanums+'_'),
'.')).setParseAction(IdentifierToken)
ident_expression = (
Group(identifier + binop + value).setParseAction(IdentExpressionToken)
| (
Literal('(') + query_expression + Literal(')')
).setParseAction(ParenthesisedQuery))
query_expression << infixNotation(
ident_expression,
[ (NOT_, 1, opAssoc.RIGHT, SearchNotAction),
(AND_, 2, opAssoc.LEFT, SearchAndAction),
(OR_, 2, opAssoc.LEFT, SearchOrAction) ])
하고 해당 클래스 (마지막 두 사람의 evaluate
방법은 아직 작성하는 방법을 모르는 것입니다) :
class BinaryLogical(object):
## abstract base class. `name` is defined in derived classes
def __init__(self, t):
self.op = t[0][1]
self.operands = t[0][0::2] # every second object is an operand
def __repr__(self):
return "(%s %s %s)" % (self.operands[0], self.name, self.operands[1])
class SearchAndAction(BinaryLogical):
name = 'AND'
def evaluate(self, domain, session):
return self.operands[0].evaluate(domain, session).intersect_all(
map(lambda i: i.evaluate(domain, session), self.operands[1:]))
class SearchOrAction(BinaryLogical):
name = 'OR'
def evaluate(self, domain, session):
return self.operands[0].evaluate(domain, session).union_all(
map(lambda i: i.evaluate(domain, session), self.operands[1:]))
class SearchNotAction(object):
name = 'NOT'
def __init__(self, t):
self.op, self.operand = t[0]
def evaluate(self, domain, session):
return session.query(domain).except_(self.operand.evaluate(domain, session))
def __repr__(self):
return "%s %s" % (self.name, str(self.operand))
class ParenthesisedQuery(object):
def __init__(self, t):
self.query = t[1]
def __repr__(self):
return "(%s)" % self.query.__repr__()
def evaluate(self, domain, session):
return self.query.evaluate(domain, session)
class IdentifierToken(object):
def __init__(self, t):
self.value = t[0]
def __repr__(self):
return '.'.join(self.value)
def evaluate(self, domain, session):
q = session.query(domain)
if len(self.value) > 1:
q = q.join(self.value[:-1], aliased=True)
return q.subquery().c[self.value[-1]]
class IdentExpressionToken(object):
def __init__(self, t):
self.op = t[0][1]
self.operation = {'>': lambda x,y: x>y,
'<': lambda x,y: x<y,
'>=': lambda x,y: x>=y,
'<=': lambda x,y: x<=y,
'=': lambda x,y: x==y,
'!=': lambda x,y: x!=y,
}[self.op]
self.operands = t[0][0::2] # every second object is an operand
def __repr__(self):
return "(%s %s %s)" % (self.operands[0], self.op, self.operands[1])
def evaluate(self, domain, session):
return session.query(domain).filter(self.operation(self.operands[0].evaluate(domain, session),
self.operands[1].express()))
위 스 니펫의 최신 코드는 모두 here입니다.
몇 가지 가능한 쿼리 : 이전 개발자가 해당 클래스를 만드는 데 많은 어려움에 갔다처럼
results = mapper_search.search("plant where accession.species.id=44")
results = mapper_search.search("species where genus.genus='Ixora'")
results = mapper_search.search("species where genus.genus=Maxillaria and not genus.family=Orchidaceae")
SQLAlchemy에서 제공 한 ORM을 무시하고 'from_statement'를 사용하여 쿼리를 실행하는 힌트를 받았습니다. ORM을 수동으로 재구성해야하기 때문에 이것은 실행 가능하지 않습니다. – mariotomo