0

Lucene に基づいてフィルターを作成しています。API からの結果がいくつかあり、特定のクエリに一致するように結果を適用したいと考えています (API が機能しない場合があります)。結果は API から取得されるため、基本的にはそれらを RAM に保存し、インデックスを付けて、フィルター処理します。Lucene がインデックスでドキュメントを見つけた場合、このドキュメントは問題ないと見なし、そうでない場合はフィルタリングします。

あいまいにしたい場合もあれば、そうでない場合もあります。近接スイッチがあります。したがって、近似 = false には StandardAnalyzer を使用し、近似 = true には BrazilAnalyzer を使用します。Ok?

問題は、BrazilianAnalyzer が否定項を概算していることです。これは優れたアプローチではないと思います。たとえば、「greve -trabalhadores」が必要な場合、「greve do trabalho」を含むドキュメントはクエリに一致しますが、一致しないはずです。StandardAnalyzer を使用すると問題なく動作します。BrazilianAnalyzer を使用すると、ステミングのために「trabalh」を含むものはすべて無視されます。

私の解決策は、ステミング/ファジーを行わない StandardAnalyzer を使用して禁止条項を書き直すことでした。したがって、クエリの禁止されている部分では、StandardAnalyzer を使用します。他の部分では、BrazilianAnalyzer または Standard (近似スイッチに応じて) を使用します。

問題は、それが機能しないことです (時々)。

私のコードの小さなテストは次のとおりです。

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.br.BrazilianAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.util.CharArraySet;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;

public class Lucene {

    private static Logger log = Logger.getLogger(Lucene.class.getName());

    private String[] fields = new String[] { "title" };

    private BrazilianAnalyzer analyzerBrazil = new BrazilianAnalyzer(Version.LUCENE_41, new CharArraySet(Version.LUCENE_41, Collections.emptyList(), true));
    private StandardAnalyzer analyzerStandard = new StandardAnalyzer(Version.LUCENE_41, new CharArraySet(Version.LUCENE_41, Collections.emptyList(), true));

    private MultiFieldQueryParser parserBrazil = new MultiFieldQueryParser(Version.LUCENE_41, fields , analyzerBrazil);
    private MultiFieldQueryParser parserStandard = new MultiFieldQueryParser(Version.LUCENE_41, fields , analyzerStandard);

    public void filter(String query, boolean fuzzy, List<Result> results) {
        Directory index = null;

        if (results == null || results.size() == 0) {
            return;
        }

        try {
            Analyzer analyzer = fuzzy ? analyzerBrazil : analyzerStandard;
            Query q = fuzzy ? parserBrazil.parse(query) : parserStandard.parse(query);

            // terms to ignore/prohibited shoudn't be fuzzyfied...
            if (fuzzy) {
                Query queryNoFuzzy = parserStandard.parse(query);

                if (q instanceof BooleanQuery) {
                    BooleanClause[] clauses = ((BooleanQuery)queryNoFuzzy).getClauses();
                    if (clauses != null && clauses.length > 0) {
                        BooleanClause clause = null;
                        for (int i = 0; i < clauses.length; i++) {
                            clause = clauses[i];
                            if (clause.isProhibited()) {
                                ((BooleanQuery)q).clauses().set(i, clause);
                            }
                        }
                    }
                }
            }

            log.info(q.toString());

            index = index(results, analyzer);
            IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(index));
            TopDocs resultsFoundDocs = searcher.search(q, results.size());

            List<Result> resultsFound = new ArrayList<Result>();
            for (ScoreDoc resultadoFiltro : resultsFoundDocs.scoreDocs) {
                log.info("Score " + resultadoFiltro.score);
                resultsFound.add(results.get(Integer.parseInt(searcher.doc(resultadoFiltro.doc).get("index"))));
            }

            for (Result result : results) {
                if (!resultsFound.contains(result)) {
                    result.setFiltered(true);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                index.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private Directory index(List<Result> resultados, Analyzer analyzer) {
        try {
            Directory index = new RAMDirectory();
            IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_41, analyzer);

            IndexWriter writer = new IndexWriter(index, config);
            indexResults(writer, analyzer, resultados);

            return index;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private void indexResults(IndexWriter w, Analyzer analyzer, List<Result> resultados) throws IOException {
        try {
            Document resultado = null;

            for (int i = 0; i < resultados.size(); i++) {
                resultado = new Document();

                resultado.add(new TextField(fields[0], resultados.get(i).getTitle(), Field.Store.YES));
                resultado.add(new IntField("index", i, Field.Store.YES));

                w.addDocument(resultado, analyzer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            w.close();
        }
    }   

    public static void main(String[] args) {
        List<Result> ocs = new ArrayList<Result>();

        Result rb = new Result("Vivo Celular - não instalação do produto");
        ocs.add(rb);

        System.out.println("ITEMS ____________________________");
        for (Result oc : ocs) {
            System.out.println(oc.getTitle());
        }
        System.out.println("ITEMS ____________________________");

        String query = "vivo -celular";

        System.out.println("\n >> QUERY " + query);

        new Lucene().filter(query, true, ocs);

        System.out.println("\nFOUND ____________________________");
        for (Result oc : ocs) {
            if (!oc.getFiltered()) {
                System.out.println(oc.getTitle());
            }
        }
        System.out.println("FOUND ____________________________");
    }

}

class Result {

    private String title;
    private Boolean filtered = false;

    public Result(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Boolean getFiltered() {
        return filtered;
    }

    public void setFiltered(Boolean filtered) {
        this.filtered = filtered;
    }

}

「Vivo Celular - não instalação do produto」というタイトルの簡単なドキュメントがあります。「vivo -celular」を照会します。そのため、ドキュメントには celular が含まれているため、 searcher.search(q, results.size());によって返されるべきではありません。電話。近似スイッチがオンの場合にのみ発生します。クエリを出力して、「vivo」から「viv」のみをステミングします (クエリは「(title:viv) -(title:celular)」です)。

あれは正しいですか???

バージョン 4.2.2 を使用しています。4.1.0 でも発生します。

誰かが私にそのことを教えてもらえますか?

よろしくお願いします。

4

3 に答える 3

1

問題は、アナライザーを混同しているという事実にあると思います。

fuzzyフラグがtrueに設定されている場合、(ステミングを行う)を使用してドキュメントのインデックスを作成していBrazilianAnalyzerますが、。を使用して、ステミングされていない用語でクエリの一部を書き直そうとしていますStandardAnalyzer

"(title:viv) -(title:celular)"つまり、正しいクエリがある場合でも、その用語celularはディレクトリでステミングされている可能性が高いため(これは、でインデックスを作成したためですStandardAnalyzer)、そのため、句-celularは機能しません。

これに対する可能な回避策は、いくらかのオーバーヘッドを追加しますが、2つの異なるインデックスを維持することです:ステム化されたものとステム化されていないものです。これを簡単に行うために、title(with StandardAnalyzer)と(with)のstemmedtitle2つの異なるフィールドを作成できますBrazilianAnalyzerPerFieldAnalyzerWrapperを使用して、2つの異なるフィールドで機能するアナライザーを作成します。次に、クエリを次のように書き直すことができますstemmedtitle:viv -title:celular。これでうまくいくはずです。

于 2013-03-19T14:34:45.047 に答える
1

答えを探している人のために:

これを行うためのより良い (そして正しい) 方法を見つけました。BrazilianAnalyzer (およびほとんどのアナライザー) には、語幹処理 (またはファジー化) してはならないストップ ワードと単語を受け入れるオーバーロードされたコンストラクターがあります。したがって、あなたがしなければならないことは次のとおりです。

次のようにアナライザーを構築します。

new BrazilianAnalyzer(Version.LUCENE_41, stops, getNoStemmingSet(query));

その場合、getNoStemmingSet は次のようになります。

private CharArraySet getNoStemmingSet(String query) {
    if (query != null && !query.contains(" -")) {
        return new CharArraySet(Version.LUCENE_41, Collections.emptyList(), true);
    }

    List<String> proihibitedClauses = new ArrayList<String>();

    for (String clause : query.split("\\s")) {
        if (clause.startsWith("-")) {
            proihibitedClauses.add(clause.replace("-", ""));
        }
    }

    return new CharArraySet(Version.LUCENE_41, proihibitedClauses, true);
}

そのため、クエリに禁止された句 (マイナス記号) が含まれている場合、それぞれの句を取り、新しい CharArraySet の構築を無視します。

Stops は、ストップ ワードとして使用する別の CharArraySet です。独自のストップ ワード セットが必要ない場合は、次を使用してデフォルトのストップ ワードを使用できます。

BrazilianAnalyzer.getDefaultStopSet()

それでおしまい。

于 2013-03-20T00:53:34.390 に答える
0

-"somephrase"を使用したい場合は、このコードでうまくいくはずです(まだ十分にテストされていませんが、試すことができます)。

private CharArraySet getNoStemmingSet(String query) {

if (query != null && !query.contains(" -")) {
    return new CharArraySet(Version.LUCENE_41, Collections.emptyList(), true);
}

List<String> proihibitedClauses = new ArrayList<String>();
String[] quotedWords = null;

for (int i = 0; i < query.length(); i++) {
    if (query.charAt(i) == '-' && query.charAt(i+1) == '\"') {
        quotedWords = query.substring(i+2, query.indexOf('\"', i+2)).split("\\s");
        for (String quotedWord : quotedWords) {
            proihibitedClauses.add(quotedWord);
        }
    } else if (query.charAt(i) == '-') {
        if (query.indexOf(' ', i+1) > 0) {
            proihibitedClauses.add(query.substring(i+1, query.indexOf(' ', i+1)));
        } else {
            proihibitedClauses.add(query.substring(i+1, query.length()));
        }
    } else {
        continue;
    }
}

return new CharArraySet(Version.LUCENE_41, proihibitedClauses, true);
}
于 2013-03-20T19:23:26.147 に答える