Регулярное выражение для поиска допустимых полей сфинкса - PullRequest
2 голосов
/ 20 апреля 2010

Я пытаюсь проверить, что поля, данные для sphinx, действительны, но у меня возникли трудности.

Представьте, что допустимыми полями являются кошка, мышь, собака, щенок.

Допустимые результаты поиска будут:

  • @ условия поиска cat
  • @ (cat) условия поиска
  • @ (кошка, собака) поисковый запрос
  • @ cat searchterm1 @dog searchterm2
  • @ (кошка, собака) searchterm1 @mouse searchterm2

Итак, я хочу использовать регулярное выражение, чтобы найти такие термины, как cat, dog, mouse в приведенных выше примерах, и сравнить их со списком допустимых терминов.

Таким образом, запрос такой как: @ (Коза)

Может привести к ошибке, потому что коза не является допустимым термином.

Я получил, чтобы я мог найти простые запросы, такие как @cat, с помощью этого регулярного выражения: (?: @) ([^ (] *)

Но я не могу понять, как найти остальное.

Я использую python & django, для чего это стоит.

Ответы [ 6 ]

3 голосов
/ 20 апреля 2010

Чтобы соответствовать всем допустимым полям, работает следующее довольно страшное регулярное выражение:

@((?:cat|mouse|dog|puppy)\b|\((?:(?:cat|mouse|dog|puppy)(?:, *|(?=\))))+\))

Возвращает эти совпадения в следующем порядке: @cat, @(cat), @(cat, dog), @cat, @dog, @(cat, dog), @mouse.

Регулярное выражение распадается следующим образом:

@                               # the literal character "@"
(                               # match group 1
  (?:cat|mouse|dog|puppy)       #  one of your valid search terms (not captured)
  \b                            #  a word boundary
  |                             #  or...
  \(                            #  a literal opening paren
  (?:                           #  non-capturing group
    (?:cat|mouse|dog|puppy)     #   one of your valid search terms (not captured)
    (?:                         #   non-capturing group
      , *                       #    a comma "," plus any number of spaces
      |                         #    or...
      (?=\))                    #    a position followed by a closing paren
    )                           #   end non-capture group
  )+                            #  end non-capture group, repeat
  \)                            #  a literal closing paren
)                               # end match group one.

Теперь, чтобы идентифицировать любой недопустимый поиск, вы бы завернули все это в негативный прогноз:

@(?!(?:cat|mouse|dog|puppy)\b|\((?:(?:cat|mouse|dog|puppy)(?:, *|(?=\))))+\))
--^^

Это идентифицирует любой символ @, после которого была предпринята попытка неверного поискового термина (или комбинации терминов). Изменение его так, чтобы оно также соответствовало недопустимой попытке вместо того, чтобы просто указывать на нее, теперь не так сложно.

Вы должны подготовить (?:cat|mouse|dog|puppy) из вашего поля динамически и подключить его к статическому остатку регулярного выражения. Не должно быть слишком сложно сделать.

2 голосов
/ 21 апреля 2010

Это решение для пиапарсинга следует тому же логическому пути, что и ваш опубликованный ответ. Все теги сопоставляются, а затем проверяются по списку известных допустимых тегов, удаляя их из сообщенных результатов. Только те совпадения, у которых есть значения, оставшиеся после удаления действительных, отображаются как совпадения.

from pyparsing import *

# define the pattern of a tag, setting internal results names for easy validation
AT,LPAR,RPAR = map(Suppress,"@()")
term = Word(alphas,alphanums).setResultsName("terms",listAllMatches=True)
sphxTerm = AT + ~White() + ( term | LPAR + delimitedList(term) + RPAR )

# define tags we consider to be valid
valid = set("cat mouse dog".split())

# define a parse action to filter out valid terms, and attach to the sphxTerm
def filterValid(tokens):
    tokens = [t for t in tokens.terms if t not in valid]
    if not(tokens):
        raise ParseException("",0,"")
    return tokens
sphxTerm.setParseAction(filterValid)


##### Test out the parser #####

test = """@cat search terms @ house
    @(cat) search terms 
    @(cat, dog) search term @(goat)
    @cat searchterm1 @dog searchterm2 @(cat, doggerel)
    @(cat, dog) searchterm1 @mouse searchterm2 
    @caterpillar"""

# scan for invalid terms, and print out the terms and their locations
for t,s,e in sphxTerm.scanString(test):
    print "Terms:%s Line: %d Col: %d" % (t, lineno(s, test), col(s, test))
    print line(s, test)
    print " "*(col(s,test)-1)+"^"
    print

С этими прекрасными результатами:

Terms:['goat'] Line: 3 Col: 29
    @(cat, dog) search term @(goat)
                            ^

Terms:['doggerel'] Line: 4 Col: 39
    @cat searchterm1 @dog searchterm2 @(cat, doggerel)
                                      ^

Terms:['caterpillar'] Line: 6 Col: 5
    @caterpillar
    ^

Этот последний фрагмент выполнит все проверки за вас и предоставит список найденных недействительных тегов:

# print out all of the found invalid terms
print list(set(sum(sphxTerm.searchString(test), ParseResults([]))))

Печать:

['caterpillar', 'goat', 'doggerel']
1 голос
/ 20 апреля 2010

Это должно работать:

@\((cat|dog|mouse|puppy)\b(,\s*(cat|dog|mouse|puppy)\b)*\)|@(cat|dog|mouse|puppy)\b

Оно будет соответствовать либо одному @parameter, либо круглому @(par1, par2) списку, содержащему только разрешенные слова (одно или несколько).

Это такжеудостоверяется, что частичные совпадения не принимаются (@caterpillar).

0 голосов
/ 21 апреля 2010

Я закончил тем, что сделал это по-другому, так как ничего из вышеперечисленного не сработало. Сначала я нашел поля типа @cat с этим:

attributes = re.findall('(?:@)([^\( ]*)', query)

Далее я нашел более сложные, с этим:

regex0 = re.compile('''
    @               # at sign
    (?:             # start non-capturing group
        \w+             # non-whitespace, one or more
        \b              # a boundary character (i.e. no more \w)
        |               # OR
        (               # capturing group
            \(              # left paren
            [^@(),]+        # not an @(),
            (?:                 # another non-caputing group
                , *             # a comma, then some spaces
                [^@(),]+        # not @(),
            )*              # some quantity of this non-capturing group
            \)              # a right paren
        )               # end of non-capuring group
    )           # end of non-capturing group
    ''', re.VERBOSE)

# and this puts them into the attributes list.
groupedAttributes = re.findall(regex0, query)
for item in groupedAttributes:
    attributes.extend(item.strip("(").strip(")").split(", "))

Затем я проверил, были ли найденные атрибуты действительными, и добавил их (уникально в массив):

# check if the values are valid.
validRegex = re.compile(r'^mice$|^mouse$|^cat$|^dog$')

# if they aren't add them to a new list.
badAttrs = []
for attribute in attributes:
    if len(attribute) == 0:
        # if it's a zero length attribute, we punt
        continue
    if validRegex.search(attribute.lower()) == None:
        # if the attribute from the search isn't in the valid list
        if attribute not in badAttrs:
            # and the attribute isn't already in the list
            badAttrs.append(attribute)

Спасибо всем за помощь, хотя. Я очень рад, что получил это!

0 голосов
/ 20 апреля 2010

Попробуйте это:

field_re = re.compile(r"@(?:([^()\s]+)|\([^()]+\))")

Одно имя поля (например, cat в @cat) будет занесено в группу # 1, а имена в скобках, такие как @(cat, dog), будут сохранены в группе # 2. В последнем случае вам нужно разбить список на split() или что-то еще; нет способа получить имена по отдельности с помощью регулярного выражения Python.

0 голосов
/ 20 апреля 2010

Это будет соответствовать всем полям, которые являются кошкой, собакой, мышью или щенком и их комбинациями.

import re
sphinx_term = "@goat some words to search"
regex = re.compile("@\(?(cat|dog|mouse|puppy)(, ?(cat|dog|mouse|puppy))*\)? ")
if regex.search(sphinx_term):
    send the query to sphinx...
...