Hibernate Search Pagination Странное поведение - PullRequest
0 голосов
/ 18 декабря 2018

Привет, мы используем hibernate-search вместе сasticsearch.

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

org.hibernate.Query hibQuery =
            fullTextSession.createFullTextQuery(query, 
Person.class).setFirstResult(0).setMaxResults(10);

return hibQuery.list();

Если мы опускаем setFirstResult (0) .setMaxResults (10) мы получаем 700 результатов, но с двумя установленными параметрами получаем 0 результатов.

Дальнейшие исследования показывают, что проблема в этом разделе кода в QueryLoader в hibernate-search

objectInitializer.initializeObjects(
            entityInfos,
            idToObjectMap,
            new ObjectInitializationContext( criteria, entityType, extendedIntegrator, timeoutManager, session )
    );

ArrayList<Object> result = new ArrayList<>( idToObjectMap.size() );
    for ( Object o : idToObjectMap.values() ) {
        if ( o != ObjectInitializer.ENTITY_NOT_YET_INITIALIZED ) {
            result.add( o );
        }
    }
    return result;

в приведенном выше коде строка

 if ( o != ObjectInitializer.ENTITY_NOT_YET_INITIALIZED )

возвращает false для всех записей idToObjectMap

Дальнейшие исследования показывают, что hibernate создает запрос и sql выглядит корректно, но вдля вызываемого объекта QueryParanters установлено значение false, и запрос никогда не выполняется.

Соответствующие библиотеки

compile "org.hibernate:hibernate-core:5.9.2.Final"
compile "org.hibernate:hibernate-search-orm:5.9.2.Final"
compile "org.hibernate:hibernate-search-elasticsearch:5.9.2.Final"

Любая помощь с объяснением, почему это происходит, и как правильно реализовать разбиение на страницы, будет принята с благодарностью.

1 Ответ

0 голосов
/ 19 декабря 2018

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

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

Если вы уверены, что сущности все еще существуют в базе данных, возможно, существует проблема с отображением идентификатораили с выбранной вами конфигурацией запроса.Пожалуйста, покажите нам код класса Person и укажите значение, которое вы задали для свойств hibernate.search.query.object_lookup_method и hibernate.search.query.database_retrieval_method, если вы не используете значения по умолчанию.

Решение в тестах

Если это проблема при тестировании, вы можете установить hibernate.search.default.elasticsearch.refresh_after_write на true. Вы не должны устанавливать это в производстве , поскольку это резко снизит производительность индексации.

Решение в производстве

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

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

Этот ключ будет использоваться для игнорирования предыдущих страниц: клиент не отправит номер страницы на сервер, он будетотправьте последнее значение для «строго монотонного» ключа, и вы просто добавите в свой запрос такой предикат: queryBuilder.range().onField("myKey").above(<the last value for the key in the previous page>).createQuery().

Затем вместо непосредственного возврата результатов вашего запроса вы вместо этого выполнитезапрос несколько раз, накапливая результаты в списке, пока он не достигнет соответствующего размера страницы (или пока getResultSize не вернет 0).

РЕДАКТИРОВАТЬ: другое решение, возможно, более простое, но это только уменьшит вероятностьэту проблему, не удаляйте ее полностью.

Вы можете убедиться, что Elasticsearch обновляет свои индексы чаще, задав для index.refresh_interval значение, которое короче значения по умолчанию (1s) для всех индексов.Обратите внимание, что это может очень плохо повлиять на производительность кластера Elasticsearch в зависимости от того, как часто вы пишете в кластер.

Чтобы применить настройку ко всем индексам, самое простое решение - создать шаблоны индексов до того, как Hibernate Search создаст индексы.

...