1 Hibernate und Elasticsearch Von 0 auf 100 in 40 Minuten Gunnar Morling, Red Hat 2 Gunnar Morling Opensource-Softwareentwickler bei Red Hat Div. Hibernate-Projekte Spec Lead für Bean Validation 2.0 Andere Projekte: ModiTect, MapStruct [email protected] @gunnarmorling http://in.relation.to/gunnar-morling/ 3 "Wir brauchen eine Suchfunktion" Sehr gute Treffer Smart (fehlertolerant...) 4 Wie umsetzen? SQL hat LIKE? SELECT * FROM PRODUCT p WHERE LOWERCASE(p.title) LIKE %:1% OR LOWERCASE(p.title) LIKE %:2% OR LOWERCASE(p.title) LIKE %:3% OR LOWERCASE(p.description) LIKE %:1% OR LOWERCASE(p.description) LIKE %:2% OR LOWERCASE(p.description) LIKE %:3% OR ... ; 5 Wie umsetzen? Ja, aber... Flexionen, Normalisierung, Stop-Worte Synonyme, Abkürzungen Unscharfe Suche Ergebnissortierung 6 Wie umsetzen? Ja, aber... Flexionen, Normalisierung, Stop-Worte Synonyme, Abkürzungen Unscharfe Suche Ergebnissortierung SELECT * FROM PRODUCT p WHERE LOWERCASE(p.title) LIKE %:1% OR LOWERCASE(p.title) LIKE %:2% OR LOWERCASE(p.title) LIKE %:3% OR LOWERCASE(p.description) LIKE %:1% OR LOWERCASE(p.description) LIKE %:2% OR LOWERCASE(p.description) LIKE %:3% OR ... ; 7 Wie umsetzen? Invertierter Index Text zerlegen ("tokenisation") Index erstellen: Token → Enthaltende Dokumente Suche: Relevanzbasierte Sortierung 8 Lucene + Elasticsearch Apache Lucene DIE Java-Library für Volltextsuche Feature-reich, anpassbar Hohe Performanz Elasticsearch "Search as a Service" Basiert auf Lucene Scale-out Architektur REST API 9 Index-Synchronisation?! ??? JPA-Anwendung 10 Index-Synchronisation?! Indexstruktur synchron halten Update des Index in Elasticsearch bei Datenänderungen JSON erzeugen Typumwandlungen Assoziationen/Denormalisierungen Mapping von Suchergebnissen 11 Hibernate Search Anwendung ORM Hibernate Search DB Volltext-Index 12 Hibernate Search Integriert Hibernate und Lucene Traditionell: Eigene Verwaltung des Lucene-Index Dateisystem Infinispan Unter Java SE and EE Kein neues Projekt :) 13 Beispiel 14 Ein Beispiel Entity-Modell 15.1 Demo 15.2 Ein Beispiel pom.xml <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <version>5.7.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-elasticsearch</artifactId> <version>5.7.0.Final</version> </dependency> 15.3 Ein Beispiel persistence.xml <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1"> <persistence-unit name="videoGamePu" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="hibernate.connection.url" value="..." /> <property name="hibernate.search.default.indexmanager" value="elasticsearch" /> <property name="hibernate.search.default.elasticsearch.host" value="http://127.0.0.1:9200" /> <property name="h.s.default.elasticsearch.index_schema_management_strategy" value="CREATE" /> </properties> </persistence-unit> </persistence> 15.4 Ein Beispiel Root-Entität curl -v -XGET 'host/videogame/_search?pretty=true' \ -d '{ "query" : { "match_all" : {} } }' @Entity @Indexed(index = "videogame") public class VideoGame { @Id @GeneratedValue public long id; @Field() public String title; @Field public String description; @Field public int rating; { ... "hits": [ { "_index": "videogame", "_type": "org.hibernate. ... .VideoGame", "_id": "5", "_score": 1.0, "_source": { "rating": 8, "release": "2005-12-04T23:00:00Z", "description": "The Samurai is...", "title": "Revenge of the Samurai" } }, ... ] } @Field(name="release") public Date publishingDate; ... } } 15.5 Ein Beispiel Embeddable und ElementCollection @Entity @Indexed(index = "videogame") public class VideoGame { { "rating": 8, "description": "The Samurai is mad and takes revenge", "release": "2005-12-04T23:00:00Z", "title": "Revenge of the Samurai", "tags": [ "action", "real-time", "anime" ], "publisher": { "name": "Samurai Games, Inc.", "address": "12 Main Road" } @ManyToOne @IndexedEmbedded public Publisher publisher; @ElementCollection @Field @IndexedEmbedded public List<String> tags; ... } } @Entity public class Publisher { @Field public String name; @Field public String address; ... 15.6 Ein Beispiel Eingebettete Assoziation @Entity @Indexed(index = "videogame") public class VideoGame { { "rating": 5, "description": "7 Ninjas live in a castle", "release": "2007-04-06T22:00:00Z", "title": "Ninja Castle", "publisher": { "name": "Samurai Games, Inc.", "address": "12 Main Road" }, "characters": [ { "nickName": "Frank", "specialPower": "Sleeping" }, { "nickName": "Dash", "specialPower": "Running" }, { "nickName": "Luigi", "specialPower": "Plumbing" } ] @ManyToMany @IndexedEmbedded public List<Character> characters; ... } @Entity public class Character { @Field public String nickName; @Field public String specialPower; ... } } 16 Abfragen Hibernate Search Query DSL Weitere: wildcard, range, boolean, fuzzy, phrase, spatial FullTextQuery query = ftem.createFullTextQuery( qb.spatial() .onField( "location" ) .within( 100, Unit.KM ) .ofLatitude( 24.0d ) .andLongitude( 32.0d ) .createQuery(), Restaurant.class ); 17 Native Abfragen JSON-Syntax FullTextQuery query = ftem.createFullTextQuery( ElasticsearchQueries.fromJson( "{" + "'query': {" + "'bool' : {" + "'must' : [" + "{ 'match' : { 'title' : 'samurai' } }," + "{ 'match' : { 'publisher.name' : 'games' } }" + "]" + "} } }" ), VideoGame.class ); 18 Projektionen FullTextQuery query = ftem.createFullTextQuery( ElasticsearchQueries.fromQueryString( "title:sam* OR description:sam*" ), VideoGame.class ) .setProjection( "title", "publisher.name", "release" ); 19 Vorteile Date und Index synchron Sichere, automatische Index-Updates Automatisches Mapping JSON-Mapping Typkonvertierungen Transparente Denormalisierung Managed Entities aus Abfragen Gute Performance (Bulk-Requests) 20 Vorteile Entkopplung Einfache Konfiguration mittels Annotationen Keine direkten Aufrufe des ES API nötig Einfacher Backendwechsel Entkopplung von Komponenten zur Laufzeit 21 Was sonst noch? Analyzer Faceting Re-indexing per Batch Mandatenfähigkeit Konfigurations-DSL Support für Java 8 / Hibernate ORM 5.2 22 Status Tech Preview in Hibernate Search 5.6/5.7 Indexierung verschiedenster Feldtypen, Embeddables etc. Diverse Abfrage-Möglichkeiten Viele Tests der bestehenden Testsuite erfolgreich 23 Nächste Schritte 5.8: Unterstützung für Elasticsearch 5 Feedback sammeln in 5.x Hibernate Search 6 Abstraktion von Lucene in API und SPI Query DSL verallgemeinern 6.x: Solr-Unterstützung 24 Feedback Website: hibernate.org/search Beispielprojekt: github.com/hibernate/hibernate-demos Hibernate-Blog: in.relation.to 25