lux.BaseSearchTest.java Source code

Java tutorial

Introduction

Here is the source code for lux.BaseSearchTest.java

Source

package lux;

import static lux.IndexTestSupport.*;
import static lux.index.IndexConfiguration.*;
import static org.junit.Assert.*;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Properties;

import javax.xml.transform.stream.StreamResult;

import lux.exception.LuxException;
import lux.index.IndexConfiguration;
import lux.index.XmlIndexer;
import lux.index.analysis.ElementVisibility;
import lux.index.field.FieldDefinition.Type;
import lux.index.field.XPathField;
import lux.xml.QName;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.query.QueryResult;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmAtomicValue;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.trans.XPathException;

import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.RAMDirectory;
import org.junit.AfterClass;

public abstract class BaseSearchTest {

    static final int MIL = 1000000;
    protected static IndexTestSupport index;
    protected static int totalDocs;

    public static void setup(String... xmlfile) throws Exception {
        XmlIndexer indexer = new XmlIndexer(
                INDEX_QNAMES | INDEX_PATHS | STORE_DOCUMENT | INDEX_FULLTEXT | STORE_TINY_BINARY);
        IndexConfiguration config = indexer.getConfiguration();
        config.addField(new XPathField("doctype", "name(/*)", null, Store.YES, Type.STRING));
        config.addField(new XPathField("actnum", "/*/@act", null, Store.YES, Type.INT));
        config.addField(new XPathField("scnlong", "/*/@scene", null, Store.YES, Type.LONG));
        config.addField(new XPathField("actstr", "/*/@act", null, Store.YES, Type.STRING));
        config.setElementVisibility("hidden", ElementVisibility.HIDDEN);
        config.setElementVisibility("name", ElementVisibility.TRANSPARENT);
        config.setElementVisibility("LINE", ElementVisibility.TRANSPARENT);
        config.setElementVisibility("SCENE", ElementVisibility.CONTAINER);

        index = new IndexTestSupport(xmlfile, indexer, new RAMDirectory());

        totalDocs = index.totalDocs;
    }

    @AfterClass
    public static void tearDown() throws Exception {
        index.close();
    }

    public BaseSearchTest() {
        super();
    }

    protected void assertResultValue(XdmResultSet results, int sceneDocCount) {
        assertEquals("incorrect query result", String.valueOf(sceneDocCount), results.iterator().next().toString());
    }

    protected XdmResultSet assertSearch(String query) throws Exception {
        return assertSearch(query, 0);
    }

    protected XdmResultSet assertSearch(String query, Integer props) throws Exception {
        return assertSearch(query, props, null);
    }

    protected XdmResultSet assertSearch(String query, Integer props, Integer docCount) throws Exception {
        return assertSearch(query, props, docCount, null);
    }

    protected XdmResultSet assertSearch(String expectedResult, String query, Integer facts, Integer docCount)
            throws Exception {
        return assertSearch(expectedResult, query, facts, docCount, null);
    }

    protected XdmResultSet assertSearch(String expectedResult, String query, Integer props, Integer docCount,
            Integer cacheMisses) throws Exception {
        XdmResultSet results = assertSearch(query, props, docCount, cacheMisses);
        if (!results.getErrors().isEmpty()) {
            throw results.getErrors().iterator().next();
        }
        boolean hasResults = results.iterator().hasNext();
        String result = null;
        if (hasResults) {
            XdmItem item = results.iterator().next();
            if (item instanceof XdmNode) {
                result = serialize(((XdmNode) item).getUnderlyingNode());
            } else {
                result = item.toString();
            }
        }
        if (expectedResult == null) {
            assertTrue("results not empty, got: " + result, !hasResults);
            return results;
        }
        assertTrue("no results", hasResults);
        if (!expectedResult.equals("__IGNORE__")) {
            assertEquals("incorrect query result", expectedResult, result);
        }
        return results;
    }

    public static String serialize(/*@NotNull*/ NodeInfo nodeInfo) throws XPathException {
        StringWriter sw = new StringWriter();
        Properties props = new Properties();
        props.setProperty("method", "xml");
        props.setProperty("indent", "no");
        props.setProperty("omit-xml-declaration", "yes");
        QueryResult.serialize(nodeInfo, new StreamResult(sw), props);
        return sw.toString();
    }

    /**
     * Executes the query, ensures that the given properties hold, and returns the result set.
     * Prints some diagnostic statistics, including total elapsed time (t) and time spent in the 
     * search result collector (tsearch), which excludes any subseuqnet evaluation of results.
     * @param query an XPath query
     * @param props properties asserted to hold for the query evaluation
     * @return the query results
     * @throws LuxException
     * @throws IOException 
     * @throws LockObtainFailedException 
     * @throws CorruptIndexException 
     */
    protected XdmResultSet assertSearch(String query, Integer props, Integer docCount, Integer cacheMisses)
            throws LuxException, CorruptIndexException, LockObtainFailedException, IOException {
        Evaluator eval = index.makeEvaluator();
        XQueryExecutable expr = eval.getCompiler().compile(query);
        QueryContext qc = new QueryContext();
        qc.bindVariable(new QName("id"), new XdmAtomicValue("test")); // bind this random id so we can use it in tests???
        XdmResultSet results = (XdmResultSet) eval.evaluate(expr, qc);
        if (!results.getErrors().isEmpty()) {
            throw new LuxException(results.getErrors().get(0).getMessage());
        }
        QueryStats stats = eval.getQueryStats();
        System.out.println(String.format("t=%d, tsearch=%d, tretrieve=%d, query=%s", stats.totalTime / MIL,
                stats.collectionTime / MIL, stats.retrievalTime / MIL, query));
        //System.out.println ("optimized query=" + eval.getCompiler().getLastOptimized());
        System.out.println(String.format("cache hits=%d, misses=%d", eval.getDocReader().getCacheHits(),
                eval.getDocReader().getCacheMisses()));
        if (props != null) {
            if ((props & QUERY_EXACT) != 0) {
                assertEquals("query is not exact", results.size(), stats.docCount);
            }
            if ((props & QUERY_CONSTANT) != 0) {
                assertEquals("query is not constant", 0, stats.docCount);
            }
            if ((props & QUERY_MINIMAL) != 0) {
                // this is not the same as minimal, but is implied by it:
                assertTrue("query is not minimal; retrieved " + stats.docCount + " docs but only got "
                        + results.size() + " results", results.size() >= stats.docCount);
                // in addition we'd need to show that every document produced at least one result
            }
            if ((props & QUERY_NO_DOCS) != 0) {
                // This only makes sense because the main cost is usually retrieving and parsing documents
                // if we spend most of our time searching (in the collector), we didn't do a lot of xquery evaluation
                assertTrue("query is not filter free", stats.retrievalTime / (stats.totalTime + 1.0) < 0.01);
            }
        }
        if (docCount != null) {
            assertEquals("incorrect document result count", docCount.intValue(), stats.docCount);
        }
        if (cacheMisses != null) {
            assertEquals("incorrect cache misses count", cacheMisses.intValue(),
                    eval.getDocReader().getCacheMisses());
        }
        return results;
    }

}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */