Позднее Ctrl + ↑

Как прикрутить поисковый движок «Роза»

Я уже рассказывал, что в новой Эгее 2.6 — новый поисковый движок «Роза» Романа Парпалака. На примере Эгеи расскажу немного о том, как вы можете использовать его в своих продуктах.

Что делает Роза

Роза — движок поиска по переданным ему данным.

У Розы нет «паука» для индексации. Это специально. В случае с движком блога, например, нет никакого смысла искать ссылки, ходить по страницам, разбирать их код. И ещё убеждаться, что не индексируешь одну и ту же заметку несколько раз, потому что она встретились на страницах разных тегов. Это лишняя работа: приложение-клиент знает, как устроены данные в нём, и может отдавать их поиску сразу «в чистом виде».

Роза понимает три команды: «проиндексируй вот это», «удали из индекса вот это» и «найди всё вот по этому запросу». Как устроен индекс и как она находит — клиенту знать не нужно, это чёрный ящик. Клиент должен только обеспечить Розе доступ к хранилищу, где она будет держать индекс, и своевременно сообщать об изменениях, чтобы этот индекс был актуален.

Таким образом, поиск — это взаимная работа клиента и Розы. Прикрутить её сложнее, чем использовать движок поиска общего назначения. Зато понимание структуры данных позволяет гибко форматировать результаты и учитывать их природу при ранжировании.

Любой приличной заметке в современном интернете нужна картинка. Поэтому пусть здесь будет скриншот, иллюстрирующий покрытие Розы автоматизированными тестами:

Как прикрутить поисковый движок «Роза»

Теперь к делу.

Хранилище

Хранилище — это объект, через который Роза взаимодействует с базой данных. Клиенту нужно передавать его Розе и для индексации, и для поиска. Поэтому удобно иметь функцию, которая его отдаёт, когда нужно:

function e2_rose_storage () {
  static $pdostorage = null;

  if ($pdostorage === null) {
    $pdo = new \PDO (
      'mysql:'.
      'host='. $_db_server .';'.
      'dbname='. $_db_name. ';'.
      'charset=utf8',
      $_db_user,
      $_db_password,
    );
    $pdo -> setAttribute (\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    $pdostorage = new PdoStorage ($pdo);
  }

  return $pdostorage;
}

Для простоты я убрал некоторые специфичные для Эгеи вещи. Я тут проверяю, нет ли уже и так созданного хранилища; если нет, то создаю; и в любом случае его возвращаю. На место переменных, начинающихся с подчёркивания, надо поставить настоящие реквизиты базы данных.

Индексация

Задача клиента — «рассказать» Розе о данных, чтобы она построила свой индекс. Для этого нужно добавить каждую сущность (в Эгее — заметку) в индекс:

$stemmer = new PorterStemmerRussian ();
$indexer = new Indexer (e2_rose_storage (), $stemmer);
$indexable = new Indexable (
  $_note_id,
  $_note_title,
  $_note_text
);
$indexer -> index ($indexable);

Когда пользователь публикует или изменяет заметку, я вызываю этот код. По идентификатору Роза понимает, нужно ли добавить в индекс новую запись или переиндексировать старую.

Стеммер — это объект, к которому обращается Роза, чтобы сравнивать слова в разных падежах и формах. Его нужно передавать при индексации и при поиске. Он обособлен для того, чтобы можно было сделать свой стеммер для другого языка и подключить, не меняя ничего в индексаторе и поисковике.

Как видите, на индексацию я передаю идентификатор, заголовок и текст заметки, а не адрес её страницы. Когда Роза что-то находит, она мне так же возвращает список идентификаторов найденных сущностей, а не ссылки. Построить страницу выдачи с рабочими ссылками — моя забота (но Роза поможет подсветить результаты поиска в выдаче — об этом ниже).

Можно отдавать и адрес страницы при индексациии, но для Розы это просто текстовое поле, она не пойдёт по этому адресу низачем, просто вернёт его потом вместе с результатами поиска. Я этим не пользуюсь, потому что мне сподручнее получить адрес заметки из неё идентификатора, а не от Розы.

Индексация одной заметки — быстрая операция, поэтому поддержание индекса в актуальном состоянии не составляет проблемы.

Но при обновлении Эгеи со старой версии, мне нужно аккуратно проиндексировать все написанные ранее заметки. В случае с моим блогом это около четырёх тысяч заметок. Поэтому просто запустить их индексацию в цикле нельзя: даже если операция уложится во временной лимит ПХП, всё это время пользователь будет думать, что сайт не отвечает. А если не уложится, то пользователь вообще ничего не увидит. Как обеспечить безболезненную фоновую индексацию — тема отдельной заметки, но важно, что это забота клиента, а не Розы.

Когда пользователь удаляет или отзывает заметку, я делаю примерно так:

$stemmer = new PorterStemmerRussian ();
$indexer = new Indexer (e2_rose_storage (), $stemmer);
return $indexer -> removeById ($_note_id);

Поиск

Чтобы искать, нам понадобится файндер (а ему понадобится стеммер):

$stemmer = new PorterStemmerRussian ();
$finder = new Finder (e2_rose_storage (), $stemmer);
$finder -> setHighlightTemplate (
  '<span class="'.CSS_CLASS_HIGHLIGHT.'">%s</span>'
);

Здесь я также настраиваю шаблон для подсветки найденных слов в результатах поиска.

Теперь можно сделать запрос и достать результаты поиска:

$rose_query = new Query ($query);
$rose_query -> setLimit (SEARCH_LIMIT); 
$resultSet = $finder -> find ($rose_query);

Тут я не могу продолжать как ни в чём не бывало и не сделать ремарку. Объекто-ориентированное программирование делает мне больно. Просто так написать нельзя:

$results = rose_find ($db_link, $query, SEARCH_LIMIT);

С объектами для выражения простых мыслей требуется много строк. Связанные фрагменты кода невозможно разместить в поле зрения, и постоянно приходится скролить туда-сюда, держа вещи в памяти. Я понимаю, что объекты имеют преимущества. Но когда-нибудь программисты поймут, что так писать — тоже не дело, и придумают способ вернуть коду выразительность, не потеряв той пользы, которую им приносят объекты.

Итак, после того, как мы получили $resultSet, мы уже можем выводить результаты. Но это тот момент, когда можно попросить Розу подкрутить релевантность, что я и делаю:

foreach ($resultSet -> getFoundExternalIds () as $external_id) {
  $note_id = e2_note_id_from_rose_id ($external_id);
  $note_rec = e2_note_by_id ($note_id);
  if ($note_rec['IsFavourite']) {
    $resultSet->setRelevanceRatio ($external_id, 2);
  }
}

Так я поднимаю избранные заметки повыше.

Тут же Розу можно попросить сделать «сниппеты» — так называются фрагменты, выводящиеся в результатах поиска. Роза сама находит предложения с найденными словами, подсвечивает в них эти слова и собирает маленький кусок текста, достаточный для узнавания заметки в выдаче. Это делается так:

$snippetBuilder = new SnippetBuilder ($stemmer);
$snippetBuilder -> setSnippetLineSeparator(' · ');
$snippetBuilder -> attachSnippets ($resultSet, function (array $external_ids) {
  $result = array ();
  foreach ($external_ids as $external_id) {
    $note_id = e2_note_id_from_rose_id ($external_id);
    $note_rec = e2_note_by_id ($note_id);
    $note_pack = e2_package_note ($note_rec);
    $result[$external_id] = $note_pack['text'];
  }
  return $result;
});

Чтобы собрать сниппеты, Розе нужны полные тексты заметок — сама она их не хранит, а о структуре моей базы данных ничего не знает. Поэтому в метод attachSnippets мы тут передаём колбек, который Роза вызывает, чтобы их получить. Он, как видите, вытаскивает заметки из базы.

После этого можно собрать результаты:

foreach ($resultSet -> getItems () as $external_id => $item) {
  $note_id = e2_note_id_from_rose_id ($external_id);
  $note_rec = e2_note_by_id ($note_id);
  $note_pack = e2_package_note ($note_rec);
  $note_pack['title' ] = $item -> getHighlightedTitle ($stemmer);
  $note_pack['text' ] = '<p>'. $item -> getSnippet () .'</p>';
  $note_packs_found[] = $note_packs;
}

Я подсвечиваю найденные слова в заголовке и показываю сниппет вместо текста заметки.

Код выдачи результатов поиска неэлегантен — хотя бы потому, что я трижды прохожусь циклом по этим результатам, многократно вызываю одни и те же функции с одними и теми же параметрами. Никаких измеримых проблем эта неэлегантность не создаёт, а её исправление потребовало бы изменения АПИ Розы. Поэтому пока пусть будет так.

В этой заметке я упускаю часть слишком специфичных для Эгеи решений, фрагменты кода местами несколько упрощены. Из примеров полностью выпилен весь код, отвечающий за «смешение» результатов поиска Розы и стандартными средствами БД (я это делаю, потому что у Розы более высокие системные требования, чем у Эгеи, и на некоторых хостингах она может просто не завестись).

Если захотите использовать Розу в своих продуктах, своими мыслями делитесь с Романом, а не со мной.

РСС, или Как подписаться на мой блог (2017)

Заметка устарела. Читайте исправленную и дополненную версию

Если вы ищете прямой адрес РСС-потока моего блога, то вот он:
http://ilyabirman.ru/meanwhile/rss/

У меня появляется новая аудитория, поэтому время от времени я повторяю эту заметку.

Многие не знают, что такое РСС (RSS) и как им пользоваться. К сожалению, РСС — немного гиковская технология, не вполне доступная нормальным людям. Но если её освоить, то становится непонятно, как ты до этого жил. В этом смысле РСС похож на торенты: там тоже есть некоторый порог входа, но польза оправдывает затраты сил на его преодоление.

Что такое РСС? Это механизм подписки на блоги и другие сайты с регулярными обновлениями. Вместо того, чтобы самому ходить по разным сайтам и смотреть, не появилось ли там чего новенького, разумно поручить эту тупую работу машине. Пусть она ходит по всем сайтам и аккуратно собирает всё новое в одном месте. Тогда получается, что новости просто приходят к нам, а не мы охотимся за ними.

Чтобы использовать РСС, нужна РСС-читалка. Это программа, которая знает, какие сайты вы любите, и следит за обновлениями на них. Когда сайты обновляются, свежие публикации автоматически скачиваются, и вы получаете свежий выпуск собственного «журнала», где собрано всё новое с любимых сайтов.

Вот как сейчас выглядит моя читалка:

РСС, или как подписаться на мой блог

Здесь все непрочитанные посты со всех блогов, за которыми я слежу. Мне не приходится ходить на их сайты, открывать сто букмарок в браузере — всё в одном месте. Не нужно ждать, пока откроется страница — всё уже скачано. Я не боюсь пропустить что-то важное — всё сохранено. Что-то я прочту сразу, что-то в выходные, что-то — когда буду в следующий раз в самолёте без интернета. А что-то не стану читать, просто ткну в него, чтобы оно перестало выглядеть как непрочтённое.

Как подписаться на кого-нибудь по РСС? Многие РСС-читалки позволяют подписать напрямую, то есть они сами будут скачивать всё новое. Но это непрактично: если вы подпишетесь независимо на компьютере, айпаде и айфоне, то три читалки не будут знать, что́ вы уже прочитали, и все заметки вы будете получать по три раза. Намного лучше воспользоваться сервисом-агрегатором, который будет ходить и собирать новости, а читалки с ваших устройств будут просто синхронизироваться с этим сервисом.

Заведите себе акаунт, например, в Фидли. В меню слева сверху есть пункт Add content — введите туда адрес блога, например, моего. Всё, теперь вы подписаны, новые посты будут приходить к вам туда. Подпишитесь так же на все любимые блоги, и теперь сможете читать всё в одном месте, например:

Есть читалки, которые не могут подхватить РСС просто с сайта, им нужно указать прямой путь к РСС-потоку. У меня такой:

У самого у меня около двух сотен разных источников в читалке. Среди них не только блоги, но и, например, результаты поиска в Яндексе по моей фамилии — так я всегда автоматически вижу, когда кто-то где-то ссылается на меня. Если вы сами читаете этот пост через РСС и недоумеваете, зачем я всё это объясняю — привет!

Конечно же, любой блог на Эгее поддерживает РСС, для этого ничего не нужно настраивать.

Читать РСС прямо в интерфейсе Фидли — это такое же насилие над собой, как использование Гмейла через веб-интерфейс. Кому-то нравится такая фигня, но мне не понять.

Для чтения РСС я предпочитаю Риидер (он платный, есть миллион других, в том числе бесплатных читалок). Риидер бывает для Айфона, Айпада и Мака — везде сказочно красивый (сверху это он на скриншоте). После того, как вы его установите, его надо будет привязать к вашему акаунту в Фидли, и всё, он будет показывать всё, на что вы подписаны. То есть это просто клиент для агрегаторов. Кстати, кроме Фидли есть ещё куча агрегаторов, просто я предпочитаю Фидли: он бесплатный и работает без нареканий. Риидер поддерживает множество разных агрегаторов, комбинировать можно как угодно. Вы можете даже использовать разные клиенты на разных устройствах — всё со всем без проблем синхронизируется.

Ещё часто спрашивают, зачем РСС, если есть соцсети. Вроде как, все туда кидают ссылки на свои посты. Тут дело личное, конечно, но я не понимаю, как можно даже сравнивать по удобству одно с другим.

Во-первых, за соцсетями, чтобы ничего не пропустить, надо внимательно следить, а это съедает целую кучу времени. И Твиттер, и Телеграм я читаю по диагонали и по умолчанию считаю, что там ничего особо ценного нет. А Фейсбук вообще ставит посты в одному ему известном порядке. Читалка ничего не пропустит.

Во-вторых, в большинство соцсетей попадают только ссылки. Чтобы посмотреть хотя бы в общих чертах, о чём статья, насколько она длинная, нужно переходить по ссылке и ждать, пока она откроется в браузере. Это трение. В читалке же к тому моменту, как я туда смотрю, всё уже скачано и показывается мгновенно.

В-третьих, как я уже говорил, я хочу иметь возможность читать что-то, когда это удобно мне, а не когда этим поделился автор. В Твиттере, например, приходится помечать интересное сердечком, потом возвращаться к нему — это всё лишняя работа. В читалке меня просто всё спокойно дожидается, мне не нужно для этого ничего делать.

Теперь вместо браузера и твиттера для чтения блогов вы можете использовать клёвый, удобный, придуманный специально для этого инструмент — РСС-читалку. На здоровье!

Книга «Пользовательский интерфейс»: готов второй раздел

На прошедшей неделе мы открыли остаток второго раздела электронного учебника «Пользовательский интерфейс» — главы «Обратная связь», «Листание и прокрутка». Теперь первые два раздела доступны целиком.

Книга «Пользовательский интерфейс»: готов второй раздел

Из письма издательства: «Бывало ли у вас такое, что вы приходите в гости, звоните в звонок, но ничего не слышите? Вы звоните ещё раз, но снова тишина. Начинаете стучать в дверь, и тут открывает возмущённый хозяин: „Чё трезвонишь?!“ Проблема в отсутствии обратной связи. Интерфейс дверного звонка прост, а в более сложных интерфейсах вроде управления автомобилем, самолётом или графическим редактором её качество ещё важнее. Из главы вы поймёте, почему руль на автомобиле удобнее, чем стрелки на клавиатуре в автосимуляторах.»

Люблю приводить старые примеры:

И, наоборот, новые:

И снова старые:

И снова новые:

Хотя вроде мода на такую фигню быстро прошла.

В конце опубликованных глав — тест по материалу из них.

У тех, кто оформил предзаказ, время подписки пока не тикает, а первый и теперь часть второго раздела уже есть. Если это вы, бегите читать!

Жуэль 2.3

Жуэль 2.2

Жуэль — нормальный аудиоплеер для веба. Его развитием занимается Женя Лазарев.

У нас вышла версия 2.3. Жуэль теперь использует Хоулер (встроен, а не идёт отдельным файлом) вместо Джейплеера. Благодаря этому изменению, трафик расходуется умнее, прелоудер работает точнее и ушло много мелких ошибок. Функция data-repeat стала работать у отдельных треков. Если файл недоступен, Жуэль показывает это, а не «висит». Вёрстка лучше смотрится в разных браузерах.

Пример песенки:

Документацию см. на Гитхабе.

Что почитать на выходных — 151

Вот:

  1. A design lesson 3.5 billion years in the making. Костя Горский.
  2. Пара особенностей дизайна схемы метро Екатеринбурга. Паша Омелёхин написал про варежку, глаз, выходы и центр.
  3. Просто ответьте на вопросы. Люда Сарычева объясняет, как написать нормальный текст.
  4. Как работает метафора. Саша Волкова.
  5. Нельзя запрещать открывать ссылку в новой вкладке. Серж Николаев.

Хотите стать спонсором рубрики? Пишите: ilyabirman@ilyabirman.ru

Интерфейс Секьюриджа 2.0

Я сделал интерфейс для программы пультовой охраны «Секьюридж». Это программа, в которой операторы пульта смотрят, не пробрался ли кто-то в вашу квартиру, и, если что, отправляют группу быстрого реагирования. Выглядит так (теперь):

Любимый момент был, когда я сидел на пульте и молча наблюдал за работой операторов (потому что пустили только молча), а потом девушки такие: «вы спрашивайте, если что», ну и я стал спрашивать. Там-то как раз и выяснил, почему клавиатура не очень используется. Возможно, если бы не это, я бы не заморочился так с Фиттс-оптимизацией. Уж точно левая панель поиска по номеру объекта бы не была такой.

А ещё когда спрашивал у них, что им больше всего мешает в существующей версии, все говорили: «да вроде ничего, всё нормально». И только на наводящие вопросы и примеры возможных улучшений говорили: «ух ты, а что, так можно»?

Страшно интересный проект. Только на примерах из этого проекта можно учебник по интерфейсу написать. Читайте подробный рассказ.

Вёрстка неформатированного текста

Первый навык вёрстки, который должен приобрести дизайнер — использование неформатированного текста. Например, в электронной почте.

Когда в твоём распоряжении разные шрифты, кегли, начертания и цвета, можно сделать текст намного выразительнее, чем без этого. Но намного проще превратить его в беспорядочную кашу.

В то же время, самый важный инструмент вёрстки — это отступы. А в неформатированном тексте это просто единственный способ показать структуру. Причём отступы ещё и обязательно кратны высоте строки. Так что если дизайнер не в состоянии аккуратно написать электронное письмо, то уж тем более нельзя ожидать от него аккуратного текста с бо́льшим числом выразительных средств.

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

Аудио по четвергам: «Жизнь в одиночку»

И ещё один случай, когда ремикс сделал песню:

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

Как взять иконку любого приложения на Маке

Чтобы нарисовать картинку ко вчерашнему совету про синтаксис, мне понадобилась иконка маковского почтового приложения. Где я её взял?

Комманд-клик по любому приложению в Доке открывает Файндер с курсором на нём. Комманд-кликаю в Мейл и жму ⌘I, чтобы открыть его панель Info:

Маленькая иконка сверху — живой объект. Если в неё ткнуть, она выделяется:

Осталось нажать ⌘C и потом ⌘V в Фотошопе — вставится полноразмерная иконка 1024×1024 на прозрачном фоне:

Если вы про такое не знали, то вы, вероятно, тем более не знали, что так же можно поменять плохую иконку любого приложения (например, Сублайм-текста) на хорошую. Для этого нужно так же выделить иконку в панели Info, но вместо ⌘C нажать ⌘V (предварительно скопировав хорошую иконку в буфер обмена, конечно).

Девальвация техно

Бывает, прочитаешь, что кто-то играет техно, обрадуешься, включаешь, а там текхаус. Причём это ещё года три назад так было, а сейчас-то и вовсе прогрессив-хаус за техно выдают. Я с удовольствием слушаю и тек, и прогрессив, но зачем же меня обманывать? Нет ничего стыдного в том, чтобы писать или играть тек. Просто не называйте его словом «техно»!

Техно:

Не техно:

Слишком сильно подчёркивается разница между сильной и слабой долями, звучит «медленнее», чем техно.

Техно:

Не техно:

Слишком «сухо».

Я выделяю несколько условных признаков техно. Не обязательно, чтобы они присутствовали все.

Важнейший признак — наличие механизма. Звучит так, как звучит, не потому, что кто-то именно так расставил звуки, а потому что иначе и не могло. Вращаются какие-то штуки и вот так само собой получается.

При этом техно можно быть очень-очень мягким. Диптехно:

Главное — механизм. Поэтому в техно спокойно живут полиритмические конструкции (см. The Four Patterns, 2016):

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

Нота играет вроде бы независимо от тактовой сетки, но в конце квадрата всё равно сворачивается так, чтобы новый квадрат начался с того же места.

Техно может наплевать на сетку:

Почему хеты вступили именно в 0:11? Нипочему. Это техно, детка.

Конечно, восприятие зависит от подачи. Я играл «Метеоры» в техносете, и ничего. Некоторые тековые треки можно сыграть как техно и наоборот, у этих направлений много общего.

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

Техно стремится заполнить пространство. Если вообще все звуки отрывисто-острые, это не похоже на техно, хотя и есть явный механизм:

Это не значит, что это плохо. Это великий трек. Лучшее, что я слышал в жанре текхауса. Просто это не техно. От силы это минимал.

Здесь сильная и слабая доли отличаются, но это похоже на техно:

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

Техно (снова полиритмия):

Не техно:

Слишком длинная и слишком смыслообразующая мелодия на первом плане, какой уж тут механизм? Плюс сухой электрический звук. Это тек.

Техно:

Не техно:

Звуки слишком разные, не соединяются в механизм.

Главное в техно — ощущение полностью безразличного к слушателю движения вперёд. Последнему треку от меня чё-то надо, поэтому нет.

Это я к чему всё. В эту пятницу, 21 апреля играю техно в Студии Лебедева. Будет тру. Чтобы послушать мой сет, надо работать в Студии Лебедева.

Ранее Ctrl + ↓