HibernateSearch etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
HibernateSearch etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

7 Haziran 2022 Salı

HibernateSearch @NaturalId Anotasyonu

Giriş
Şu satırı dahil ederiz.
import org.hibernate.annotations.NaturalId;
Örnek
Şöyle yaparız
@Indexed
@Entity
@Table
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class Plant {
  
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @FullTextField()
  @NaturalId
  private String name;

  @FullTextField
  @NaturalId
  private String scientificName;

  @FullTextField
  private String family;

  private Instant createdAt ;
}


28 Eylül 2021 Salı

HibernateSearch MassIndexer Arayüzü

Giriş
Şu satırı dahil ederiz
import org.hibernate.search.MassIndexer;
massIndexer metodu - Class
Hangi sınıfların indeksleneceğini belirtmek için kullanılır

Örnek
Şöyle yaparız
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.massindexing.MassIndexer;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;

@Transactional
@Component
public class Indexer {

  private EntityManager entityManager;

  private static final int THREAD_NUMBER = 4;

  public Indexer(EntityManager entityManager) {
    this.entityManager = entityManager;
  }

  public void indexPersistedData(String indexClassName) throws IndexException {

    try {
      SearchSession searchSession = Search.session(entityManager);

      Class<?> classToIndex = Class.forName(indexClassName);
      MassIndexer indexer = searchSession
        .massIndexer(classToIndex)
        .threadsToLoadObjects(THREAD_NUMBER);

        indexer.startAndWait();
    } catch (Exception e) {
      ...
  }
}
Şöyle yaparız
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public ApplicationRunner buildIndex(Indexer indexer) throws Exception {
        return (ApplicationArguments args) -> {
            indexer.indexPersistedData("com.mozen.springboothibernatesearch.model.Plant");
        };
    }
}
startAndWait metodu
Burada massIndexer() metoduna sınıf ismi geçilmiyor.

Örnek
Şöyle yaparız
@Configuration
public class HibernateSearchIndexBuild implements ApplicationListener<ApplicationReadyEvent>{

  @Autowired
  private EntityManager entityManager;

  @Override
  @Transactional
  public void onApplicationEvent(ApplicationReadyEvent event) {
    SearchSession searchSession = Search.session(entityManager);
    MassIndexer indexer = searchSession.massIndexer()
.idFetchSize(150)
.batchSizeToLoadObjects(25).threadsToLoadObjects(12);
    try {
      indexer.startAndWait();
    } catch (InterruptedException e) {
      ...
    }
  }
}

23 Eylül 2021 Perşembe

HibernateSearch Kullanımı

Maven
Açıklaması şöyle
Hibernate Search provides you with both Lucene and ElasticSearch implementations that are highly optimized for full-text search.
Örnek - Lucene
Şöyle yaparız
<dependency>
<groupId>org.Hibernate.search</groupId> <artifactId>Hibernate-search-mapper-orm</artifactId> <version>6.0.2.Final</version> </dependency> <dependency> <groupId>org.Hibernate.search</groupId> <artifactId>Hibernate-search-backend-lucene</artifactId> <version>6.0.2.Final</version> </dependency>
SpringBoot İle Kullanım
Spring için şu kütüphaneyi dahil ederiz
spring-boot-hibernate-search
Index dizinini belirtmek için şöyle yaparız
# HIBERNATE SEARCH
spring.jpa.properties.hibernate.search.backend.directory.root=/home/indexes/
yaml ile şöyle yaparız
server:
    port: 9000

spring:
    datasource:
        url: jdbc:h2:mem:mydb
        username: mozen
        password: password
    jpa:
        open-in-view: false
        properties:
            hibernate:
                search:
                    backend:
                        type: lucene
                        directory.root: ./data/index
Data Model
Entity üzerinde
@Indexed - Sınıfın indeksleneceğini belirtir
@FullTextField - Metin alanlarda arama içindir. Diğer tipte alanlar için başka anotasyonlar var
anotasyonları kullanılır

MassIndexer ile indeksleme başlatılır
SearchSession ile arama yapılır

Repository
Spring için şöyle bir kod ekleriz. Burada @NoRepositoryBean kullanılıyor, böylece bu sınıf repository olarak algılanmaz.
@NoRepositoryBean
public interface SearchRepository<T, ID extends Serializable> 
  extends JpaRepository<T, ID> {

  List<T> searchBy(String text, int limit, String... fields);
}
searchBy metodunun gerçekleştirimini ekleriz.  Spring kuralları gereği interface ismine Impl son ekini vererek yeni bir sınıf yaratırız. Burada SearchSession hibernate sınıfı. Burada fetch() metoduna limit değeri geçiliyor.  
import org.hibernate.search.mapper.orm.session.SearchSession;

@Transactional
public class SearchRepositoryImpl<T, ID extends Serializable> 
  extends SimpleJpaRepository<T, ID>
        implements SearchRepository<T, ID> {

  private final EntityManager entityManager;

  public SearchRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
    super(domainClass, entityManager);
    this.entityManager = entityManager;
  }

  public SearchRepositoryImpl(
          JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    this.entityManager = entityManager;
  }

  @Override
  public List<T> searchBy(String text, int limit, String... fields) {

    SearchResult<T> result = getSearchResult(text, limit, fields);

     return result.hits();
  }

  private SearchResult<T> getSearchResult(String text, int limit, String[] fields) {
    SearchSession searchSession = Search.session(entityManager);

    SearchResult<T> result = searchSession
      .search(getDomainClass())
      .where(f -> f.match().fields(fields).matching(text).fuzzy(2))
      .fetch(limit);
    return result;
  }
}
Artık yeni bir repository şöyledir
@Repository
public interface PlantRepository extends SearchRepository<Plant, Long> {
}
Örnek - Sayfalama
Şöyle yaparız. Burada fetch() metoduna offset + limit değerleri geçiliyor. 
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageDTO<T> {

    private List<T> content;
    private long total;
}

@Override
public PageDTO<T> searchPageBy(String text, int limit, int offset, String... fields) {
  SearchResult<T> result = getSearchResult(text, limit, offset, fields);

  return new PageDTO<T>(result.hits(), result.total().hitCount());
}

private SearchResult<T> getSearchResult(String text, int limit, int offset, 
  String[] fields) {
  SearchSession searchSession = Search.session(entityManager);

  SearchResult<T> result = searchSession
    .search(getDomainClass())
    .where(f -> f.match().fields(fields).matching(text).fuzzy(2))
    .fetch(offset, limit);
    return result;
}



21 Haziran 2018 Perşembe

HibernateSearch @AnalyzerDef Anotasyonu

Giriş
Şu satırı dahil ederiz.
import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;

import org.hibernate.search.annotations.AnalyzerDef;
import org.hibernate.search.annotations.TokenFilterDef;
import org.hibernate.search.annotations.TokenizerDef;
Örnek
Şöyle yaparız.
@Entity
@Indexed
@Inheritance
@Table(name = "application",
 uniqueConstraints = { @UniqueConstraint(columnNames = "reference") })
@AnalyzerDef(name = "customAnalyser",
 tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class), filters = {

        @TokenFilterDef(factory = LowerCaseFilterFactory.class), // Lowercase
                                                                    // all
                                                                    // characters
})
public class Application implements Serializable {
  ...
}

HibernateSearch @Field Anotasyonu

Giriş
Şu satırı dahil ederiz.
import org.hibernate.search.annotations.Field;
Date,String,Long gibi tiplere kullanılabilir.
index Alanı
Index.YES ise alan searchable olur.

name Alanı
Lucene Document içinde hangi isim ile saklanacağını belirtir.

store Alanı
Store.YES ile alanın orijinal değeri de saklanır

Örnek
Şöyle yaparız.
@Column(name = "reference")
@Analyzer(definition = "customAnalyser")
@Field(index = Index.YES, analyze = Analyze.YES, store = Store.YES)
private String reference;

HibernateSearch @Indexed Anotasyonu

Giriş
Şu satırı dahil ederiz.
import org.hibernate.search.annotations.Indexed;
Açıklaması şöyle
... the @Index annotation indicates to Hibernate Search that we want to index this entity in order to apply search operation on it.

Örnek
Şöyle yaparız
@Entity
@Indexed
@Table(name = "ApplicationTypeCreation")
public class ApplicationTypeCreation extends Application {
  ...
}
Örnek
Şöyle yaparız
import org.hibernate.annotations.NaturalId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;

import javax.persistence.*;
import java.time.Instant;

@Indexed
@Entity
@Table(name = "plant")
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class Plant {
  ...
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @FullTextField()
  @NaturalId()
  private String name;

  @FullTextField()
  @NaturalId()
  private String scientificName;

  @FullTextField()
  private String family;
} 
routingBinder Alanı - Conditional Index İçindir
Routing Bridges ile gerçekleştirilir

Örnek
Şöyle yaparız. Burada indeksleme koşulunu belirtiyoruz. Eğer name ismi rosaceae ise nesne indekslenmez. Burada RoutingBridge arayüzünden kalıtan sınıf nested kullanılıyor ama harici bir sınıf ta olabilir. RoutingBridge nesnesi RoutingBinder ile Hibernate'e tanıtılır. 
public class PlantRoutingBinder implements RoutingBinder {

  private static final String FIELD_TO_USE = "name";
  private static final List<String> FAMILIES_TO_IGNORE = Arrays.asList("rosaceae");

  @Override
  public void bind(RoutingBindingContext context) {
    context.dependencies().use(FIELD_TO_USE);

    context.bridge(Plant.class, new Bridge());
  }

  public static class Bridge implements RoutingBridge<Plant> {
    @Override
    public void route(
      DocumentRoutes routes,
      Object entityIdentifier,
      Plant indexedEntity,
      RoutingBridgeRouteContext context) {
      if (FAMILIES_TO_IGNORE.contains(indexedEntity.getFamily())) {
        routes.notIndexed();
      } else {
        routes.addRoute();
      }
    }

    @Override
    public void previousRoutes(
      DocumentRoutes routes,
      Object entityIdentifier,
      Plant indexedEntity,
      RoutingBridgeRouteContext context) {
        routes.addRoute();
    }
  }
}
ve kullanmak için şöyle yaparız
@Indexed(routingBinder = @RoutingBinderRef(type = PlantRoutingBinder.class))
@Entity
@Table(name = "plant")
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class Plant {
   ...
}