package querqy.lucene.rewrite; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import querqy.lucene.rewrite.BooleanQueryFactory.Clause; import querqy.lucene.rewrite.cache.CacheKey; import querqy.lucene.rewrite.cache.TermQueryCache; import querqy.lucene.rewrite.cache.TermQueryCacheValue; import querqy.lucene.rewrite.prms.PRMSAndQuery; import querqy.lucene.rewrite.prms.PRMSDisjunctionMaxQuery; import querqy.lucene.rewrite.prms.PRMSQuery; import querqy.lucene.rewrite.prms.PRMSTermQuery; import querqy.rewrite.commonrules.model.PositionSequence; public class TermSubQueryBuilderTest { static final Analyzer ANALYZER = new StandardAnalyzer(); TermQueryCache cache = Mockito.mock(TermQueryCache.class); @Before public void setUp() throws Exception { when(cache.get(any(CacheKey.class))).thenReturn(null); } @Test public void testNoTerm() throws Exception { TermSubQueryBuilder builder = new TermSubQueryBuilder(ANALYZER, null); PositionSequence<org.apache.lucene.index.Term> sequence = new PositionSequence<>(); assertNull(builder.positionSequenceToQueryFactoryAndPRMS(sequence)); sequence.nextPosition(); assertNull(builder.positionSequenceToQueryFactoryAndPRMS(sequence)); } @Test public void testSingleTerm() { TermSubQueryBuilder builder = new TermSubQueryBuilder(ANALYZER, null); PositionSequence<org.apache.lucene.index.Term> sequence = new PositionSequence<>(); sequence.nextPosition(); Term term = new Term("f", "a"); sequence.addElement(term); LuceneQueryFactoryAndPRMSQuery lap = builder.positionSequenceToQueryFactoryAndPRMS(sequence); assertThat( lap, lap(tqf(term), prmsTq(term)) ); } @Test public void testTwoTermsAtSinglePosition() throws Exception { TermSubQueryBuilder builder = new TermSubQueryBuilder(ANALYZER, null); PositionSequence<org.apache.lucene.index.Term> sequence = new PositionSequence<>(); sequence.nextPosition(); Term term1 = new Term("f", "a"); sequence.addElement(term1); Term term2 = new Term("f", "b"); sequence.addElement(term2); LuceneQueryFactoryAndPRMSQuery lap = builder.positionSequenceToQueryFactoryAndPRMS(sequence); assertThat( lap, lap( dmqf( tqf(term1), tqf(term2) ), prmsDmq( prmsTq(term1), prmsTq(term2)) ) ); } @Test public void testTwoTermsAtTwoPositions() throws Exception { TermSubQueryBuilder builder = new TermSubQueryBuilder(ANALYZER, null); PositionSequence<org.apache.lucene.index.Term> sequence = new PositionSequence<>(); sequence.nextPosition(); Term term1 = new Term("f", "a"); sequence.addElement(term1); sequence.nextPosition(); Term term2 = new Term("f", "b"); sequence.addElement(term2); LuceneQueryFactoryAndPRMSQuery lap = builder.positionSequenceToQueryFactoryAndPRMS(sequence); assertThat( lap, lap( bqf( tqf(term1), tqf(term2) ), prmsAnd( prmsTq(term1), prmsTq(term2)) ) ); } @Test public void testTwoTermsAtFirstAndOneTermAtSecondPosition() throws Exception { TermSubQueryBuilder builder = new TermSubQueryBuilder(ANALYZER, null); PositionSequence<org.apache.lucene.index.Term> sequence = new PositionSequence<>(); sequence.nextPosition(); Term term1_1 = new Term("f", "a1"); sequence.addElement(term1_1); Term term1_2 = new Term("f", "a2"); sequence.addElement(term1_2); sequence.nextPosition(); Term term2 = new Term("f", "b"); sequence.addElement(term2); LuceneQueryFactoryAndPRMSQuery lap = builder.positionSequenceToQueryFactoryAndPRMS(sequence); assertThat( lap, lap( dmqf( tqf(term1_1), bqf( tqf(term1_2), tqf(term2) ) ), prmsDmq( prmsTq(term1_1), prmsAnd( prmsTq(term1_2), prmsTq(term2)) ) ) ); } @Test public void testThatDeletingAllTermsInLuceneAnalysisDoesNotCauseException() throws Exception { TermSubQueryBuilder builder = new TermSubQueryBuilder(ANALYZER, cache); PositionSequence<org.apache.lucene.index.Term> sequence = new PositionSequence<>(); sequence.nextPosition(); querqy.model.Term term = new querqy.model.Term(null, "f", ".", false); builder.termToFactory("f", term, ConstantFieldBoost.NORM_BOOST); verify(cache, never()).put(any(CacheKey.class), any(TermQueryCacheValue.class)); } public TQFMatcher tqf(Term term) { return new TQFMatcher(term); } @SafeVarargs public final DMQFMatcher dmqf(TypeSafeMatcher<LuceneQueryFactory<?>>... disjuncts) { return new DMQFMatcher(disjuncts); } @SafeVarargs public final BQFMatcher bqf(TypeSafeMatcher<LuceneQueryFactory<?>>... clauses) { return new BQFMatcher(clauses); } public PRMSTermQueryMatcher prmsTq(Term term) { return new PRMSTermQueryMatcher(term); } @SafeVarargs public final PRMSDisjunctionMaxQueryMatcher prmsDmq(TypeSafeMatcher<PRMSQuery>... disjuncts) { return new PRMSDisjunctionMaxQueryMatcher(disjuncts); } @SafeVarargs public final PRMSAndQueryMatcher prmsAnd(TypeSafeMatcher<PRMSQuery>... clauses) { return new PRMSAndQueryMatcher(clauses); } public LuceneQueryFactoryAndPRMSQueryMatcher lap(TypeSafeMatcher<LuceneQueryFactory<?>> factory, TypeSafeMatcher<PRMSQuery> prmsQuery) { return new LuceneQueryFactoryAndPRMSQueryMatcher(factory, prmsQuery); } class LuceneQueryFactoryAndPRMSQueryMatcher extends TypeSafeMatcher<LuceneQueryFactoryAndPRMSQuery> { final TypeSafeMatcher<LuceneQueryFactory<?>> factory; final TypeSafeMatcher<PRMSQuery> prmsQuery; public LuceneQueryFactoryAndPRMSQueryMatcher(TypeSafeMatcher<LuceneQueryFactory<?>> factory, TypeSafeMatcher<PRMSQuery> prmsQuery) { this.factory = factory; this.prmsQuery = prmsQuery; } @Override public void describeTo(Description description) { description.appendText("LuceneQueryFactoryAndPRMSQuery: ") .appendDescriptionOf(factory) .appendDescriptionOf(prmsQuery); } @Override protected boolean matchesSafely(LuceneQueryFactoryAndPRMSQuery luceneQueryFactoryAndPRMSQuery) { return factory.matches(luceneQueryFactoryAndPRMSQuery.queryFactory) && prmsQuery.matches(luceneQueryFactoryAndPRMSQuery.prmsQuery); } } class DMQFMatcher extends TypeSafeMatcher<LuceneQueryFactory<?>> { final TypeSafeMatcher<LuceneQueryFactory<?>>[] disjuncts; @SafeVarargs public DMQFMatcher(TypeSafeMatcher<LuceneQueryFactory<?>>... disjuncts) { super(DisjunctionMaxQueryFactory.class); this.disjuncts = disjuncts; } @Override public void describeTo(Description description) { description.appendText("DMQF: "); for (TypeSafeMatcher<LuceneQueryFactory<?>> disjunct: disjuncts) { description.appendDescriptionOf(disjunct); } } @Override protected boolean matchesSafely(LuceneQueryFactory<?> factory) { DisjunctionMaxQueryFactory dmqf = (DisjunctionMaxQueryFactory) factory; for (TypeSafeMatcher<LuceneQueryFactory<?>> disjunct : disjuncts) { boolean found = false; for (LuceneQueryFactory<?> qf : dmqf.disjuncts) { found = disjunct.matches(qf); if (found) { break; } } if (!found) { return false; } } return true; } } class BQFMatcher extends TypeSafeMatcher<LuceneQueryFactory<?>> { final TypeSafeMatcher<LuceneQueryFactory<?>>[] clauses; @SafeVarargs public BQFMatcher(TypeSafeMatcher<LuceneQueryFactory<?>>... clauses) { super(BooleanQueryFactory.class); this.clauses = clauses; } @Override public void describeTo(Description description) { description.appendText("BQF: "); for (TypeSafeMatcher<LuceneQueryFactory<?>> clause: clauses) { description.appendDescriptionOf(clause); } } @Override protected boolean matchesSafely(LuceneQueryFactory<?> factory) { BooleanQueryFactory bqf = (BooleanQueryFactory) factory; for (TypeSafeMatcher<LuceneQueryFactory<?>> clauseMatcher : clauses) { boolean found = false; for (Clause clause : bqf.getClauses()) { if (clause.occur != BooleanClause.Occur.MUST) { return false; } found = clauseMatcher.matches(clause.queryFactory); if (found) { break; } } if (!found) { return false; } } return true; } } class TQFMatcher extends TypeSafeMatcher<LuceneQueryFactory<?>> { final Term term; public TQFMatcher(Term term) { super(TermQueryFactory.class); this.term = term; } @Override public void describeTo(Description description) { description.appendText("TQF term: " + term); } @Override protected boolean matchesSafely(LuceneQueryFactory<?> factory) { TermQueryFactory tqf = ((TermQueryFactory) factory); return term.equals(tqf.term); } } class PRMSTermQueryMatcher extends TypeSafeMatcher<PRMSQuery> { final Term term; public PRMSTermQueryMatcher(Term term) { super(PRMSTermQuery.class); this.term = term; } @Override public void describeTo(Description description) { description.appendText("PRMSTermQuery term: " + term); } @Override protected boolean matchesSafely(PRMSQuery query) { PRMSTermQuery prmsTq = ((PRMSTermQuery) query); return term.equals(prmsTq.getTerm()); } } class PRMSDisjunctionMaxQueryMatcher extends TypeSafeMatcher<PRMSQuery> { final TypeSafeMatcher<PRMSQuery>[] disjuncts; @SafeVarargs public PRMSDisjunctionMaxQueryMatcher(TypeSafeMatcher<PRMSQuery>... disjuncts) { super(PRMSDisjunctionMaxQuery.class); this.disjuncts = disjuncts; } @Override public void describeTo(Description description) { description.appendText("PRMSDisjunctionMaxQuery: "); for (TypeSafeMatcher<PRMSQuery> disjunct: disjuncts) { description.appendDescriptionOf(disjunct); } } @Override protected boolean matchesSafely(PRMSQuery query) { PRMSDisjunctionMaxQuery prmsDmq = ((PRMSDisjunctionMaxQuery) query); for (TypeSafeMatcher<PRMSQuery> disjunct : disjuncts) { boolean found = false; for (PRMSQuery q : prmsDmq.getDisjuncts()) { found = disjunct.matches(q); if (found) { break; } } if (!found) { return false; } } return true; } } class PRMSAndQueryMatcher extends TypeSafeMatcher<PRMSQuery> { final TypeSafeMatcher<PRMSQuery>[] clauses; @SafeVarargs public PRMSAndQueryMatcher(TypeSafeMatcher<PRMSQuery>... clauses) { super(PRMSAndQuery.class); this.clauses = clauses; } @Override public void describeTo(Description description) { description.appendText("PRMSAndQuery: "); for (TypeSafeMatcher<PRMSQuery> clause: clauses) { description.appendDescriptionOf(clause); } } @Override protected boolean matchesSafely(PRMSQuery query) { PRMSAndQuery prmsBq = ((PRMSAndQuery) query); for (int i = 0; i < clauses.length; i++) { boolean found = false; for (PRMSQuery q : prmsBq.getClauses()) { found = clauses[i].matches(q); if (found) { break; } } if (!found) { return false; } } return true; } } }