Lucene Spannearquery 부분 매칭
문제
{ 'foo', 'bar', 'baz'} 문서가 주어지면 Spannearquery를 토큰 { 'baz', 'extra'}와 일치시키고 싶습니다.
그러나 이것은 실패합니다.
이걸 어떻게 가나 요?
다음 결과와 함께 샘플 테스트 (Lucene 2.9.1 사용) :
- GivensingLematch- 패스
- GiventWomatches- 패스
- GiventhReematches- 패스
- givensinglematch_andextraterm- 실패
...
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
public class SpanNearQueryTest {
private RAMDirectory directory = null;
private static final String BAZ = "baz";
private static final String BAR = "bar";
private static final String FOO = "foo";
private static final String TERM_FIELD = "text";
@Before
public void given() throws IOException {
directory = new RAMDirectory();
IndexWriter writer = new IndexWriter(
directory,
new StandardAnalyzer(Version.LUCENE_29),
IndexWriter.MaxFieldLength.UNLIMITED);
Document doc = new Document();
doc.add(new Field(TERM_FIELD, FOO, Field.Store.NO, Field.Index.ANALYZED));
doc.add(new Field(TERM_FIELD, BAR, Field.Store.NO, Field.Index.ANALYZED));
doc.add(new Field(TERM_FIELD, BAZ, Field.Store.NO, Field.Index.ANALYZED));
writer.addDocument(doc);
writer.commit();
writer.optimize();
writer.close();
}
@After
public void cleanup() {
directory.close();
}
@Test
public void givenSingleMatch() throws IOException {
SpanNearQuery spanNearQuery = new SpanNearQuery(
new SpanQuery[] {
new SpanTermQuery(new Term(TERM_FIELD, FOO))
}, Integer.MAX_VALUE, false);
TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100);
Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length);
}
@Test
public void givenTwoMatches() throws IOException {
SpanNearQuery spanNearQuery = new SpanNearQuery(
new SpanQuery[] {
new SpanTermQuery(new Term(TERM_FIELD, FOO)),
new SpanTermQuery(new Term(TERM_FIELD, BAR))
}, Integer.MAX_VALUE, false);
TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100);
Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length);
}
@Test
public void givenThreeMatches() throws IOException {
SpanNearQuery spanNearQuery = new SpanNearQuery(
new SpanQuery[] {
new SpanTermQuery(new Term(TERM_FIELD, FOO)),
new SpanTermQuery(new Term(TERM_FIELD, BAR)),
new SpanTermQuery(new Term(TERM_FIELD, BAZ))
}, Integer.MAX_VALUE, false);
TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100);
Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length);
}
@Test
public void givenSingleMatch_andExtraTerm() throws IOException {
SpanNearQuery spanNearQuery = new SpanNearQuery(
new SpanQuery[] {
new SpanTermQuery(new Term(TERM_FIELD, BAZ)),
new SpanTermQuery(new Term(TERM_FIELD, "EXTRA"))
},
Integer.MAX_VALUE, false);
TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100);
Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length);
}
}
해결책
SpannearQuery를 사용하면 서로 특정 거리 내에있는 용어를 찾을 수 있습니다.
예제 ( http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/):
Doug가 Lucene (주문 문제)을 따르는 Doug의 5 위치 내에서 Lucene을 찾고 싶다고 가정 해 봅시다. 다음과 같은 범위를 사용할 수 있습니다.
new SpanNearQuery(new SpanQuery[] {
new SpanTermQuery(new Term(FIELD, "lucene")),
new SpanTermQuery(new Term(FIELD, "doug"))},
5,
true);
Alt Text http://www.lucidimagination.com/blog/wp-content/uploads/2009/07/spanquery-dia1.png
이 샘플 텍스트에서 Lucene은 Doug의 3 세 이내입니다.
그러나 예를 들어, 내가 볼 수있는 유일한 일치는 귀하의 쿼리와 대상 문서에 "CD"가 있다는 것입니다 (그리고 모든 용어가 단일 필드에 있다는 가정을하고 있습니다). 이 경우 특수 쿼리 유형을 사용할 필요가 없습니다. 표준 메커니즘을 사용하면 동일한 필드에 동일한 용어를 포함한다는 사실에 따라 0이 아닌 가중치를 얻게됩니다.
편집 3 - 최신 의견에 대한 응답으로 답은 사용할 수 없다는 것입니다. SpanNearQuery
문서의 여러 용어가 서로 특정 수의 장소 내에서 발생하는지 여부를 찾는 것 외에는 다른 일을하는 것입니다. 특정 사용 사례 / 예상 결과가 무엇인지 알 수는 없습니다 (자유롭게 게시하십시오). 마지막 경우 하나 이상 ( "BAZ", "Extra")이 문서, a BooleanQuery
잘 작동합니다.
편집 4 - 이제 당신은 당신의 유스 케이스를 게시 했으므로, 당신이 무엇을하고 싶은지 이해합니다. 다음은 할 수있는 방법입니다. BooleanQuery
위에서 언급했듯이 원하는 개별 용어와 SpanNearQuery
, 그리고 SpanNearQuery
.
따라서 텍스트 형식의 쿼리는 다음과 같습니다.
BAZ OR EXTRA OR "BAZ EXTRA"~100^5
(예로서 - 이것은 "baz"또는 "extra"를 포함하는 모든 문서와 일치하지만 "baz"와 "extra가 서로 100 곳 내에서 엑스트라가 발생하는 문서에 더 높은 점수를 할당하고, 위치를 조정하고 이 예제는 Solr 요리 책에서 나온 것이므로 루센에서 구문 분석하지 못하거나 바람직하지 않은 결과를 줄 수 있습니다. 다음 섹션에서는 API를 사용하여이를 구축하는 방법을 보여주기 때문에 괜찮습니다).
프로그래밍 방식으로 다음과 같이 구성합니다.
Query top = new BooleanQuery();
// Construct the terms since they will be used more than once
Term bazTerm = new Term("Field", "BAZ");
Term extraTerm = new Term("Field", "EXTRA");
// Add each term as "should" since we want a partial match
top.add(new TermQuery(bazTerm), BooleanClause.Occur.SHOULD);
top.add(new TermQuery(extraTerm), BooleanClause.Occur.SHOULD);
// Construct the SpanNearQuery, with slop 100 - a document will get a boost only
// if BAZ and EXTRA occur within 100 places of each other. The final parameter means
// that BAZ must occur before EXTRA.
SpanNearQuery spanQuery = new SpanNearQuery(
new SpanQuery[] { new SpanTermQuery(bazTerm),
new SpanTermQuery(extraTerm) },
100, true);
// Give it a boost of 5 since it is more important that the words are together
spanQuery.setBoost(5f);
// Add it as "should" since we want a match even when we don't have proximity
top.add(spanQuery, BooleanClause.Occur.SHOULD);
도움이되기를 바랍니다! 앞으로, 당신이 기대하는 결과를 정확하게 게시하여 시작하려고 노력하십시오. 그것이 당신에게 분명하더라도 독자에게는 그렇지 않을 수도 있고, 명백한 것은 여러 번 앞뒤로 갈 필요가 없습니다.