用于连字符词的Lucene索引/查询策略

时间:2011-02-08 09:55:40

标签: java search lucene

有许多单词用连字符或空格分隔,但经常用作一个单词。 例如:篮球或篮球可以写成篮球。

现在当我作为句子索引时,请说:"Hey dude, I played basket ball yesterday". 现在我尝试查询"basketball" [没有双引号] ..

这种情况,反之亦然,(索引basketball和查询basket ball)我不会得到任何结果。有没有办法直接或间接解决这个问题?

Edit:
我举了个例子来说明这个问题。在我的实际应用场景中,我将索引和搜索ID。 如果我索引:011 12345,
我应该可以使用01112345查询它。

提前致谢。

2 个答案:

答案 0 :(得分:3)

连字符在这里不是问题,假设您使用的是类似于连字符等标记的StandardTokenizer,那么搜索“basket ball”的用户将匹配原始文本“Basket-Ball”(反之亦然)那么没问题。

问题在于两个单词和一个单词等价物,例如“篮球”和“篮球”。你基本上需要处理同义词(例如夹克/外套或你的情况下篮球/'篮球')。

您可以通过自己创建等效单词列表,或使用WordNet之类的字典,并使用每个术语的同义词补充索引或搜索来解决此问题。 Solr有一个你可以利用的SynonymFilter(另见here)。

编辑:

这是我刚才写的一个非常基本的同义词过滤器的代码。同义词不是外部化的,但您可以轻松地自己添加。

public class SynonymFilter extends TokenFilter {
    private static final Logger log = Logger.getLogger(SynonymFilter.class);

    private Stack<Token> synStack = new Stack<Token>();

    static CharArrayMap<String[]> synLookup = new CharArrayMap<String[]>(5, true);
    static {
        synLookup.put("basketball".toCharArray(), new String[]{"basket ball"});
        synLookup.put("trainer".toCharArray(), new String[]{"sneaker"});
        synLookup.put("burger".toCharArray(), new String[]{"hamburger"});
        synLookup.put("bike".toCharArray(), new String[]{"bicycle", "cycle"});
    }

    // TODO reverse map all the syns to each other e.g. sneaker to trainer

    protected SynonymFilter(TokenStream input) {
        super(input);
    }

    @Override
    public Token next(Token reusableToken) throws IOException {
        if (synStack.size() > 0)
            return synStack.pop();

        Token nextToken = input.next(reusableToken);
        if (nextToken != null) {
            addSynonyms(nextToken);
        }

        return nextToken;
    }

    private void addSynonyms(Token nextToken) {
        char[] word = Arrays.copyOf(nextToken.termBuffer(), nextToken.termLength());
        String[] synonyms = synLookup.get(word);
        if (synonyms != null) {
            for (String s : synonyms) {
                if (!equals(word, s)) {
                    char[] chars = s.toCharArray();
                    Token synToken = new Token(chars, 0, chars.length, nextToken.startOffset(),  nextToken.endOffset());
                    synToken.setPositionIncrement(0);
                    synStack.add(synToken);
                    log.info("Found synonym: " + s + " for: " + new String(nextToken.term()));
                }
            }
        }
    }

public static boolean equals(char[] word, String subString) {
    return equals(word, word.length, subString);
}

public static boolean equals(char[] word, int len, String subString) {

    if (len != subString.length())
        return false;

    for (int i = 0 ; i < subString.length(); i++) {
        if (word[len - i - 1] != subString.charAt(subString.length() - i - 1))
            return false;
    }

    return true;

}
}

答案 1 :(得分:0)

我不是Lucene用户,但这是我的2美分:在开始编制索引之前,您必须预先处理数据,使其看起来像您想要搜索它的方式。如果有人搜索球,你是否也希望它出现在搜索结果中?如果是,那么你必须用两句话作为这句单句的输入(“嘿伙计,我昨天打篮球”和“嘿老兄,我昨天打过篮球”)并对其中的两个进行索引。这是你在找什么?