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/. */