2014-01-14 2 views
1

У меня есть коллекция книг в моей базе данных МонгоМонго регулярного выражение не соответствует слову расположения строки префикса

{ 
    "title": "Some cool title", 
    "authors": [ "Author1", "Author2", ... ], 
    ... 
} 

Я хочу, чтобы создать достаточно умный поисковик для этих книг. Если пользователь вводит что-то на вход поиска, это происходит: входную строку

  1. Преобразовать в массив ключевых слов
  2. поиск всех документов, в которых по крайней мере один ключевом слове соответствует названию или имя любого автора

Тогда Я делаю с ней немного волшебства, но мне нужна помощь в этом - когда я говорю, что ключевое слово соответствует заголовку/автору, я имею в виду, что оно соответствует некоторому слову в заголовке/авторе или его префиксе. Например, do будет соответствовать любой строке, содержащей do, 10, double в ней, но не ado или badoo.

Я гугле его, и это должно быть правильный способ сделать это:

public function searchBooksByKeywords($keywords) { 

    array_walk($keywords, function(&$keyword) { 
     $keyword = preg_quote($keyword, "/"); 
    }); 

    $filter = array(
     '$or'  => [ 
      [ "title" => new \MongoRegex("/\\b(" . implode('|', $keywords) . ")/i") ], 
      [ "authors" => new \MongoRegex("/\\b(" . implode('|', $keywords) . ")/i") ], 
     ] 
    ); 

    $books = $this->database->Books->find($filter); 
    return \iterator_to_array($books); 
} 

Это не работает. Я все еще получаю результаты, такие как steal для tea, то есть он соответствует даже подстрокам внутри слов, а не только префиксам. Я здесь довольно утерян ...

BTW, я использую PHP.

EDIT: Я нашел вероятную причину проблемы. В случае совпадения внутри слова искомое слово происходит сразу после некоторого символа, отличного от ASCII (но, может быть, и не для всех), например, я искал sto и получил результаты, такие как Město & město, для ste найдено Kroatien Dalmatinische Küste и Ostseeküste,Darss,Rostock и т. Д.

+0

Вы распечатали строку регулярных выражений образца, чтобы увидеть, как она выглядит? – sln

+0

Ну, например, если вы ищете 'steve jobs', строка регулярного выражения будет'/\ b (steve | jobs)/i' –

ответ

2

Я наконец нашел решение. Я просто добавил флаг u в регулярное выражение.

new \MongoRegex("/\\b(" . implode('|', $keywords) . ")/iu" 

PHP Документация говорит

Этот модификатор включает дополнительную функциональность PCRE, которая не совместима с Perl. Строки шаблонов рассматриваются как UTF-8. Этот модификатор доступен с PHP 4.1.0 или выше в Unix и с PHP 4.2.3 на win32. Срок действия шаблона UTF-8 проверяется с PHP 4.3.5.

here.

1

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

new \MongoRegex("/\\b(" . implode('|', $keywords) . ").*\\b/i") 

EDIT:

Как О.П. упоминает в своем редактировать, выше регулярное выражение не выполняется для ключевых слов, содержащих не-ASCII символы, например, ключевое слово sto матчи результаты, как Město & město, для ste он соответствует Küste, .. и т.д.

Таким образом, в этом случае, я изменил регулярное выражение следующим образом:

new \MongoRegex("/(?:^|\\s)(" . implode('|', $keywords) . ")/i") 

регулярных выражений Пример: http://regex101.com/r/nR9lH6

+0

, которая не поможет, см. Отредактированный вопрос –

0

После просмотра вашего редактирования , его ясно, что вам необходимо улучшить ограничение слов, чтобы ограничить только символы ASCII только
. Есть много способов сделать это.

Если первый символ в строке поиска/ключевого слова может быть между \ x80 - \ xff, тогда необходим совершенно другой подход. Надеюсь, это не так.

new \MongoRegex("/(?:^|(?<=[\\x00-\\x7f]))(?=[\\x00-\\x7f])\\b(" . implode('|', $keywords) . ")/i") 

# -------------------------------------------- 
# Using hex 
(?:       # Group start 
    ^       # Beginning of string 
    | (?<= [\x00-\x7f])   # or, ASCII character behind us 
)        # Group end 
(?= [\x00-\x7f])    # ASCII character in front of us 
\b       # word boundry 

# -------------------------------------------- 
# Using Posix 
(?:       # Group start 
    ^       # Beginning of string 
    | (?<= [[:ascii:]])   # or, ASCII character behind us 
)        # Group end 
(?= [[:ascii:]])    # ASCII character in front of us 
\b       # word boundry 
Смежные вопросы