Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Поиск не парсит слэши #1052

Open
2 tasks done
GRbit opened this issue Feb 19, 2023 · 10 comments
Open
2 tasks done

Поиск не парсит слэши #1052

GRbit opened this issue Feb 19, 2023 · 10 comments

Comments

@GRbit
Copy link
Contributor

GRbit commented Feb 19, 2023

Чеклист

  • Я поискал поиском по трекеру похожие проблемы, в том числе в закрытых Issues
  • Баг стабильно воспроизводится и я знаю как это сделать

Описание бага

Есть пост https://vas3k.club/question/18282/
Очень хочется его найти по слову mastodon, но так как рядом с ним слэш — поиск не выдаёт ничего путного:
https://vas3k.club/search/?q=mastodon&type=post

Ожидаемый результат

Видеть пост в результатах поиска

Шаги к воспроизведению

Да вроде и так всё ясно

@zyuhel
Copy link
Contributor

zyuhel commented Mar 30, 2023

Это особенность работы ts_vector, он не в одном из конфигов не считает / как разделитель слов, мало ли, вдруг ищете http/2.0 и хотите чтобы он находил только его, а не http и 2 и 0 :)
вроде бы . тоже является разделителем только если в конце строки, или с пробелом
и mastodon.twitter, также не будет находиться по mastodon.twitter
(точки потому что тип host, слеши потом что тип url, ну а собачка не разделитель так как email)
вообще имхо стоило бы в таком случае индексить и то и другое, но это как бы поведение, которое должно быть настраиваемым.

мне видится четыре решения:

  1. Переделывать парсер постгри, (CREATE TEXT SEARCH PARSER) , а дальше альтерить конфигурации чтобы они юзали новый парсер. - в принципе самое адекватное решение, но жесткий оверкилл
  2. Приделать другой fts - manticore/lucene и переделывать куча всего - не вариант
  3. Преобразовывать данные перед поиском, и перед индексацией. То есть в зависимости от того какое поведение желаемое, либо просто заменять / на пробел, либо добавляя в конце отдельно разделенные слова. При втором варианте не нужно будет изменять данные перед поиском, только перед индексацией. Да индекс вырастит, да и индексацию возможно придется делать в виде чего то типа to_tsvector('russian', concat(field, translate(field,'/@.',' ')), как это подружить с джанго, я без понятия. но это максимально просто решение в плане трудозатрат.
  4. Забить.

@GRbit
Copy link
Contributor Author

GRbit commented Mar 30, 2023

@zyuhel
Пункт 3 правда звучит неплохо. Понятия не имею правда как именно это сделать.

Могу только предложить регулярку для поиска элементов через слэши которые не похожи на веб адрес:
[^ :/\.]*(/[^ /]*)+
Вот можно ей проходится по тексту, и в в итоге то что она найдёт делить на слова и добавлять к индексу.
Чуть поясню за реглярку: ищет что-то что начинается с пробела, так чтобы первое слово не было похоже на веб адрес. Похожесть проверяется по содержанию двоеточия или точки. Т.е. то что начинается с vas3k.com или http:// не подпадает. А дальше ищутся не разделённые пробелами куски типа /чето-то/там/ещё в любом количестве.

@zyuhel
Copy link
Contributor

zyuhel commented Mar 30, 2023

@GRbit с учетом того что код кидает в django'вский SearchVector, вот так _multi_search_vector("full_name", weight="A") , а дальше это идет в ts_vector, я опасаюсь что приделывание туда регулярок будет выглядеть максимально костыльно. я ни черта не разбираюсь в django, мне на ум приходят только костыли, в виде создания вирт колонки, с нужным преобразованием, и потом использовать ее как дополнительное поле по которому ищется. Но оно даже кажется не красивым

@zyuhel
Copy link
Contributor

zyuhel commented Mar 30, 2023

@vas3k тонкий стеб? :)

@vas3k
Copy link
Owner

vas3k commented Mar 30, 2023

Не так прочитал

@GRbit
Copy link
Contributor Author

GRbit commented Mar 31, 2023

@zyuhel Я с джанго не работал никогда. Как видишь ток в регулярки могу) Ну и когда ты говоришь про вирт колонки мне это ни о чём не говорит, к сожалению.

Почитал код немножк. Ну регулярки смотрятся костыльно, но работать же будут) Вариант который мне видится более "общим" это добавление доп поля в Post типа "search_additions" и его индексирование. Может это и есть то что ты имел ввиду с вирт колонкой.

Тут как говорится "шо то костыль, шо это костыль". Грамотно будет фиксить то как индекс строится и учить его отличать ссылки от простого перечисления через слэши, при этом давать больший вес на матч всего куска со слэшами и меньший на мэтч отдельных элементов, но тип кто за такую фундаментальную штуку возьмётся?

Имхо костылю главное лишних сайд-эффектов не добавлять. Из проблем с регуляркой которые я вижу, не смотря на то что они работают быстро, гонять её по всему тексту поста возможно не стоит и можно ограничиться тайтлом.

@GRbit
Copy link
Contributor Author

GRbit commented Mar 31, 2023

Я к сожалению на PyCharm community editions и с питоном последний раз связывался лет 7 назад, так что мне стыдно PR открывать, но по-моему что-то такое должно помочь.

Можешь дать небольшой фидбек? Если кажется норм, то я могу PR открыть

diff --git a/search/models.py b/search/models.py
index 47b5745..f1131b4 100644
--- a/search/models.py
+++ b/search/models.py
@@ -37,6 +37,10 @@ class SearchIndex(models.Model):
 
     index = SearchVectorField(null=False, editable=False)
 
+    # regular expression to find words with "/" deimeter, but not web addresses like http://soonething.com/asdas or
+    # vas3k.com/some/word
+    reSlash = re.compile(' [^ \/:\.]+\/([^ ]+)')
+
     class Meta:
         db_table = "search_index"
         ordering = ["-created_at"]
@@ -80,6 +84,8 @@ class SearchIndex(models.Model):
                  + _multi_search_vector("topic__name", weight="C")
 
         if post.is_searchable:
+            post.title = cls.improve_title_search(post.title)
+
             SearchIndex.objects.update_or_create(
                 post=post,
                 defaults=dict(
@@ -96,6 +102,17 @@ class SearchIndex(models.Model):
         else:
             SearchIndex.objects.filter(post=post).delete()
 
+    def improve_title_search(cls, title):
+        all = cls.reSlash.findAll(title)
+        groupsJoined = [i[0]+i[1] for i in all]
+
+        words = []
+        for s in groupsJoined:
+            words += s.split("/")
+
+        return title + " ".join(words)
+
+

@ngrishanov
Copy link

Я к сожалению на PyCharm community editions и с питоном последний раз связывался лет 7 назад, так что мне стыдно PR открывать, но по-моему что-то такое должно помочь.

Можешь дать небольшой фидбек? Если кажется норм, то я могу PR открыть

diff --git a/search/models.py b/search/models.py
index 47b5745..f1131b4 100644
--- a/search/models.py
+++ b/search/models.py
@@ -37,6 +37,10 @@ class SearchIndex(models.Model):
 
     index = SearchVectorField(null=False, editable=False)
 
+    # regular expression to find words with "/" deimeter, but not web addresses like http://soonething.com/asdas or
+    # vas3k.com/some/word
+    reSlash = re.compile(' [^ \/:\.]+\/([^ ]+)')
+
     class Meta:
         db_table = "search_index"
         ordering = ["-created_at"]
@@ -80,6 +84,8 @@ class SearchIndex(models.Model):
                  + _multi_search_vector("topic__name", weight="C")
 
         if post.is_searchable:
+            post.title = cls.improve_title_search(post.title)
+
             SearchIndex.objects.update_or_create(
                 post=post,
                 defaults=dict(
@@ -96,6 +102,17 @@ class SearchIndex(models.Model):
         else:
             SearchIndex.objects.filter(post=post).delete()
 
+    def improve_title_search(cls, title):
+        all = cls.reSlash.findAll(title)
+        groupsJoined = [i[0]+i[1] for i in all]
+
+        words = []
+        for s in groupsJoined:
+            words += s.split("/")
+
+        return title + " ".join(words)
+
+
  1. Названия переменных в питоне пишут в snake_case, а не camelCase
  2. improve_title_search: плохо передает суть функции, лучше сделать его @property и назвать как-то в духе search_index_title
  3. в post.title записывать ничего не надо, если не собираешься сохранять в базу
  4. all уже зарезервировано в языке, нужно другое название
  5. groupsJoined = [i[0]+i[1] for i in all]: плохо понятно, что тут происходит. i обычно используют как название для индекса при итерации

Вообще лучше сразу PR сделать, его сильно удобнее ревьюить

@GRbit
Copy link
Contributor Author

GRbit commented Mar 31, 2023

@ngrishanov спасибо, это было полезно, теперь я осмелел до PR. Сделаю на днях, но мне с ним явно нужна будет помощь)

  1. Ок
  2. Ок
  3. Косяк. Но как тогда это в поисковый индекс добавить?
  4. Блин, а чего PyCharm молчит? И кстати я что-то не нахожу https://www.w3schools.com/python/python_ref_keywords.asp точно all зарезервирован?
  5. Поправлю

@trin4ik
Copy link
Contributor

trin4ik commented Jun 14, 2024

Это особенность работы ts_vector, он не в одном из конфигов не считает / как разделитель слов, мало ли, вдруг ищете http/2.0 и хотите чтобы он находил только его, а не http и 2 и 0 :) вроде бы . тоже является разделителем только если в конце строки, или с пробелом и mastodon.twitter, также не будет находиться по mastodon.twitter (точки потому что тип host, слеши потом что тип url, ну а собачка не разделитель так как email) вообще имхо стоило бы в таком случае индексить и то и другое, но это как бы поведение, которое должно быть настраиваемым.

мне видится четыре решения:

  1. Переделывать парсер постгри, (CREATE TEXT SEARCH PARSER) , а дальше альтерить конфигурации чтобы они юзали новый парсер. - в принципе самое адекватное решение, но жесткий оверкилл
  2. Приделать другой fts - manticore/lucene и переделывать куча всего - не вариант
  3. Преобразовывать данные перед поиском, и перед индексацией. То есть в зависимости от того какое поведение желаемое, либо просто заменять / на пробел, либо добавляя в конце отдельно разделенные слова. При втором варианте не нужно будет изменять данные перед поиском, только перед индексацией. Да индекс вырастит, да и индексацию возможно придется делать в виде чего то типа to_tsvector('russian', concat(field, translate(field,'/@.',' ')), как это подружить с джанго, я без понятия. но это максимально просто решение в плане трудозатрат.
  4. Забить.

ку, а почему пункт 2 не вариант? я одно время активно внедрял сфинкс на волне его популярности, посмотрел мантик -- вроде всё просто. морфология из коробки, стоп-слова, словоформы итд итп всё есть. да и синтаксис поиска вполне понятный и богатый (https://manual.manticoresearch.com/Searching/Full_text_matching/Operators#Full-text-operators)
на примере поста с mastodon:

mysql> create table posts(`id`, `title` text, `text` text, `post_id` string, `type` string, `is_public` bool, `hotness` integer, `room_id` integer) morphology='stem_enru';
Query OK, 0 rows affected (0,01 sec)

mysql> insert into posts values (0, 'Клубные аккаунты в децентрализованных сетях типа mastodon/nostr/minds/diaspora/dtube', 'Есть посты про наши твиттеры, инстаграмы и фейсбуки, а про новомодные mastodon/nostr/whatever пока \(вроде\) ещё ничего официально не было.\n\nДавайте пиарить друг другу свои трансантиметакартошкафриджаз-аккаунты в
децентрализованных океанах слов, плавающих в пустоте одиночества китайской комнаты наших душ?..\n\nЯ сама, к сожалению, ничего туда не пишу \(ни в мастодон, ни в ностр\), но с удовольствием набрала бы себе более милую ленту.', '387ec440-64b6-4412-b8ed-1b6106c2c96e', 'question',1,0,1);
Query OK, 1 row affected (0,00 sec)

и результат

mysql> SELECT *, highlight() FROM posts WHERE match('mastodon');
+---------------------+---------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------+----------+-----------+---------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id                  | title                                                                                                                           | text                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | post_id                              | type     | is_public | hotness | room_id | highlight()                                                                                                                                                                                                                                                         |
+---------------------+---------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------+----------+-----------+---------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 4182053103148728335 | Клубные аккаунты в децентрализованных сетях типа mastodon/nostr/minds/diaspora/dtube                                            | Есть посты про наши твиттеры, инстаграмы и фейсбуки, а про новомодные mastodon/nostr/whatever пока (вроде) ещё ничего официально не было.

Давайте пиарить друг другу свои трансантиметакартошкафриджаз-аккаунты в децентрализованных океанах слов, плавающих в пустоте одиночества китайской комнаты наших душ?..

Я сама, к сожалению, ничего туда не пишу (ни в мастодон, ни в ностр), но с удовольствием набрала бы себе более милую ленту.                                                                                                                                                                                                                                                                                                                                           | 387ec440-64b6-4412-b8ed-1b6106c2c96e | question |         1 |       0 |       1 | Клубные аккаунты в децентрализованных сетях типа <b>mastodon</b>/nostr/minds/diaspora/dtube |  ...  и фейсбуки, а про новомодные <b>mastodon</b>/nostr/whatever пока (вроде) ещё ...                                                                                |
+---------------------+---------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------+----------+-----------+---------+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0,00 sec)
--- 1 out of 1 results in 0ms ---

ну и реализовать 4 метода:

  • добавление поста
  • изменение фултекст-индекса (редактирование текста/заголовка)
  • изменение строк/значений (редактирование параметров поста)
  • удаление поста

раскидать их по нужным местам как async_task и готово. не знаю как fulltext реализован в постгри, в мускуле это делалось через отдельный fulltext индекс на myisam. переход на сфинкс в своё время частенько приводил к экономии ресурсов

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants