py将一个查询格式解析为另一个

2024-10-06 09:54:34 发布

您现在位置:Python中文网/ 问答频道 /正文

我不知所措。我已经试了好几天了。但这件事我做不到,所以我想我可以咨询你们这里的人,看看有没有人能帮我!在

我使用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,我可以得到以下结构:

^{pr2}$

但现在,我不知所措。我需要将上述输出格式化为lucene搜索查询。 下面是一个关于所需转换的简短示例:

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

但我被困在那里了。我还需要能够使用特殊的单词AND 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

Tags: ortitlegrouppyparsingstructurewordmeshterms
1条回答
网友
1楼 · 发布于 2024-10-06 09:54:34

好吧,你已经有了一个不错的开始。在这里,你可以很容易地从解析器中得到细节。让我们从原始查询语法开始逐步解决您的问题。在

当你开始这样的项目时,写一个你想要解析的语法的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_op和{}表达式之间存在一些可能的歧义,因为“and”和“or”确实符合单词的定义。我们需要在实现时加强这一点,以确保“癌症、癌症、淋巴瘤或黑色素瘤”被读取为4个不同的搜索词,用“或”隔开,而不仅仅是一个大的搜索词(我认为这是您当前的解析器应该做的)。我们还得到了识别运算符优先级的好处-也许不是严格必要的,但我们现在就开始吧。在

转换为pyparsing非常简单:

^{pr2}$

为了解决“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']]]]]]]

实际上,与解析器的结果非常相似。我们现在可以通过这个结构递归并构建新的查询字符串,但是我更喜欢使用解析对象来完成这项工作,在解析时通过将类定义为令牌容器而不是Groups来创建,然后向类中添加行为以获得所需的输出。区别在于,我们解析的对象令牌容器可以具有特定于被解析的表达式类型的行为。在

我们将从一个抽象基类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)

请注意,pyparsing与传统解析器稍有不同,我们的BinaryOperation将把“a或b或c”作为单个表达式匹配,而不是作为嵌套对(a或b)或c。所以我们必须使用步进片[0::2]重新连接所有的术语。在

最后,我们添加了一个parse操作,通过将所有表达式包装在()中来反映任何嵌套:

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 boolean运算符的支持。在

相关问题 更多 >