no-hql at jug milano
TRANSCRIPT
NoHQL: oltre Hibernate e i database relazionali
Java Users Group Milano4 Novembre 2010
Sanne GrinoveroTeam Hibernate, Sourcesense
Tristan TarrantFreelance consultant
● Consulente presso Sourcesense e Red Hat● sviluppatore di Scarlet, clustering for JIRA● Consulente JBoss e Search (Lucene)
● Team Hibernate● Contributi open source:
● Apache Lucene/Solr● Infinispan● JGroups● Seam
chi sono?
● Il problema● Lucene e integrazione tramite Hibernate
Search● Infinispan, in breve
● Infinispan Query● Hibernate Search con Infinispan
● Custom stores: Cassandra● Hibernate OGM
Agenda
Search engine
● Chi cerca non sa cosa sta cercando:● non è in grado di inserire l'ID dell'oggetto che si
desidera recuperare● non conosce il contenuto esatto del documento
● Interfaccia tipica:● Form complesse con molti campi● Testo libero
Esempio di modello (Hibernate)
Una prima implementazione:
Funziona?
String nomeAutore = “Fabrizio De André”String nomeProdotto = “Nuvole barocche”List<Prodotti> list = s.createQuery( “ ...? “ );
String nomeAutore = “De André, Fabrizio”String nomeProdotto = “Nuvole barocche”List<Prodotti> list = s.createQuery( “ ...? “ );
String nomeAutore = “De Andre, Fabrizio”String nomeProdotto = “Nuvole barocche”List<Prodotti> list = s.createQuery( “ ...? “ );
Nuove feature richieste
● Campo unico● Nome autore e/o nome prodotto● Nome e cognome dell'autore, nomi composti
● Rilevanza● prodotti che corrispondono sia per nome che per
nome autore devono venire per primi● Nomi completi di prodotto prima di nomi parziali
– “portatili mac” > 1 - portatile mac2 - portatili
SQL è il martello:
List<Prodotti> list = s.createQuery(“ ...? “).setParameter(“F. de André nuvole barocche”).list();
● case misto● posizione relativa dei termini● accenti e abbreviazioni● match su field multipli (titolo e autore)● 18,800 risultati in 0,41 secondi
altre funzioni utili:
● Similitudine:● hibernate ~ hybernat
● Prossimità, sinonimi, abbreviazioni:● 'JPA' o 'Java Persistence API'
● Aggiustamenti nella rilevanza:● Un termine nel titolo “vale” di più?
● Stemming (per lingua!)
Quanto userestiGoogle
se ti restituisse i siti in ordine alfabetico?
Quanto userestiGoogle
se ti restituisse i siti in ordine alfabetico?
“hibernate”About 8,320,000 results (0.10 seconds)
quindi..● Il database non è sufficiente
● SQL non è adatto a certi problemi● eppure SQL è tutt'ora molto utile
● Bisogna conoscere più strumenti per sceglere quelli giusti● database relazionali● filesystem● motori di indicizzazione fulltext● key-value store
● Integrare, mantenere coerenza e integrità dei dati● quanto basta
Esempi di prodotti
Nexus
Tipi di form e di search
inverted index: Lucene
● Progetto open source Apache™,● nella “top 10” dei più scaricati.● Community molto attiva.
● Un'implementazione di indice invertito all'avanguaria, costantemente aggiornato allo stato dell'arte del settore.
● Principalmente in Java ma portato in vari altri linguaggi.● Si trovano innumerevoli estensioni open source, ad
esempio per il supporto ottimale di tutte le lingue occidentali.
Lucene
● Similitudine tra termini e documenti● Sinonimi● Stemming● Stopwords● TermVectors● MoreLikeThis● Faceted Search● Velocità e scalabilità orizzontale
● ... e naturalmente ricerche full-text.
Similitudine
● N-Grams (distanza di editing)● Fonetico (Soundex™)● Altro... definibile secondo necessità
Cagliari ⁓ càliariCagliari ⁓ cag agl gli lia ari
Cagliari ⁓ CGRI
Lucene: Sinonimi (o quasi)
● Applicabile a “index time”● a “query time”● Necessita di un vocabolario● Integrabile con vari vocabolari (WordNet)
giornale ⁓ periodico ⁓ quotidiano
Lucene: Stemming
In varie lingue!
parliamone ⁓ parloVelocemente ⁓ velocità
Lucene: Stopwords
● Rimuovono “rumore di fondo” e termini di disturbo dall'indice: una ricerca per “non e queste al per da” non da indizi su cosa l'utente stia cercando.
Lucene: Indice
● Necessita di un indice● Su disco● In memoria● ...
● Formato da elementi immutabili● Ottimizza la ricerca, non la modifica
● Un mondo fatto di stringhe e frequenze
Può sembrare facile
● La struttura di un indice è profondamente diversa da un modello relazionale.
● L'indice e il database devono rimanere in coerenza, immediata o eventuale.● In caso di incoerenza: di quale vi fidate?
● Cosa restituiscono le query?● Come sono fatte le query?
Strutture differenti per natura
● Un Documento Lucene, risultato di una query fulltext, assomiglia (vagamente) ad una
Map<String,String>
● Un modello Hibernate è composto da entità strutturate, è designato per essere funzionale come business model.
● I valori di ritorno di Query JPA/Hibernate sono gestiti dal persistence manager: modifiche sincronizzate al commit
● Si devono mappare dati strutturalmente diversi.
Differenze strutturali dell'informazione
Incoerenze architetturali
Quickstart su Hibernate Search
● Aggiungere hibernate-search al classpath e dipendenze<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernatesearch</artifactId>
<version>3.2.0.Final</version>
</dependency>
● Il resto è opzionale:
● Dove mettere l'indice● Estensioni● Parametri per le performance● Mapping più sofisticati● Clustering
Come usare Hibernate Search@Entitypublic class Essay { @Id public Long getId() { return id; }
public String getSummary() { return summary; } @Lob public String getText() { return text; } @ManyToOne public Author getAuthor() { return author; }...
Come usare Hibernate Search@Entity @Indexedpublic class Essay { @Id public Long getId() { return id; }
public String getSummary() { return summary; } @Lob public String getText() { return text; } @ManyToOne public Author getAuthor() { return author; }...
Come usare Hibernate Search@Entity @Indexedpublic class Essay { @Id public Long getId() { return id; } @Field public String getSummary() { return summary; } @Lob public String getText() { return text; } @ManyToOne public Author getAuthor() { return author; }...
Come usare Hibernate Search@Entity @Indexedpublic class Essay { @Id public Long getId() { return id; } @Field public String getSummary() { return summary; } @Lob @Field @Boost(0.8) public String getText() { return text; } @ManyToOne public Author getAuthor() { return author; }...
Come usare Hibernate Search@Entity @Indexedpublic class Essay { @Id public Long getId() { return id; } @Field public String getSummary() { return summary; } @Lob @Field @Boost(0.8) public String getText() { return text; } @ManyToOne @IndexedEmbedded public Author getAuthor() { return author; }...
@Entitypublic class Author {
@Id @GeneratedValueprivate Integer id;private String name;@OneToManyprivate Set<Book> books;
}
@Entitypublic class Book { private Integer id; private String title;}
Altro esempio
@Entity @Indexedpublic class Author {
@Id @GeneratedValueprivate Integer id;
@Field(store=Store.YES)
private String name;@OneToMany
@IndexedEmbeddedprivate Set<Book> books;
}
@Entitypublic class Book { private Integer id; @Field(store=Store.YES) private String title;}
Struttura dell'indice
String[] productFields = {"summary", "author.name"};
QueryParser parser = new MultiFieldQueryParser(productFields, new StandardAnalyzer() );
Query luceneQuery = parser.parse(searchQuery);
FullTextEntityManager ftEm = Search.getFullTextEntityManager(entityManager);
FullTextQuery query = ftEm.createFullTextQuery(luceneQuery,Product.class );
List<Essay> items = query.setMaxResults(100).getResultList();
int totalNbrOfResults = query.getResultSize();
Codice di Query:
TotalNbrOfResults= 8.320.000(0.20 seconds)
Risultati
● Pojo gestiti: coerenza tra le tecnologie di store● Paginazione JPA
● .setMaxResults( 20 ).setFirstResult( 100 );
● Restrizioni per tipo e polimorfismo:● .createQuery( luceneQuery, A.class, B.class, ..);
● Projection● Result mapping
Filtri
FullTextQuery ftQuery = s // s è di tipo FullTextSession
.createFullTextQuery( query, Prodotto.class )
.enableFullTextFilter( "vietatoMinori" )
.enableFullTextFilter( "offerteDelGiorno" )
.setParameter( "giorno", “20101104” )
.enableFullTextFilter( "disponibiliSubito" )
.setParameter( "sede", "Milano" );
List<Prodotto> risultati = ftQuery.list();
Analisi del testo@Entity @Indexed
@AnalyzerDef(name = "italianAnalyzer", tokenizer =
@TokenizerDef(factory=StandardTokenizerFactory.class),filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = SnowballPorterFilterFactory.class,
params = {@Parameter(name = "language", value = "Italian")})
})
public class Book {
@Field(index=Index.TOKENIZED, store=Store.NO)
@Analyzer(definition = "italianAnalyzer")
private String title;
...
Altro...
● @Boost e @DynamicBoost● @AnalyzerDiscriminator● @DateBridge(resolution=Resolution.MINUTE)● @ClassBridge e @FieldBridge● @Similarity● Automatic Index optimization
Clustering per coda
Attualmente in beta:
● Astrazione delle query Lucene● Performance migliorate
● Lucene 3● NumericField● FieldCache
● Mapping dinamico
● Infinispan clustering
cos'è Infinispan? Perchè?
● Highly scalable data grid platform● 100% open source licensed (LGPL)● Based on some JBoss Cache code
● But mostly all-new!
● JBoss Cache is a clustered caching library● Infinispan is a data grid platform
● JBoss Cache uses a tree-structured API● Infinispan is a Map. Like JSR-107’s JCACHE
Data Storage
● Le cloud sono di natura effimera● appliance più affidabili sono stateless
● Database difficili da gestire● MySQL in cluster.. ! solo con RDS
● Scalabilità in cloud: importante?● Database sono ancora un collo di bottiglia
● e single point of failure!
Soluzione: data grids!
● Altamente scalabili● no single point of failure● funziona con nodi effimeri● latenza minima
● Implementazioni● Amazon SimpleDB / Dynamo● Infinispan● altri...
Complicato?
Tutti conoscono Map<?,?>
CacheManager cm = new DefaultCacheManager("infin-cfg.xml");Cache cache1 = cm.getCache("cache1");
Map distMap = cache1;distMap.put( "chiave", "valore" );distMap.get( "altraChiave" );
ConcurrentMap concurrentDistMap = cm.getCache("cache2");concurrentDistMap.replace( "k", "atteso", "nuovo" );
Map, arricchito
CacheManager cm = new DefaultCacheManager("infi-cfg.xml");Cache cache1 = cm.getCache("cache1");
cache1.addListener( arg0 );
cache1.putAsync( arg0, arg1 );cache1.removeAsync( arg0 );
cache1.startBatch();
AdvancedCache advancedCache = cache1.getAdvancedCache();advancedCache.withFlags( Flag.SKIP_REMOTE_LOOKUP ) .put( arg0, arg1 );
Caratteristiche
● Strutture interne efficienti● CAS totale● Synchronized assente● Contenitori di dati naturalmete ordinate
– Molto efficiente per gestire policy di eviction
● Serializzazione ottimizzata● JBoss Marshalling
– payloads minimali + poolable streams
Ereditati da JBoss Cache
● JTA transactions● Replicated data structure● Eviction● Notifications and eventing API● JMX reporting● Fine-grained replication● MVCC locking● Non-blocking state transfer techniques
● CacheStore API● Custom (non-JDK) marshalling
Nuove caratteristiche di Infinispan
● Cache distribuita: Consistent hash based data distribution
● Map API semplicissima (JSR-107 compliant)● Non limitato ai linguaggi JVM
● REST API● modulo compatibile memcached Client/server● HotRod (es: Java, Ruby, Python, ...)
● Plugin di management per JOPR● Distributed executors
● Query API
Cache distribuita
● Consistent hash based data distribution● Permette di scalare su cluster grandi● Test in corso su cluster di migliaia di nodi
● Cache locale “L1” per letture migliorate● Invalidazione distribuita
● Ribilanciamento dinamico● Non presenta single-point-of-failure
Memoria condivisa, Indice condiviso
Vari CacheStore
● Filesystem● Database SQL● S3 / jclouds● Jdbm● ...
Storage esterno, copie locali
Clustering, store condiviso
Vari CacheStore
● Filesystem● Database SQL● S3 / jclouds● Jdbm●
...in conclusione con Infinispan
● query full text in memoria distribuita● cache distribuita, evict to “slow” CacheStore
● usa quella adatta all'uso!
● Infinispan: Hibernate Second level cache● usa anche i database, con caching (Not-Only SQL)
● Indicizza gli oggetti anche senza database● Infinispan Query● Query distribute (map/reduce, non full text)
● ogni componente prende parte a XA● eppur scala!
In conclusione● Integrità e transazioni
● infinispan supporta transazioni XA– e ne fornisce i vantaggi poggiando su store “unaware”
● Hibernate Search offre transazioni su Lucene
● efficienza: strategie ottimali per ogni query● monitoring
● enterprise level● statistiche
● scalabilità orizzontale
open source: partecipa!
Quale approccio è più complesso?Quale funziona?
@Entity @Indexedpublic class Essay { @Id public Long getId() { return id; } @Field public String getSummary() {return summary; } @ManyToOne @IndexedEmbedded public Author getAuthor() { return author; }
Tecnologie già note
Codice costoso da refattorizzareNon scalaSolo i requisiti più semplici sono fattibili
Velocissimo e scalabileFunziona anche per rilevanzaSemplice evolvere il modello
Utile conoscere i fondamenti di Lucene
Domande?
http://search.hibernate.org● Hibernate Search in Action
http://lucene.apache.org● Lucene In Action (2°ed)
http://www.infinispan.org
http://in.relation.to
http://forum.hibernate.org
twitter.com/SanneGrinovero
http://www.sourcesense.com