?

Log in

No account? Create an account

selectize.js и полнотекстовой поиск

« previous entry | next entry »
Apr. 21st, 2015 | 11:55 am

Сегодня столкнулся с проблемой у selectize.js. Вообще не могу сказать, что библиотека идеальная. В проекте, который я разрабатываю, selectize используется довольно часто. И уже очень много проблем с ним было выявлено. Держимся за него лишь потому, что он позволяет удобно добавлять новые элементы в выпадающий список на лету.

Одна из таких найденных проблем - это фильтрация результатов, полученных с сервера посредством AJAX.
У нас используется полнотекстовой поиск по базе данных. Поле, по которому идёт поиск в PostgreSQL, нормализуется методом to_tsvector. Т.е. переводит слова в нижний регистр, удаляет знаки препинания. Допустим, у нас в БД лежит строка "Москва г, Ленинский пр-кт". Храним мы его для удобства полнотекстового поиска в индексе, получаемом командой to_tsvector. Вот, что она делает с этим запросом:




# select to_tsvector('Москва г, Ленинский пр-кт');
                      to_tsvector                       
--------------------------------------------------------
 'г':2 'кт':6 'ленинский':3 'москва':1 'пр':5 'пр-кт':4




Теперь допустим, что мы в поле, обрабатываемом selectize'ом вводим "Москва, ленинский пр". При этом, команда plainto_tsquery преобразует запрос следующим образом:




# select plainto_tsquery('Москва, ленинский пр');
        plainto_tsquery        
-------------------------------
 'москва' & 'ленинский' & 'пр'




Всё в порядке. Хоть напрямую и запрос не находит нужную строку:

# select to_tsvector('Москва г, Ленинский пр') @@ plainto_tsquery('Москва, ленинский пр-кт');

Это происходит лишь потому что искомый запрос не является точным повторением строки, лежащей в базе. Но при использовании индекса gin, запрос отлично отрабатывает. Но здесь мы уже сталкиваемся с фильтрацией selectize'a. Т.е. мы получаем с сервера "Москва г, Ленинский пр-кт", но т.к. он не совпадает с искомым "Москва, ленинский пр", selectize отбрасывает этот вариант и в выдаче ничего не выводится.

К сожалению, у selectize по-умолчанию не предусмотрено отключение фильтрации. Пришлось делать это посредством костылей. Selectize сортирует результаты, используя вес, описываемый функцией в параметре score. Если вес равен 0, то он отсеивает результат. Т.к. сортировку всё равно удобнее производить на стороне сервера, то присвоим этому весу всегда значение 1. Тогда результаты будут выводиться в том порядке, в котором они приходят с сервера:

score: function(){return function(item){return 1}}.

Но тогда результаты вообще перестают пропадать из списка и новые запросы лишь дописывают результаты снизу. Придётся чистить перед каждым ajax-запросом список selectize'a. Вставим перед ajax-запросом следующую строку:



$full_text_name[0].selectize.clearOptions();



Вуаля! Теперь запросы фильтруются на стороне сервера, а selectize их не трогает.

Link | Leave a comment | Share

Comments {0}