2010-09-23 27 views
2

Utilisation des annotations Hibernate Search (principalement @Field(index = Index.TOKENIZED)) J'ai indexé un certain nombre de champs liés à une classe de mine persistante appelée Compound. J'ai mis en place la recherche de texte sur tous les champs indexés, en utilisant le MultiFieldQueryParser, qui a jusqu'ici fonctionné très bien.La correspondance générique de Lucene échoue sur les notations chimiques (?)

Parmi les champs indexés et interrogeables est un champ appelé CompoundName, avec des valeurs de l'échantillon:

  • 3-Hydroxyflavone
  • 6,4'-Dihydroxyflavone

Quand je cherche une ou l'autre de ces valeurs dans sa totalité le composé apparenté les instances sont renvoyées. Cependant, des problèmes se produisent lorsque j'utilise le nom partiel et présenter des caractères génériques:

  • recherche de 3-Hydroxyflav* donne toujours le coup correct, mais
  • recherche de 6,4'-Dihydroxyflav* ne parvient pas à trouver quoi que ce soit.

Maintenant que je suis tout à fait nouveau pour Lucene/Hibernate recherche, je ne suis pas tout à fait sûr où regarder ce point .. Je pense qu'il pourrait avoir quelque chose à voir avec la présente ' dans la deuxième requête , mais je ne sais pas comment procéder .. Dois-je regarder dans Tokenizers/Analyzers/QueryParsers ou quelque chose d'autre entièrement?

Ou est-ce que quelqu'un peut me dire comment je peux faire correspondre la deuxième recherche générique, de préférence sans rompre le comportement de MultiField-search? J'utilise Hibernate-Search 3.1.0.GA & Lucene-core 2.9.3.


Certains bits de code pertinents pour illustrer mon approche actuelle:

parties pertinentes de la classe indexée composé:

@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; 
... 
} 

Comment je recherche actuellement sur les champs indexés:

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(); 

Répondre

2

Je pense que votre problème est une combinaison de problèmes d'analyseur et de langage de requête. Il est difficile de dire ce qui cause exactement le problème. Pour le savoir, je vous recommande d'inspecter votre index en utilisant l'outil d'index Lucene Luke.

Étant donné que dans votre configuration de recherche Hibernate vous n'utilisez pas d'analyseur personnalisé, la valeur par défaut StandardAnalyzer est utilisée. Cela serait cohérent avec le fait que vous utilisez StandardAnalyzer dans le constructeur de MultiFieldQueryParser (utilisez toujours le même analyseur pour l'indexation et la recherche!). Ce dont je ne suis pas si sûr, c'est comment "6,4'-Dihydroxyflavone" est marqué par StandardAnalyzer. C'est la première chose à savoir.Par exemple, le javadoc dit:

mots à Splits traits d'union, à moins que il y a un certain nombre dans le jeton, dans auquel cas tout le jeton est interprété comme un numéro de produit et est pas divisé.

Il se peut que vous deviez écrire votre propre analyseur qui symbolise vos noms chimiques comme vous en avez besoin pour vos cas d'utilisation.

Ensuite, l'analyseur de requêtes. Assurez-vous de bien comprendre la syntaxe de la requête - Lucene query syntax. Certains caractères ont une signification particulière, par exemple un «-». Il se peut que votre requête soit analysée de la mauvaise façon.

De toute façon, la première étape consiste à découvrir comment vos noms chimiques deviennent atomisés. J'espère que cela pourra aider.

+0

Pour votre information, j'ai vérifié rapidement comment le StandardAnalyzer tokenizes vos exemples. "3-Hydroxyflavone" semble tomber sous la règle du produit mentionné ci-dessus. Il devient un seul jeton "3-hydroxyflavone". "6,4'-Dihydroxyflavone" d'autre part devient deux jetons "6,4" et "dihydroxyflavone". – Hardy

+0

Wow, merci! J'essayais juste d'utiliser Luke ici pour tester la même chose. Gues cela signifie que j'ai besoin d'utiliser un autre analyseur? (J'ai essayé de mettre le champ à UN_TOKENIZED, mais cela casse même le premier exemple de recherche ..) – Tim

+0

Il semble que le StandardTokenizer divise les mots en apostrophes .. Cela identifie au moins le problème, mais il me faudra du temps pour le réparer ceci .. :) Merci pour l'aide! – Tim

4

Utilisez WhitespaceAnalyzer au lieu de StandardAnalyzer. Il ne se séparera que des espaces, et non des virgules, des traits d'union, etc. (Il ne les minuscatera pas, donc vous devrez construire votre propre chaîne d'espaces + minuscules, en supposant que vous voulez que votre recherche soit insensible à la casse). Si vous devez faire les choses différemment pour différents champs, vous pouvez utiliser un PerFieldAnalyzer.

Vous ne pouvez pas simplement le définir sur Non-tokenized, car cela interprétera l'ensemble de votre corps de texte comme un seul jeton.

+0

Hmm, cela semble prometteur, mais les tests initiaux jusqu'à présent n'ont pas encore résolu le problème .. Je pourrais avoir à essayer avec la chaîne de filtres et PerFieldAnalyzer .. rendra compte une fois que je le ferai. – Tim

1

J'ai écrit mon propre analyseur:

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()); 
    } 
}