/** * Copyright 2014 National University of Ireland, Galway. * * This file is part of the SIREn project. Project and contact information: * * https://github.com/rdelbru/SIREn * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sindice.siren.search; import static org.sindice.siren.search.AbstractTestSirenScorer.NodeTermQueryBuilder.ntq; import java.io.IOException; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.sindice.siren.index.DocsAndNodesIterator; import org.sindice.siren.search.node.LuceneProxyNodeQuery; import org.sindice.siren.search.node.MultiNodeTermQuery.RewriteMethod; import org.sindice.siren.search.node.NodeBooleanClause; import org.sindice.siren.search.node.NodeBooleanClause.Occur; import org.sindice.siren.search.node.NodeBooleanQuery; import org.sindice.siren.search.node.NodeNumericRangeQuery; import org.sindice.siren.search.node.NodePhraseQuery; import org.sindice.siren.search.node.NodePrimitiveQuery; import org.sindice.siren.search.node.NodeQuery; import org.sindice.siren.search.node.NodeScorer; import org.sindice.siren.search.node.NodeTermQuery; import org.sindice.siren.search.node.TupleQuery; import org.sindice.siren.search.node.TwigQuery; import org.sindice.siren.search.node.TwigQuery.EmptyRootQuery; import org.sindice.siren.util.BasicSirenTestCase; public abstract class AbstractTestSirenScorer extends BasicSirenTestCase { public static LuceneProxyNodeQuery dq(final NodeQuery nq) { return new LuceneProxyNodeQuery(nq); } protected NodeScorer getScorer(final NodeQueryBuilder builder) throws IOException { return (NodeScorer) this.getScorer(builder.getNodeQuery()); } protected Scorer getScorer(final Query query) throws IOException { final Weight weight = searcher.createNormalizedWeight(query); assertTrue(searcher.getTopReaderContext() instanceof AtomicReaderContext); final AtomicReaderContext context = (AtomicReaderContext) searcher.getTopReaderContext(); return weight.scorer(context, true, true, context.reader().getLiveDocs()); } public static abstract class LuceneQueryBuilder { public abstract Query getQuery(); } public static abstract class NodeQueryBuilder { public NodeQueryBuilder bound(final int lowerBound, final int upperBound) { this.getNodeQuery().setNodeConstraint(lowerBound, upperBound); return this; } public NodeQueryBuilder level(final int level) { this.getNodeQuery().setLevelConstraint(level); return this; } public abstract NodeQuery getNodeQuery(); public abstract Query getLuceneProxyQuery(); /** * Should be implemented only by {@link NodePrimitiveQuery} builders */ public NodeQueryBuilder setDatatype(final String datatype) { throw new UnsupportedOperationException(); } } public static class NodeNumericRangeQueryBuilder extends NodeQueryBuilder { protected final NodeNumericRangeQuery<? extends Number> nmq; public NodeNumericRangeQueryBuilder setRewriteMethod(final RewriteMethod method) { nmq.setRewriteMethod(method); return this; } private NodeNumericRangeQueryBuilder(final String field, final int precisionStep, final Integer min, final Integer max, final boolean minInclusive, final boolean maxInclusive) { nmq = NodeNumericRangeQuery .newIntRange(field, precisionStep, min, max, minInclusive, maxInclusive); } private NodeNumericRangeQueryBuilder(final String field, final int precisionStep, final Float min, final Float max, final boolean minInclusive, final boolean maxInclusive) { nmq = NodeNumericRangeQuery .newFloatRange(field, precisionStep, min, max, minInclusive, maxInclusive); } private NodeNumericRangeQueryBuilder(final String field, final int precisionStep, final Double min, final Double max, final boolean minInclusive, final boolean maxInclusive) { nmq = NodeNumericRangeQuery .newDoubleRange(field, precisionStep, min, max, minInclusive, maxInclusive); } private NodeNumericRangeQueryBuilder(final String field, final int precisionStep, final Long min, final Long max, final boolean minInclusive, final boolean maxInclusive) { nmq = NodeNumericRangeQuery .newLongRange(field, precisionStep, min, max, minInclusive, maxInclusive); } public static NodeNumericRangeQueryBuilder nmqInt(final String field, final int precisionStep, final Integer min, final Integer max, final boolean minInclusive, final boolean maxInclusive) { return new NodeNumericRangeQueryBuilder(field, precisionStep, min, max, minInclusive, maxInclusive); } public static NodeNumericRangeQueryBuilder nmqFloat(final String field, final int precisionStep, final Float min, final Float max, final boolean minInclusive, final boolean maxInclusive) { return new NodeNumericRangeQueryBuilder(field, precisionStep, min, max, minInclusive, maxInclusive); } public static NodeNumericRangeQueryBuilder nmqDouble(final String field, final int precisionStep, final Double min, final Double max, final boolean minInclusive, final boolean maxInclusive) { return new NodeNumericRangeQueryBuilder(field, precisionStep, min, max, minInclusive, maxInclusive); } public static NodeNumericRangeQueryBuilder nmqLong(final String field, final int precisionStep, final Long min, final Long max, final boolean minInclusive, final boolean maxInclusive) { return new NodeNumericRangeQueryBuilder(field, precisionStep, min, max, minInclusive, maxInclusive); } @Override public NodeQuery getNodeQuery() { return nmq; } @Override public Query getLuceneProxyQuery() { return new LuceneProxyNodeQuery(nmq); } @Override public NodeQueryBuilder setDatatype(final String datatype) { nmq.setDatatype(datatype); return this; } } public static class NodeTermQueryBuilder extends NodeQueryBuilder { protected final NodeTermQuery ntq; private NodeTermQueryBuilder(final String fieldName, final String term) { final Term t = new Term(fieldName, term); ntq = new NodeTermQuery(t); } public static NodeTermQueryBuilder ntq(final String term) { return new NodeTermQueryBuilder(DEFAULT_TEST_FIELD, term); } @Override public NodeQuery getNodeQuery() { return ntq; } @Override public Query getLuceneProxyQuery() { return new LuceneProxyNodeQuery(ntq); } @Override public NodeQueryBuilder setDatatype(final String datatype) { ntq.setDatatype(datatype); return this; } } public static class NodePhraseQueryBuilder extends NodeQueryBuilder { protected final NodePhraseQuery npq; private NodePhraseQueryBuilder(final String fieldName, final String[] terms) { npq = new NodePhraseQuery(); for (int i = 0; i < terms.length; i++) { if (terms[i].isEmpty()) { // if empty string, skip it continue; } final Term t = new Term(fieldName, terms[i]); npq.add(t, i); } } /** * If term is equal to an empty string, this is considered as a position * gap. */ public static NodePhraseQueryBuilder npq(final String ... terms) { return npq(DEFAULT_TEST_FIELD, terms); } /** * If term is equal to an empty string, this is considered as a position * gap. * The field value is passed as an argument */ public static NodePhraseQueryBuilder npq(final String field, final String[] terms) { return new NodePhraseQueryBuilder(field, terms); } @Override public NodeQuery getNodeQuery() { return npq; } @Override public Query getLuceneProxyQuery() { return new LuceneProxyNodeQuery(npq); } @Override public NodeQueryBuilder setDatatype(final String datatype) { npq.setDatatype(datatype); return this; } } public static class BooleanClauseBuilder { public static BooleanBag must(final NodeQueryBuilder builder) { return BooleanBag.must(builder.getNodeQuery()); } public static BooleanBag must(final LuceneQueryBuilder... builders) { final Query[] queries = new Query[builders.length]; for (int i = 0; i < builders.length; i++) { queries[i] = builders[i].getQuery(); } return BooleanBag.must(queries); } public static BooleanBag must(final String term) { return BooleanBag.must(ntq(term).ntq); } public static BooleanBag must(final String... terms) { final Query[] queries = new Query[terms.length]; for (int i = 0; i < terms.length; i++) { queries[i] = ntq(terms[i]).ntq; } return BooleanBag.must(queries); } public static BooleanBag should(final NodeQueryBuilder builder) { return BooleanBag.should(builder.getNodeQuery()); } public static BooleanBag should(final LuceneQueryBuilder... builders) { final Query[] queries = new Query[builders.length]; for (int i = 0; i < builders.length; i++) { queries[i] = builders[i].getQuery(); } return BooleanBag.should(queries); } public static BooleanBag should(final String term) { return BooleanBag.should(ntq(term).ntq); } public static BooleanBag should(final String ... terms) { final Query[] queries = new Query[terms.length]; for (int i = 0; i < terms.length; i++) { queries[i] = ntq(terms[i]).ntq; } return BooleanBag.should(queries); } public static BooleanBag not(final NodeQueryBuilder builder) { return BooleanBag.not(builder.getNodeQuery()); } public static BooleanBag not(final LuceneQueryBuilder... builders) { final Query[] queries = new Query[builders.length]; for (int i = 0; i < builders.length; i++) { queries[i] = builders[i].getQuery(); } return BooleanBag.not(queries); } public static BooleanBag not(final String term) { return BooleanBag.not(ntq(term).ntq); } public static BooleanBag not(final String ... terms) { final Query[] queries = new Query[terms.length]; for (int i = 0; i < terms.length; i++) { queries[i] = ntq(terms[i]).ntq; } return BooleanBag.not(queries); } } public static class NodeBooleanQueryBuilder extends NodeQueryBuilder { protected NodeBooleanQuery nbq; private NodeBooleanQueryBuilder(final BooleanBag[] clauses) { nbq = new NodeBooleanQuery(); for (final BooleanBag bag : clauses) { for (final NodeBooleanClause clause : bag.toNodeBooleanClauses()) { nbq.add(clause); } } } public static NodeBooleanQueryBuilder nbq(final BooleanBag ... clauses) { return new NodeBooleanQueryBuilder(clauses); } @Override public NodeQuery getNodeQuery() { return nbq; } @Override public NodeBooleanQueryBuilder bound(final int lowerBound, final int upperBound) { return (NodeBooleanQueryBuilder) super.bound(lowerBound, upperBound); } @Override public Query getLuceneProxyQuery() { return new LuceneProxyNodeQuery(nbq); } } public static class BooleanQueryBuilder extends LuceneQueryBuilder { protected BooleanQuery bq; private BooleanQueryBuilder(final BooleanBag[] clauses) { bq = new BooleanQuery(); for (final BooleanBag bag : clauses) { for (final BooleanClause clause : bag.toBooleanClauses()) { bq.add(clause); } } } public static BooleanQueryBuilder bq(final BooleanBag... clauses) { return new BooleanQueryBuilder(clauses); } @Override public Query getQuery() { return bq; } } public static class TwigQueryBuilder extends NodeQueryBuilder { protected TwigQuery twq; private TwigQueryBuilder(final int rootLevel, final NodeQueryBuilder builder) { twq = new TwigQuery(rootLevel); twq.addRoot(builder.getNodeQuery()); } private TwigQueryBuilder(final int rootLevel) { twq = new TwigQuery(rootLevel); } public static TwigQueryBuilder twq(final int rootLevel, final BooleanBag ... clauses) { return new TwigQueryBuilder(rootLevel, NodeBooleanQueryBuilder.nbq(clauses)); } public static TwigQueryBuilder twq(final int rootLevel) { return new TwigQueryBuilder(rootLevel); } public TwigQueryBuilder root(final NodeQueryBuilder root) { if (!(twq.getRoot() instanceof EmptyRootQuery)) { throw new IllegalArgumentException("The root is already set: " + twq.getRoot()); } twq.addRoot(root.getNodeQuery()); return this; } public TwigQueryBuilder with(final NodeQueryBuilder nq) { twq.addChild(nq.getNodeQuery(), Occur.MUST); return this; } public TwigQueryBuilder without(final NodeQueryBuilder nq) { twq.addChild(nq.getNodeQuery(), Occur.MUST_NOT); return this; } public TwigQueryBuilder optional(final NodeQueryBuilder nq) { twq.addChild(nq.getNodeQuery(), Occur.SHOULD); return this; } public TwigQueryBuilder with(final TwigChildBuilder child) { twq.addChild(child.nbq, Occur.MUST); return this; } public TwigQueryBuilder without(final TwigChildBuilder child) { twq.addChild(child.nbq, Occur.MUST_NOT); return this; } public TwigQueryBuilder optional(final TwigChildBuilder child) { twq.addChild(child.nbq, Occur.SHOULD); return this; } public TwigQueryBuilder with(final TwigDescendantBuilder desc) { twq.addDescendant(desc.level, desc.nbq, Occur.MUST); return this; } public TwigQueryBuilder without(final TwigDescendantBuilder desc) { twq.addDescendant(desc.level, desc.nbq, Occur.MUST_NOT); return this; } public TwigQueryBuilder optional(final TwigDescendantBuilder desc) { twq.addDescendant(desc.level, desc.nbq, Occur.SHOULD); return this; } @Override public NodeQuery getNodeQuery() { return twq; } @Override public Query getLuceneProxyQuery() { return new LuceneProxyNodeQuery(twq); } } public static class TwigChildBuilder { NodeBooleanQuery nbq; private TwigChildBuilder(final BooleanBag[] clauses) { nbq = NodeBooleanQueryBuilder.nbq(clauses).nbq; } public static TwigChildBuilder child(final BooleanBag ... clauses) { return new TwigChildBuilder(clauses); } } public static class TwigDescendantBuilder { int level; NodeBooleanQuery nbq; private TwigDescendantBuilder(final int level, final BooleanBag[] clauses) { this.level = level; nbq = NodeBooleanQueryBuilder.nbq(clauses).nbq; } public static TwigDescendantBuilder desc(final int level, final BooleanBag ... clauses) { return new TwigDescendantBuilder(level, clauses); } } public static class TupleQueryBuilder extends NodeQueryBuilder { protected TupleQuery tq; private TupleQueryBuilder() { tq = new TupleQuery(true); } private TupleQueryBuilder(final int rootLevel) { tq = new TupleQuery(rootLevel, true); } public static TupleQueryBuilder tuple() { return new TupleQueryBuilder(); } public static TupleQueryBuilder tuple(final int rootLevel) { return new TupleQueryBuilder(rootLevel); } public TupleQueryBuilder with(final NodeBooleanQueryBuilder ... clauses) { for (final NodeBooleanQueryBuilder clause : clauses) { tq.add(clause.nbq, Occur.MUST); } return this; } public TupleQueryBuilder without(final NodeBooleanQueryBuilder ... clauses) { for (final NodeBooleanQueryBuilder clause : clauses) { tq.add(clause.nbq, Occur.MUST_NOT); } return this; } public TupleQueryBuilder optional(final NodeBooleanQueryBuilder ... clauses) { for (final NodeBooleanQueryBuilder clause : clauses) { tq.add(clause.nbq, Occur.SHOULD); } return this; } @Override public NodeQuery getNodeQuery() { return tq; } @Override public Query getLuceneProxyQuery() { return new LuceneProxyNodeQuery(tq); } } /** * Assert if a scorer reaches end of stream, and check if sentinel values are * set. */ public static void assertEndOfStream(final NodeScorer scorer) throws IOException { assertFalse(scorer.nextCandidateDocument()); assertEquals(DocsAndNodesIterator.NO_MORE_DOC, scorer.doc()); assertFalse(scorer.nextNode()); assertEquals(DocsAndNodesIterator.NO_MORE_NOD, scorer.node()); } }