我不知所措。几天来,我一直在努力让它正常工作。但是我在这方面没有任何进展,所以我想我应该在这里咨询你们,看看是否有人能够帮助我!
我使用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])使用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']]]]但现在,我感到不知所措。我需要将上面的输出格式化为lucene搜索查询。下面是一个关于所需转换的简短示例:
"breast neoplasms"[MeSH Terms] --> [['"', 'breast', 'neoplasms', '"'],
['MeSH', 'Terms']] --> mesh terms: "breast neoplasms"但我被困在那里了。我还需要能够使用特殊的单词AND和OR。
因此,最后的查询可能是:网格术语:“乳腺肿瘤”和预防
谁能帮助我,给我一些关于如何解决这个问题的提示?任何形式的帮助都将不胜感激。
因为我使用的是pyparsing,所以我只能使用python。我已经粘贴了下面的代码,这样你就可以使用它,而不必从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发布于 2012-04-12 12:56:08
好吧,你已经给自己开了个好头。但是在这里,很容易陷入解析器调整的细节中,而且您可能会在这种模式下工作几天。让我们从原始的查询语法开始逐步介绍您的问题。
当你开始一个像这样的项目时,编写一个你想要解析的语法的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 ')')这非常接近-我们在word与and_op和or_op表达式之间存在一些可能的歧义,因为' and‘和'or’确实与单词的定义相匹配。我们需要在实现时加强这一点,以确保“癌症、癌症、淋巴瘤或黑色素瘤”被理解为4个不同的搜索词,由‘or’隔开,而不仅仅是一个大词(我认为这是你当前的解析器所做的)。我们还能从识别运算符的优先级中获益--也许不是严格意义上的必要,但现在就让我们来看看吧。
转换为pyparsing非常简单:
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为了解决'or‘和' and’的歧义,我们在单词的开头放了一个否定的前视:
word = ~(and_op | or_op) + Word(alphanums + '.-/&')要为结果提供一些结构,请包装在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_term和or_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)请注意,pyparsing与传统解析器略有不同-我们的BinaryOperation将匹配"a或b或c“作为单个表达式,而不是作为嵌套对"(a或b)或c”。因此,我们必须使用步进切片[0::2]重新连接所有项。
最后,我们添加一个解析操作,通过将所有表达式包装在()的:
class Expr(ParsedObject):
def queryString(self):
return '(%s)' % self.tokens[0].queryString()
expr.setParseAction(Expr)为了方便起见,下面是一个副本/可粘贴块中的整个解析器:
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()该命令将打印以下内容:
((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))我猜你需要加强一些输出--那些lucene标签名称看起来非常模糊--我只是在关注你发布的样本。但是您不需要过多地更改解析器,只需调整附加类的queryString方法即可。
作为向海报添加的练习:在查询语言中添加对NOT布尔运算符的支持。
https://stackoverflow.com/questions/10113245
复制相似问题