我很茫然。 我一直试图让这个到现在的天工作。 但我不是这个Anywhere入门,所以我想我会在这里请教你们,看看是否有人能帮助我!
我使用的企图一个查询格式解析到另一个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,我是通缉令,以蟒蛇。 我已经粘贴下面,这样就可以发挥与它周围的代码,不要有在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
嗯,你已经得到了你自己开了一个不错的开局。 但是,从这里,很容易解析器,扭捏的细节,陷入了下来,你可以在该模式下了好几天。 让我们一步步通过你的问题与原来的查询语法开始。
当你与这样一个项目开始时,写上你要解析语法的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
表达,因为“和”和“或”不匹配一个单词的定义。 我们需要在实施时间收紧这件事,以确保“癌症或癌或淋巴瘤或黑色素瘤”被理解为通过分隔的4个不同的搜索词“或的,不只是一个大项(我认为这是你目前的解析器会做)。 我们也认识到获得运营商的优先利益 - 也许不是绝对必要的,但让我们用它去了。
转换为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
为了解决“或”,“与”歧义,我们把负先行在单词的开头:
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
s,然后添加行为的类,让我们的期望的输出。 区别在于,我们的分析对象令牌容器可以有行为是特定于该被解析的那种表情。
我们会用碱抽象类,开始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]
最后,我们添加了一个解析动作的所有包裹在exprs()的,以反映任何嵌套:
class Expr(ParsedObject):
def queryString(self):
return '(%s)' % self.tokens[0].queryString()
expr.setParseAction(Expr)
为方便起见,这里是一个复制/ pastable块整个解析器:
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布尔运算符的支持。