Title, индексация и поиск (историческая статья)

С самого начала создания Rext с помощью FieldProcessing (создание полей) мы выделили некоторые базовые параметры страницы в отдельные поля. По-умолчанию, в PmWiki поле Дата недоступно пользователю и содержит автоматически заполняемую дату последнего изменения страницы. Поля description и title предлагается заполнять в теле страницы командами (:description:) и (:title:) соответственно. Мы их смело вынесли, а вот последствия этого шага стали ясны только сейчас.

С точки зрения архитектуры системы, исходная логика объяснима, Первоначально PmWiki — это система для создания Баз Знаний, энциклопедий. Предполагалось, что в ней будут храниться в основном термины / понятия на английском языке. Для этого в большинстве случаев подходят обычные URL, как мы знаем по Википедии. Поэтому неудобный (:title:) не предполагалось часто использовать. При этом вся ключевая информация хранилась в одном поле, что, как мы увидим ниже, удобно для индексации.

Однако при адаптации под российско-кириллическую действительность этот подход пришлось изменить, и вот почему:

  1. вот как выглядят полностью кириллические ссылки при их копировании через буфер обмена: 96b8b7bd8c/a1bfbe8082 :(
  2. сложившая практика SEO в нашей стране отдает предпочтение транcлитерированным URL, похожим на кириллический <h1> ;
  3. при использовании PmWiki в качестве Блога, заголовки не будут короткими, и их уж точно невозможно будет автоматически "восстановить" их из URL, как в оригинале.

Конечно, можно было бы создавать все новые страницы с автоматически подставляемым кодом вида (:title Title, индексация и поиск (историческая статья):) , однако это сильно усложнило бы масштабирование в дальнейшем, например внедрение EditTemplates.

Одним словом, по историческим причинам поля вынесены.
Так к чему же это привело?

К тому, что PmWiki перестала искать по ним. По-умолчанию, pagelist делает поиск пользуясь $PageIndexFile. В этот файл, кроме служебных данных, для каждой страницы записывается две вещи:

  • содержимое поля targets (т.е. backlinks, как хорошо, что при реализации MultyTags внутренний голос подсказал не создавать новое поле, а сделать лишь легкую надстройку над существующим)
  • индекс всех слов страницы

Таким образом, выполнение (:pagelist:) существенно ускоряется, особенно при поиске одиночных слов.

Однако в индекс попадает только содержание страницы. До выделения отдельных полей это не было проблемой, а теперь туда не попадает информация, содержащаяся в заголовке.

Что с этим можно сделать?

Вариант №1: попробовать включить кастомные поля в механизм индексации

Этот код живет в файле pagelist.php, функция PageIndexUpdate. Легко видеть, что ничего не получится. В функции не заложено подобной масштабируемости, надо конкретно править ядро, причем придется просить ведущего разработчика внедрить функционал, и это будет очень непросто. Кроме того, наверняка это не единственное место. Теоретически, PmWiki ведь должна работать и без индекса.

В общем, вариант отпадает.

Вариант №2: отнестись с этому философски

Плохо, что текст заголовков не попадает в поиск, зато поиск по заголовкам/дескрипшенам может быть отделен от прочих результатов, и, например приоритезирован.

Далее открывается некоторое поле для фантазии. Можно внедрить HttpVariables и после этого действовать примерно такими путями:

  • на Search сделать два последовательных вывода результатов — сначала совпадения в заголовках, потом в телах. Поиск получит некоторую релевантность. В принципе, можно наверное даже всю логику всунуть в один (:searchbox:)
  • при заполнении формы, можно быстренько делать AJAX-запрос и подгружать ссылки на статьи с совпадающими заголовками. Или даже не AJAX, если сайт не огромный, можно сгенерить всю его "карту" на странице поиска в скрытом виде.

В любом случае, остаются следующие минусы:

  • богатый синтаксис Search будет неприменим для поиска по заголовкам
  • некоторая просадка по производительности.

Итак, что же далеко ходить, вот базовый пример формы, осуществляющий выборку по полю title (используем HttpVariables для получения GET-параметров):

(:input form method=get:)
(:input search query value="{$?query}" :) (:input submit value="Искать":)
(:input end:)
(:if !equal {$?query} "":)
Результат поиска ''{$?query}'':
(:pagelist $Title=*{$?query}* fmt=#title :)
(:ifend:)

update:
Чтобы выборка корректно обрабатывала и "на литом", и "На литом", т.е. обучить ее пробелам и игнорировать большую букву, приходится костылить:

 PhQuery:"*{(tolower "{$?query}")}*,*{(ucfirst "{$?query}")}*"
 (:pagelist $Title={$:PhQuery} fmt=#local-search-news group=News : )

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

Пример страницы с поисковой формой

(:if false:)
PhQuery:"*{(tolower "{$?query}")}*,*{(ucfirst "{$?query}")}*"
[[#local-search]]
(:template default list=normal order=group,title:)
(:template first {=$Group}:)
{{=$Group}$Title}
(:template each:)
[[{=$Group}/{=$Name}|+]]
(:template none:)
Поиск не дал результатов. 
[[#local-searchend]]
(:ifend:)
!%apply=block ph-sandy% {$Title}
----
Словосочетание:
(:input form method=get:)
(:input search query value="{$?query}" :) (:input submit value="Искать":)
(:input end:)
(:if !equal {$?query} "":)
!!!Коллекция
(:pagelist $Title={$:PhQuery} fmt=#local-search:)
(:ifend:)

(:if [ auth edit && !equal {$?query} "" ] :)
>>admin<<
Исходный поисковый запрос:
{$?query}

К выборке:
{$:PhQuery}
>><<
(:ifend:)