Статьи

SearcherLifetimeManager предотвращает неправильный поиск пользователей


Раньше поисковые индексы, как правило, были очень статичными: вы строили их один раз, в конце вызывали оптимизацию и отправляли их, а не очень часто меняли.

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

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

Но есть серьезная, но часто упускаемая из виду проблема с этим подходом. Чтобы увидеть это, вы должны поставить себя на место пользователя. Представьте, что Алиса заходит на ваш сайт, выполняет поиск и просматривает результаты поиска. Не удовлетворенный, через несколько секунд она решает уточнить этот первый поиск. Возможно, она углубляется в один из приятных аспектов, которые вы представили, или, может быть, она щелкает на следующей странице или выбирает другой критерий сортировки (подойдет любое последующее действие). Таким образом, новый запрос поиска отправляется обратно на ваш сервер, включая первый поиск плюс запрошенное изменение (переход на следующую страницу, поле сортировки изменений и т. Д.).

Как вы обрабатываете этот дополнительный поисковый запрос? Просто возьмите самый последний и лучший поисковик из вашего
SearcherManager или
NRTManagerи искать, верно?

Неправильно!

Если вы сделаете это, вы рискуете потерять опыт поиска для Алисы, потому что новый поисковик может отличаться от исходного поисковика, использованного для первого поискового запроса Алисы. Различия могут быть существенными, если вы только что открыли новый поисковик после обновления пачки документов. Это означает, что результаты последующего поиска Алисы могли сместиться: счетчики фасетов теперь отключены, попадания отсортированы по-разному, поэтому некоторые совпадения могут дублироваться на второй странице или могут быть потеряны (если они были перемещены со страницы 2 на страницу 1) и т. д. Если вы используете новый (будет в Lucene 3.5.0)
API
searchAfter для эффективной подкачки страниц, риск еще больше!

И наоборот, частое повторное открытие поисковика, которое, как вы думали, обеспечивает такой большой пользовательский опыт, делая все результаты поиска такими свежими, на самом деле может иметь только обратный эффект. Каждое повторное открытие может нарушить все текущие поиски в вашем приложении; чем активнее ваш сайт, тем больше поисков вы можете прервать!

Умышленно нарушать поисковый опыт пользователей смертельно опасно: они (правильно) решат, что ваш поиск ошибочен, разрушит их доверие, а затем перенесут свой бизнес в конкурентную борьбу.

Оказывается, это легко исправить! Вместо того, чтобы извлекать последний поисковик для каждого входящего поискового запроса, вы должны попытаться извлечь тот же поисковик, который использовался для начального поискового запроса в сеансе. Таким образом, все последующие поиски видят точно такой же индекс.

К счастью, в Lucene 3.5.0 появился новый класс, который упрощает это: SearcherLifetimeManager. Класс не зависит от того, как вы получаете новые поисковые устройства (например, SearcherManager, NRTManager или ваш собственный пользовательский источник), используемые для начального поиска. Как и другие классы управления Lucene
, SearcherLifetimeManager очень прост в использовании. Создайте менеджер один раз, сразу:

  SearcherLifetimeManager mgr = new SearcherLifetimeManager();

Затем, когда поступает поисковый запрос, если это начальный (не последующий) поиск, получите самый последний поисковик
обычным способом , но затем запишите этот поисковик:

  long token = mgr.record(searcher);

Возвращенный токен однозначно идентифицирует конкретного искателя; Вы должны сохранить его где-нибудь в результатах поиска пользователя, например, поместив его в скрытое поле формы HTML.

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

  // If possible, obtain same searcher version as last
  // search:
  IndexSearcher searcher = mgr.acquire(token);
  if (searcher != null) {
    // Searcher is still here
    try {
      // do searching...
    } finally {
      mgr.release(searcher);
      // Do not use searcher after this!
      searcher = null;
    }
  } else {
    // Searcher was pruned -- notify user session timed
    // out
  }

Пока оригинальный поисковик все еще доступен, менеджер вернет его вам; обязательно освободите этот поисковик (в идеале в пункте finally).

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

Вы должны изящно обработать этот случай, например, уведомив Алису, что время поиска истекло, и попросив ее повторно отправить исходный запрос (который затем получит самый последний и лучший поисковик). К счастью, вы можете уменьшить частоту этого, контролируя, насколько агрессивно вы удаляете старых искателей:

  mgr.prune(new PruneByAge(600.0));

Это удалит всех искателей старше 10 минут (вы также можете реализовать собственную стратегию обрезки). Вы должны вызывать его из отдельного выделенного потока (не потока поисковика), в идеале из того же потока, который периодически индексирует изменения и открывает новых поисковиков.

Сохранение большого количества поисковиков будет обязательно связывать ресурсы (открытые файловые дескрипторы, ОЗУ, индексные файлы на диске, которые иначе бы удалил IndexWriter). Тем не менее, поскольку вновь открытые поисковики совместно используют суб-читателей, потребление ресурсов, как правило, будет хорошо сдерживаться пропорционально тому, сколько изменений индекса произошло между каждым повторным открытием. Просто убедитесь, что вы используете NRTCachingDirectory, чтобы убедиться, что вы не столкнетесь с ограничениями открытых дескрипторов файлов в вашей операционной системе (это также дает хорошее ускорение при повторном открытии цикла).

Не подрывайте доверие своих пользователей, намеренно ломая их поиски!

LUCENE-3486 имеет подробности.

Источник: http://blog.mikemccandless.com/2011/11/searcherlifetimemanager-prevents-broken.html