Как сортировать результаты поиска «Hibernate with Lucene», основываясь на «целых словах», а не на содержании - PullRequest
0 голосов
/ 16 января 2020

Я использую Hibernate Search для полнотекстового поиска товаров / товаров в нашем приложении магазина. Вот как выглядит мой класс Item:

@Entity
@Table(name = "items", indexes = {
    @Index(name = "idx_item_uuid", columnList = "uuid", unique = true),
    @Index(name = "idx_item_gtin", columnList = "gtin", unique = true),
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
@ToString(exclude = {"storeItems"})
@Indexed
@AnalyzerDef(name = "ngram",
    tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
    filters = {
        @TokenFilterDef(factory = StandardFilterFactory.class),
        @TokenFilterDef(factory = LowerCaseFilterFactory.class),
        @TokenFilterDef(factory = StopFilterFactory.class),
        @TokenFilterDef(factory = NGramFilterFactory.class,
            params = {
                @Parameter(name = "minGramSize", value = "1"),
                @Parameter(name = "maxGramSize", value = "3")})
    }
)
public class Item extends BaseModel {

  @Column(nullable = false)
  @Field(analyzer = @Analyzer(definition = "ngram"))
  private String name;

  @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "item", fetch = FetchType.EAGER)
  @Fetch(FetchMode.SELECT)
  private List<Image> images;

  @OneToMany(mappedBy = "item", cascade = CascadeType.REFRESH)
  @Fetch(FetchMode.SELECT)
  @JsonIgnore
  @IndexedEmbedded(includePaths = {"store.uuid"})
  private Set<StoreItem> storeItems;

  @Enumerated(EnumType.STRING)
  private QuantityType quantityType;

  @Column(nullable = false, length = 14)
  private String gtin;

  private String articleSize;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "brand_id", foreignKey = @ForeignKey(name = "fk_brands_items"))
  private Brand brand;

  private String supplierName;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "category_id", foreignKey = @ForeignKey(name = "fk_categories_items"))
  @IndexedEmbedded(includePaths = {"uuid"})
  private Category category;

  private String taxType;

  private Double taxRate;

  @Lob
  private String marketingMessage;

  private boolean seasonal;

  private String seasonCode;

  @Lob
  private String nutritionalInformation;

  @Lob
  private String ingredients;

  private Double depth;

  private String depthUnit;

  private Double height;

  private String heightUnit;

  private Double width;

  private String widthUnit;

  private Double netContent;

  private String netContentUnit;

  private Double grossWeight;

  private String grossWeightUnit;

  private Double maxStorageTemp;

  private Double minStorageTemp;

  private Double maxTransportTemp;

  private Double minTransportTemp;

  private boolean organic;

  private String origin;

}

И вот как мой пользовательский репозиторий ищет элементы в указанном c магазине:

  @Override
  public List<Item> findItemBySearchStrAndStoreUuid(final String searchStr, final String storeUuid) {
    final EntityManager entityManager = entityManagerFactory.createEntityManager();

    final FullTextEntityManager manager = Search.getFullTextEntityManager(entityManager);
    entityManager.getTransaction().begin();

    final QueryBuilder qb = manager.getSearchFactory()
        .buildQueryBuilder().forEntity(Item.class).get();

    final Query query = qb.bool()
        .must(qb.keyword().onField("name").matching(searchStr).createQuery())
        .must(qb.keyword().onField("storeItems.store.uuid").matching(storeUuid).createQuery())
        .createQuery();

    return executeQuery(entityManager, manager, query);
  }

У нас около 13k элементы в базе данных и в основном имеют имена Swedi sh, поэтому, когда покупатель ищет молоко в Swedi sh "mjölk", должны появиться элементы, связанные с молоком, они делают это, но сортировка идет не так, как мы хотим, например.

Ожидаемые результаты:

  1. mjölk
  2. mjölk chocolate
  3. Kokosmjölk

Фактические результаты:

  1. Kokosmjölk
  2. mjölk chocolate
  3. mjölk

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

Так что, пожалуйста, объясните мне, как мне улучшить мой анализатор / запрос, чтобы добиться такой сортировки, мне нужно давать результаты даже с одним символом, поиск также должен обрабатывать некоторые опечатки, поэтому я использовал фильтр Ngram с вышеуказанными настройками .

Кроме того, я попытался использовать SwedishLightStemFilterFactory, который немного помог, но затем элементы перестали отображаться, если кто-то не напечатал 'mjölk' полностью и правильно.

Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 16 января 2020

Вам необходимо объявить отдельное поле в том же свойстве, которое используется исключительно для сортировки, и назначить ему нормализатор вместо анализатора.

См. https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#section -нормализаторы

0 голосов
/ 16 января 2020

Я бы рассмотрел 2 вещи:

  • ASCIIFoldingFilterFactory: замена акцентированных символов на простые
  • Отдельный анализатор для сортировки, где значение не маркируется и только в нижнем регистре

Для сортировки в Hibernate обычно используется другая стратегия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...