Вложенная группа автоматически не приводит к катастрофическому обратному отскоку. В вашем случае это связано с тем, что ваше регулярное выражение вырождается в классический пример катастрофического обратного отсчета (a*)*
.
Поскольку \s
в необязательном в ^([a-zA-Z0-9'-]+\s?)*$
, на входе без каких-либо пробелов, но имеет символы вне разрешенных list, регулярное выражение просто вырождается до ^([a-zA-Z0-9'-]+)*$
.
Вы также можете думать о расширении исходного регулярного выражения:
[a-zA-Z0-9'-]+\s?[a-zA-Z0-9'-]+\s?[a-zA-Z0-9'-]+\s?[a-zA-Z0-9'-]+\s?...
Поскольку \s
является необязательным, мы можем удалите его:
[a-zA-Z0-9'-]+[a-zA-Z0-9'-]+[a-zA-Z0-9'-]+[a-zA-Z0-9'-]+...
И мы получили серию последовательных [a-zA-Z0-9'-]+
, которые будут пытаться распространять символы между собой и взорвать сложность.
Стандартный способ записи регулярного выражения в соответствие с token delimiter token ... delimiter token
- token (delimiter token)*
. Хотя можно переписать регулярное выражение, не повторять token
, я бы рекомендовал против него, так как это сложно сделать правильно. Чтобы избежать повторения, вы можете захотеть создать регулярное выражение путем конкатенации строк.
Следуя рецепту выше:
^[a-zA-Z0-9'-]+(\s[a-zA-Z0-9'-]+)*$
Хотя вы можете видеть повторение в повторении здесь, нет катастрофическое обратное отслеживание, поскольку регулярное выражение может расширяться только до:
[a-zA-Z0-9'-]+\s[a-zA-Z0-9'-]+\s[a-zA-Z0-9'-]+\s[a-zA-Z0-9'-]+...
И \s
и [a-zA-Z0-9'-]
взаимно исключают друг друга - есть только один способ сопоставления любой строки.
Короткий ответ - то, что можно использовать функциональность полнотекстового поиска Пост-ГРЭС со специализированной поисковой конфигурацией.
Начиная с Вашего контакта с названиями места, Ваш, вероятно, не хотят происходить, таким образом, можно использовать простую конфигурацию в качестве начальной точки. Можно также добавить стоп-слова, которые имеют смысл для названий места (с примерами выше, можно, вероятно, рассмотреть "Св.", "Святого" и "del" как стоп-слова).
А довольно основная схема установки Вашего специализированного ниже:
$SHAREDIR/tsearch_data
каталог Postgres. См. https://www.postgresql.org/docs/9.1/static/textsearch-dictionaries.html#TEXTSEARCH-STOPWORDS. pg_catalog.simple
в качестве шаблонного словаря). См. https://www.postgresql.org/docs/9.1/static/textsearch-dictionaries.html#TEXTSEARCH-SIMPLE-DICTIONARY. Другое соображение состоит в том, как рассмотреть интернационализацию. Кажется, что проблемой для Вашего второго примера (Ameca del Torro
по сравнению с Ameca Torro
) мог бы быть испанский по сравнению с английским представлением имени. Если это так, Вы могли также рассмотреть хранение и "локализованный" и "универсальное" (например, английский язык) версия названия города.
В конце, Ваш запрос (использующий полнотекстовый поиск) мог бы быть похожим на это (где 'места' являются названием Вашей поисковой конфигурации):
SELECT cities."id" FROM cities INNER JOIN "state" ON "state".id = cities.state_id WHERE "state".short_name = 'CA' AND TO_TSVECTOR('places', cities.name) @@ TO_TSQUERY('places', 'Los & Angeles')