Lucene corrispondenza jolly non riesce a notazioni chimici (?)
-
04-10-2019 - |
Domanda
usando Hibernate ricerca annotazioni (per lo più solo @Field(index = Index.TOKENIZED)
) Ho indicizzato un certo numero di settori relativi ad una classe persistente di mine chiamato Compound. I'VE di ricerca messa a punto del testo su tutti i campi indicizzati, utilizzando il MultiFieldQueryParser
, che ha finora funzionato bene.
Tra i campi indicizzati e ricercabili è un campo chiamato CompoundName, con valori di esempio:
-
3-Hydroxyflavone
-
6,4'-Dihydroxyflavone
Quando cerco uno di questi valori in pieno le relative istanze composti vengono restituiti. Tuttavia i problemi si verificano quando si utilizza il nome parziale e presento i caratteri jolly:
- alla ricerca di
3-Hydroxyflav*
dà ancora il colpo corretto, ma - alla ricerca di
6,4'-Dihydroxyflav*
non riesce a trovare nulla.
Ora come io sono abbastanza nuovo per Lucene / Hibernate-ricerca, non sono abbastanza sicuro dove andare a cercare, a questo punto .. penso che potrebbe avere qualcosa a che fare con la '
presente nella seconda query, ma io non so come procedere .. devo cercare in tokenizers / analizzatori / QueryParsers o qualcosa di completamente diverso?
O qualcuno può dirmi come posso ottenere la seconda ricerca con caratteri jolly per abbinare, preferibilmente senza rompere il comportamento multicampo-ricerca?
Sto usando Hibernate-Search & 3.1.0.GA Lucene-core 2.9.3.
Alcuni bit di codice rilevanti per illustrare il mio approccio attuale:
parti rilevanti della classe Compound indicizzato:
@Entity
@Indexed
@Data
@EqualsAndHashCode(callSuper = false, of = { "inchikey" })
public class Compound extends DomainObject {
@NaturalId
@NotEmpty
@Length(max = 30)
@Field(index = Index.TOKENIZED)
private String inchikey;
@ManyToOne
@IndexedEmbedded
private ChemicalClass chemicalClass;
@Field(index = Index.TOKENIZED)
private String commonName;
...
}
Come Attualmente la ricerca sui campi indicizzati:
String[] searchfields = Compound.getSearchfields();
MultiFieldQueryParser parser =
new MultiFieldQueryParser(Version.LUCENE_29, searchfields, new StandardAnalyzer(Version.LUCENE_29));
FullTextSession fullTextSession = Search.getFullTextSession(getSession());
FullTextQuery fullTextQuery =
fullTextSession.createFullTextQuery(parser.parse("searchterms"), Compound.class);
List<Compound> hits = fullTextQuery.list();
Soluzione
Credo che il problema è una combinazione di problemi dell'analizzatore e del linguaggio di interrogazione. E 'difficile dire che cosa causa esattamente il problema. Per scoprirlo vi consiglio di ispezionare indice utilizzando lo strumento dell'indice Lucene Luca .
Dal momento che nella configurazione Hibernate Search non si utilizza un analizzatore personalizzata di default - StandardAnalyzer - viene utilizzato. Ciò sarebbe coerente con il fatto che si usa StandardAnalyzer nel costruttore di MultiFieldQueryParser (utilizzare sempre lo stesso analizzatore per l'indicizzazione e la ricerca!). Quello che non sono così sicuro di è come "6,4'-Dihydroxyflavone" viene formato token da StandardAnalyzer . Che la prima cosa che dovete sapere. Ad esempio, il javadoc dice:
Divide parole a trattini, a meno che non c'è un numero nel token, in tal caso tutto il token è interpretato come numero prodotto ed è Non scissione.
Potrebbe essere che è necessario scrivere il proprio analizzatore che tokenizza vostri nomi chimici il modo in cui avete bisogno per i vostri casi d'uso.
Avanti il ??parser query. Assicurati di conoscere la sintassi di query - Lucene interrogazione sintassi . Alcuni caratteri hanno un significato speciale, per esempio, un '-'. Potrebbe essere che la query viene analizzato il modo sbagliato.
In entrambi i casi, os primo gradino per scoprire come i vostri nomi chimici vengono token. Speranza che aiuta.
Altri suggerimenti
Usa WhitespaceAnalyzer invece di StandardAnalyzer. E 'solo dividere a spazi bianchi, e non a virgole, trattini, ecc (Non sarà minuscolo loro però, quindi sarà necessario per costruire la propria catena di spazi bianchi + minuscolo, supponendo che si desidera la vostra ricerca per essere case-insensitive). Se avete bisogno di fare le cose in modo diverso per i diversi campi, è possibile utilizzare un PerFieldAnalyzer.
È possibile non solo impostare per non-token, perché che interpretare il vostro intero corpo del testo come un token.
Ho scritto il mio analizzatore:
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.lucene.index.memory.PatternAnalyzer;
import org.apache.lucene.util.Version;
public class ChemicalNameAnalyzer extends PatternAnalyzer {
private static Version version = Version.LUCENE_29;
private static Pattern pattern = compilePattern();
private static boolean toLowerCase = true;
private static Set stopWords = null;
public ChemicalNameAnalyzer(){
super(version, pattern, toLowerCase, stopWords);
}
public static Pattern compilePattern() {
StringBuilder sb = new StringBuilder();
sb.append("(-{0,1}\\(-{0,1})");//Matches an optional dash followed by an opening round bracket followed by an optional dash
sb.append("|");//"OR" (regex alternation)
sb.append("(-{0,1}\\)-{0,1})");
sb.append("|");//"OR" (regex alternation)
sb.append("((?<=([a-zA-Z]{2,}))-(?=([^a-zA-Z])))");//Matches a dash ("-") preceded by two or more letters and succeeded by a non-letter
return Pattern.compile(sb.toString());
}
}