2012-04-11 3 views
4

나는 손실에 처해있다. 나는 이것을 지금 며칠 동안 일하도록 노력하고있다. 그러나 나는 이것으로 어디에도 가지 않고있다. 그래서 나는 당신에게 여기의 사람을 상담 할 것이고, 누군가 나를 도울 수 있는지 알 것이다!하나의 쿼리 형식을 다른 형식으로 pyparsing

하나의 쿼리 형식을 다른 형식으로 구문 분석하기 위해 pyparsing을 사용하고 있습니다. 이것은 단순한 변화가 아니라 실제로 일부 뇌 :

현재 쿼리는 다음과 같다 소요 :

("breast neoplasms"[MeSH Terms] OR breast cancer[Acknowledgments] 
OR breast cancer[Figure/Table Caption] OR breast cancer[Section Title] 
OR breast cancer[Body - All Words] OR breast cancer[Title] 
OR breast cancer[Abstract] OR breast cancer[Journal]) 
AND (prevention[Acknowledgments] OR prevention[Figure/Table Caption] 
OR prevention[Section Title] OR prevention[Body - All Words] 
OR prevention[Title] OR prevention[Abstract]) 

그리고 나는 다음과 같은 구조를 얻을 수 있었다 대한 파싱을 사용하여 :

[[[['"', 'breast', 'neoplasms', '"'], ['MeSH', 'Terms']], 'or', 
[['breast', 'cancer'], ['Acknowledgments']], 'or', [['breast', 'cancer'], 
['Figure/Table', 'Caption']], 'or', [['breast', 'cancer'], ['Section', 
'Title']], 'or', [['breast', 'cancer'], ['Body', '-', 'All', 'Words']], 
'or', [['breast', 'cancer'], ['Title']], 'or', [['breast', 'cancer'], 
['Abstract']], 'or', [['breast', 'cancer'], ['Journal']]], 'and', 
[[['prevention'], ['Acknowledgments']], 'or', [['prevention'], 
['Figure/Table', 'Caption']], 'or', [['prevention'], ['Section', 'Title']], 
'or', [['prevention'], ['Body', '-', 'All', 'Words']], 'or', 
[['prevention'], ['Title']], 'or', [['prevention'], ['Abstract']]]] 

하지만 지금, 나는 실종됐다. 위의 출력을 lucene 검색 쿼리로 형식화해야합니다. 여기에 필요한 변환에 대한 간단한 예입니다

"breast neoplasms"[MeSH Terms] --> [['"', 'breast', 'neoplasms', '"'], 
['MeSH', 'Terms']] --> mesh terms: "breast neoplasms" 

그러나 나는 바로 거기에 붙어있다. 또한 특수 단어 AND 및 OR을 사용할 수 있어야합니다.

때문에 최종 수 있습니다 쿼리 용어를 메쉬 : "유방 종양"및 예방

도와 저에게이 문제를 해결하는 방법에 대한 몇 가지 힌트를 줄 수

? 모든 종류의 도움을 주시면 감사하겠습니다.

필자는 pyparsing을 사용하고 있기 때문에 파이썬을 사용하고 있습니다. 아래 코드를 붙여 넣어서 놀 수 있고 0부터 시작하지 않아도됩니다!

도움을 주셔서 감사합니다.

def PubMedQueryParser(): 
    word = Word(alphanums +".-/&§") 
    complex_structure = Group(Literal('"') + OneOrMore(word) + Literal('"')) + Suppress('[') + Group(OneOrMore(word)) + Suppress(']') 
    medium_structure = Group(OneOrMore(word)) + Suppress('[') + Group(OneOrMore(word)) + Suppress(']') 
    easy_structure = Group(OneOrMore(word)) 
    parse_structure = complex_structure | medium_structure | easy_structure 
    operators = oneOf("and or", caseless=True) 
    expr = Forward() 
    atom = Group(parse_structure) + ZeroOrMore(operators + expr) 
    atom2 = Group(Suppress('(') + atom + Suppress(')')) + ZeroOrMore(operators + expr) | atom 
    expr << atom2 
    return expr 

답변

5

글쎄, 당신은 괜찮은 출발을 할 수있었습니다. 그러나 여기에서 파서 - 미세 조정의 세부 사항에 빠지기가 쉽습니다. 그리고 며칠 동안이 모드에있을 수 있습니다. 원래 쿼리 구문으로 시작하여 문제를 해결해 보겠습니다.

이와 같은 프로젝트를 시작할 때 구문 분석 할 구문의 BNF를 작성하십시오. - 우리는 몇 가지 가능한 모호함 사이에 약간의 문제가

word :: Word('a'-'z', 'A'-'Z', '0'-'9', '.-/&§') 
field_qualifier :: '[' word+ ']' 
search_term :: (word+ | quoted_string) field_qualifier? 
and_op :: 'and' 
or_op :: 'or' 
and_term :: or_term (and_op or_term)* 
or_term :: atom (or_op atom)* 
atom :: search_term | ('(' and_term ')') 

아주 가까이 : 그것은 매우 엄격한 될 필요가 없습니다, 사실, 여기에 내가 샘플에서 볼 수있는 내용에 따라 하나의 시작이다 wordand_opor_op 표현식이 있습니다. '및'과 '또는'는 단어의 정의와 일치하기 때문입니다. 우리는 구현 시간에 이것을 단단히하여 "암 또는 암종 또는 림프종 또는 흑색 종"이 4 개의 다른 검색 용어로 '또는'로 분리되어 있는지를 확인해야합니다. 하나의 큰 용어가 아닙니다. 파서가 할 것이다). 우리는 또한 연산자의 우선 순위를 인식하는 이점을 얻습니다. 아마도 꼭 필요한 것은 아니지만 지금 당장 보겠습니다.대한 파싱로 변환

는 간단하다 :

word = ~(and_op | or_op) + Word(alphanums + '.-/&') 

사람 :

LBRACK,RBRACK,LPAREN,RPAREN = map(Suppress,"[]()") 
and_op = CaselessKeyword('and') 
or_op = CaselessKeyword('or') 
word = Word(alphanums + '.-/&') 

field_qualifier = LBRACK + OneOrMore(word) + RBRACK 
search_term = ((Group(OneOrMore(word)) | quoted_string)('search_text') + 
       Optional(field_qualifier)('field')) 
expr = Forward() 
atom = search_term | (LPAREN + expr + RPAREN) 
or_term = atom + ZeroOrMore(or_op + atom) 
and_term = or_term + ZeroOrMore(and_op + or_term) 
expr << and_term 

는 '나'와 '와'의 모호성을 해결하기 위해, 우리는 단어의 시작 부분에 부정적 예측을 넣어 결과에 어떤 구조를주고, Group 클래스로 감싼다 :

field_qualifier = Group(LBRACK + OneOrMore(word) + RBRACK) 
search_term = Group(Group(OneOrMore(word) | quotedString)('search_text') + 
          Optional(field_qualifier)('field')) 
expr = Forward() 
atom = search_term | (LPAREN + expr + RPAREN) 
or_term = Group(atom + ZeroOrMore(or_op + atom)) 
and_term = Group(or_term + ZeroOrMore(and_op + or_term)) 
expr << and_term 

지금 파 와 샘플 텍스트를 노래 :

res = expr.parseString(test) 
from pprint import pprint 
pprint(res.asList()) 

을 제공합니다 파서의 결과에 매우 유사한 사실

[[[[[[['"breast neoplasms"'], ['MeSH', 'Terms']], 
    'or', 
    [['breast', 'cancer'], ['Acknowledgments']], 
    'or', 
    [['breast', 'cancer'], ['Figure/Table', 'Caption']], 
    'or', 
    [['breast', 'cancer'], ['Section', 'Title']], 
    'or', 
    [['breast', 'cancer'], ['Body', '-', 'All', 'Words']], 
    'or', 
    [['breast', 'cancer'], ['Title']], 
    'or', 
    [['breast', 'cancer'], ['Abstract']], 
    'or', 
    [['breast', 'cancer'], ['Journal']]]]], 
    'and', 
    [[[[['prevention'], ['Acknowledgments']], 
    'or', 
    [['prevention'], ['Figure/Table', 'Caption']], 
    'or', 
    [['prevention'], ['Section', 'Title']], 
    'or', 
    [['prevention'], ['Body', '-', 'All', 'Words']], 
    'or', 
    [['prevention'], ['Title']], 
    'or', 
    [['prevention'], ['Abstract']]]]]]] 

을. 우리는 이제이 구조체를 통해 반복하여 새 쿼리 문자열을 작성할 수 있습니다. 그러나 구문 분석 된 객체를 사용하여 이것을 수행하는 것이 더 좋으며, 구문 분석시에 클래스를 Group 대신 토큰 컨테이너로 정의한 다음 클래스에 동작을 추가하여 원하는 출력. 구별되는 개체 토큰 컨테이너는 구문 분석 된 식의 종류에 특정한 동작을 가질 수 있습니다.

기본 추상 클래스 인 ParsedObject으로 시작하여 파싱 된 토큰을 초기화 구조로 사용합니다. 우리는 또한 우리가 원하는 출력을 생성하는 모든 파생하는 클래스에 구현하는 것이다 추상적 인 방법 queryString, 추가 할 것입니다 :

class ParsedObject(object): 
    def __init__(self, tokens): 
     self.tokens = tokens 
    def queryString(self): 
     '''Abstract method to be overridden in subclasses''' 

이제 우리는이 클래스에서 파생 할 수 있습니다 및 모든 서브 클래스는로 사용할 수 있습니다 문법을 정의 할 때 구문 분석 동작. 우리는이 작업을 수행 할 때

, 구조 추가 된 Group들 종류의 우리의 방식으로, 그래서 우리는 그들없이 원래의 파서를 다시 정의 할 수 있습니다 :

search_term = Group(OneOrMore(word) | quotedString)('search_text') + 
        Optional(field_qualifier)('field') 
atom = search_term | (LPAREN + expr + RPAREN) 
or_term = atom + ZeroOrMore(or_op + atom) 
and_term = or_term + ZeroOrMore(and_op + or_term) 
expr << and_term 

이제 우리는 사용 search_term의 클래스를 구현 self.tokens는 입력 문자열에있는 구문 분석 비트에 액세스 :

class SearchTerm(ParsedObject): 
    def queryString(self): 
     text = ' '.join(self.tokens.search_text) 
     if self.tokens.field: 
      return '%s: %s' % (' '.join(f.lower() 
             for f in self.tokens.field[0]),text) 
     else: 
      return text 
search_term.setParseAction(SearchTerm) 

다음으로 우리가 and_termor_term 표현을 구현하는 것입니다. 모두 출력 쿼리에서의 결과 연산자 문자열 만 다른 이항 연산자, 그래서 우리는 단지 하나 개의 클래스를 정의하고 그들의 각각의 운영자 문자열 상수 클래스를 제공하도록 할 수 있습니다 : 대한 파싱이 조금 다르다는 것을

class BinaryOperation(ParsedObject): 
    def queryString(self): 
     joinstr = ' %s ' % self.op 
     return joinstr.join(t.queryString() for t in self.tokens[0::2]) 
class OrOperation(BinaryOperation): 
    op = "OR" 
class AndOperation(BinaryOperation): 
    op = "AND" 
or_term.setParseAction(OrOperation) 
and_term.setParseAction(AndOperation) 

주 전통적인 파서의 경우 - BinaryOperation은 "a 또는 b 또는 c"를 중첩 된 쌍 "(a 또는 b) 또는 c"가 아닌 단일 표현식과 일치시킵니다. 따라서 스테핑 슬라이스 [0::2]을 사용하여 모든 조건에 다시 가입해야합니다.여기에서 복사/붙여 넣을 수있는 블록의 전체 파서이며, 여러분의 편의를 위해

class Expr(ParsedObject): 
    def queryString(self): 
     return '(%s)' % self.tokens[0].queryString() 
expr.setParseAction(Expr) 

:

마지막으로, 우리는()의 모든 exprs 포장에 의한 중첩을 반영하기 위해 구문 분석 작업을 추가

난 당신이 t의 일부를 강화해야합니다 같은데요

((mesh terms: "breast neoplasms" OR acknowledgments: breast cancer OR 
    figure/table caption: breast cancer OR section title: breast cancer OR 
    body - all words: breast cancer OR title: breast cancer OR 
    abstract: breast cancer OR journal: breast cancer) AND 
(acknowledgments: prevention OR figure/table caption: prevention OR 
    section title: prevention OR body - all words: prevention OR 
    title: prevention OR abstract: prevention)) 

을 다음 인쇄

from pyparsing import * 

LBRACK,RBRACK,LPAREN,RPAREN = map(Suppress,"[]()") 
and_op = CaselessKeyword('and') 
or_op = CaselessKeyword('or') 
word = ~(and_op | or_op) + Word(alphanums + '.-/&') 
field_qualifier = Group(LBRACK + OneOrMore(word) + RBRACK) 

search_term = (Group(OneOrMore(word) | quotedString)('search_text') + 
       Optional(field_qualifier)('field')) 
expr = Forward() 
atom = search_term | (LPAREN + expr + RPAREN) 
or_term = atom + ZeroOrMore(or_op + atom) 
and_term = or_term + ZeroOrMore(and_op + or_term) 
expr << and_term 

# define classes for parsed structure 
class ParsedObject(object): 
    def __init__(self, tokens): 
     self.tokens = tokens 
    def queryString(self): 
     '''Abstract method to be overridden in subclasses''' 

class SearchTerm(ParsedObject): 
    def queryString(self): 
     text = ' '.join(self.tokens.search_text) 
     if self.tokens.field: 
      return '%s: %s' % (' '.join(f.lower() 
             for f in self.tokens.field[0]),text) 
     else: 
      return text 
search_term.setParseAction(SearchTerm) 

class BinaryOperation(ParsedObject): 
    def queryString(self): 
     joinstr = ' %s ' % self.op 
     return joinstr.join(t.queryString() 
           for t in self.tokens[0::2]) 
class OrOperation(BinaryOperation): 
    op = "OR" 
class AndOperation(BinaryOperation): 
    op = "AND" 
or_term.setParseAction(OrOperation) 
and_term.setParseAction(AndOperation) 

class Expr(ParsedObject): 
    def queryString(self): 
     return '(%s)' % self.tokens[0].queryString() 
expr.setParseAction(Expr) 


test = """("breast neoplasms"[MeSH Terms] OR breast cancer[Acknowledgments] 
OR breast cancer[Figure/Table Caption] OR breast cancer[Section Title] 
OR breast cancer[Body - All Words] OR breast cancer[Title] 
OR breast cancer[Abstract] OR breast cancer[Journal]) 
AND (prevention[Acknowledgments] OR prevention[Figure/Table Caption] 
OR prevention[Section Title] OR prevention[Body - All Words] 
OR prevention[Title] OR prevention[Abstract])""" 

res = expr.parseString(test)[0] 
print res.queryString() 

그의 출력 - 그 lucene 태그 이름은 매우 모호하게 보입니다 - 나는 단지 당신의 게시 된 샘플을 따르고있었습니다. 하지만 파서를 많이 변경하지 않아도되며 첨부 된 클래스의 queryString 메서드 만 조정하면됩니다.

포스터 추가 연습 : 쿼리 언어에 NOT 부울 연산자에 대한 지원을 추가하십시오.

+1

놀라운 대답 – cerberos

관련 문제