Lucene и Criteria Api объединяются на 2 разных предметах - PullRequest
0 голосов
/ 12 июня 2018

У меня есть отношение OneToOne между 2 объектами: Ad (содержит address_id как FK) и Address.

Для адреса я использую запрос fullTextSearch (lucene) и для запроса критериев добавления.

Я хочу найти ключевое слово в некоторых полях адреса, получить объявление, связанное с полученными записями (Адреса) из поиска в lucene, а затем использовать фильтры из запроса Criteria для уменьшения количества результатов.

Lucene:

FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);

    QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Address.class).get();

    Query luceneQuery = queryBuilder
            .keyword()
            .wildcard()
            .onFields(Address_.__REGION, Address_.__TOWN, Address_.__STREET)
            .matching(input.toLowerCase() + "*")
            .createQuery();

    return fullTextEntityManager.createFullTextQuery(luceneQuery, Address.class);

Criteria API:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Ad> criteria = criteriaBuilder.createQuery(Ad.class);

    Root<Ad> AdRoot = criteria.from(Ad.class);

    criteria.select(AdRoot);

    if (AddressID != null) {
        whereConditions = (whereConditions == null) ? criteriaBuilder.equal(AdRoot.get(Ad_.address), AddressID)
                : criteriaBuilder.and(whereConditions, criteriaBuilder.equal(AdRoot.get(Ad_.address), AddressID));
    }

    if (PriceFrom != null && PriceTo != null)
        whereConditions = (whereConditions == null) ? criteriaBuilder.between(AdRoot.get(Ad_._Price), PriceFrom, PriceTo)
                : criteriaBuilder.and(whereConditions, criteriaBuilder.between(AdRoot.get(Ad_._Price), PriceFrom, PriceTo));

    if (SizeFrom != null && SizeTo != null)
        whereConditions = (whereConditions == null) ? criteriaBuilder.between(AdRoot.get(Ad_._Size), SizeFrom, SizeTo)
                : criteriaBuilder.and(whereConditions, criteriaBuilder.between(AdRoot.get(Ad_._Size), SizeFrom, SizeTo));

    if (whereConditions==null){
        return null;
    }

    criteria.where(whereConditions);

    return entityManager.createQuery(criteria).setMaxResults(MAX_NR_SESULTS_PER_PAGE).getResultList();

1 Ответ

0 голосов
/ 13 июня 2018

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

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

Действительно, если вам нужно применить предикаты к двум различным объектам, самый простой путь - использовать аннотацию @IndexedEmbedded поиска Hibernate:

  • добавьте @Indexed к вашей Ad сущности и удалите ее из вашей Address сущности (хотя оставьте @Field s на вашей Address сущности)
  • добавьте @IndexedEmbedded в поле address вашей Ad сущности
    • , если ваша сущность Address может со временем измениться, вы также должны добавить обратную сторону в ассоциацию (поле Ad ad в вашей сущности Address и аннотируйте его @ContainedIn, чтобы при обновлении адресов обновлялся индекс для сущностей Ad.
  • add @Field аннотации к полям price и size вашего Ad entity
  • полностью создайте свой запрос с помощью Hibernate Search, ориентируясь на сущность Ad.
    • скопируйте код, который вы имели в своем существующем полнотекстовом запросе, за исключением того, что вы получите конструктор запросов для сущности Ad вместо сущности Address, и вы нацелитесь на address.region, *Поля 1044 *, address.street.
    • создают дополнительные запросы lucene для полей price и size, используя запросы диапазона
    • , объединяя все запросы lucene водин с использованием логического перехода

У вас не будет полной мощности предложений и объединений SQL WHERE, но во многих случаях этодостаточно, и в ваших случаях этого кажется достаточно.

Почему сложно объединить результаты из разных запросов при выполнении подкачки

Если вам нужны подробности о том, почему это сложно ... Внимание:это немного сложно, поэтому я не уверен, что могу объяснить это ясно.

Пейджинг обычно выполняется механизмом запросов вне вашего приложения и даже вне Hibernate.Но вы будете объединять результаты двух запросов после того, как эти запросы произошли, в вашем приложении, поэтому вы применяете другой уровень фильтрации после подкачки.Это означает, что первые результаты, которые были пропущены обработчиком запросов, могут вообще не совпадать с результатами.

Так, если вы пропустили, например, 100 результатов, возможно, только 50 из этих пропущенных результатов были фактическими результатами в вашем запросе.Таким образом, вы на самом деле получили результаты, начиная с 51-го результата, тогда как вы запросили результаты, начиная с 101-го ...

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

По сути, вместо того, чтобы запрашивать «первые 10 результатов», затем «результаты с 11 по 20» и т. Д., Например, вы будете сортировать по дате создания, запрашивать «первые 10 результатов», а затем«10 первых результатов с датой создания выше foo» (foo является датой создания 10-го результата, полученного на предыдущей странице).

Эта стратегия имеет несколько недостатков, в частности:

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

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

...