package lux.index.field; import static org.junit.Assert.*; import java.io.IOException; import java.util.Iterator; import javax.xml.transform.TransformerException; import lux.Evaluator; import lux.IndexTestSupport; import lux.MultiThreadedRunner; import lux.XdmResultSet; import lux.index.XmlIndexer; import lux.index.field.FieldDefinition.Type; import net.sf.saxon.s9api.XdmItem; 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; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; /** * Tests for features related to XPathFields using the hamlet.xml dataset. */ @RunWith (MultiThreadedRunner.class) public class HamletXPathFieldTest { private static RAMDirectory dir; private static IndexTestSupport indexTestSupport; private Evaluator eval; @BeforeClass public static void setup () throws Exception { XmlIndexer indexer = new XmlIndexer (); indexer.getConfiguration().addField(new XPathField("doctype", "name(/*)", null, Store.NO, Type.STRING)); indexer.getConfiguration().addField(new XPathField("doctype-stored", "name(/*)", null, Store.YES, Type.STRING)); indexer.getConfiguration().addField(new XPathField("title", "/*/TITLE", null, Store.YES, Type.STRING)); dir = new RAMDirectory(); indexTestSupport = new IndexTestSupport ("lux/hamlet.xml", indexer, dir); } @AfterClass public static void cleanup () throws Exception { indexTestSupport.close(); // indexwriter close? dir.close(); } @Before public void init () throws CorruptIndexException, LockObtainFailedException, IOException { eval = indexTestSupport.makeEvaluator(); } @Test public void testFieldValues () throws Exception { // node argument assertEval ("PLAY", "lux:key('doctype-stored', /PLAY)"); // node from context item assertEval ("PLAY", "/PLAY/lux:key('doctype-stored')"); // no value for empty context item assertEval ("", "lux:key('doctype-stored')"); // values of field that's not stored can't be retrieved assertEval ("", "/PLAY/lux:key('doctype')"); } private void assertEval (String expectedResult, String xquery) { assertEquals (expectedResult, evalOne (xquery)); } private String evalOne (String expr) { Iterator<XdmItem> iterator = eval.evaluate (expr).iterator(); if (! iterator.hasNext()) { return ""; } return iterator.next().getStringValue(); } /** * tests ordering by relevance and by lux:key() * @throws Exception */ @Test public void testOrderByKey () throws Exception { final String PITHY_QUOTE = "\"There are more things in heaven and earth, Horatio\""; // , than are dreamt of in your philosophy String xquery = "for $doc in lux:search('" + PITHY_QUOTE + "') return $doc/*/name()"; // should be ordered in *relevance* order, which will basically be ordered by length: assertResultSequence (xquery, "LINE", "SPEECH", "SCENE", "ACT", "PLAY"); xquery = "for $doc in lux:search('" + PITHY_QUOTE + "')" + " order by lux:key('doctype', $doc) return $doc/*/name()"; // should be ordered by the names of the root elements: // ACT, LINE, PLAY, SCENE, SPEECH assertResultSequence (xquery, "ACT", "LINE", "PLAY", "SCENE", "SPEECH"); } @Test public void testOrderByEmpty () throws Exception { // get the titles of the first three docs in order by title, using the default "empty least": // these are blank String xquery = "subsequence(for $doc in collection() order by $doc/lux:key('title') " + "return string($doc/*/TITLE), 1, 3)"; assertResultSequence (xquery, "", "", ""); // get the first three non-empty titles in order by title, using the default "empty least": // this requires iterating over all the blank titles first, and discarding xquery = "subsequence(for $doc in collection() order by $doc/lux:key('title') " + "return $doc/lux:key('title'), 1, 3)"; assertResultSequence (xquery, "ACT I", "ACT II", "ACT III"); // get the first three non-empty titles in order by title, using "empty greatest": // this requires Saxon to perform the sorting since we don't implement empty greatest using Lucene xquery = "subsequence(for $doc in collection() order by $doc/lux:key('title') empty greatest " + "return string($doc/*/TITLE), 1, 3)"; assertResultSequence (xquery, "ACT I", "ACT II", "ACT III"); // Get the first three non-empty titles in reverse order by title, using the default "empty least": xquery = "subsequence(for $doc in collection() order by $doc/lux:key('title') descending " + "return $doc/lux:key('title'), 1, 2)"; assertResultSequence (xquery, "The Tragedy of Hamlet, Prince of Denmark", "SCENE VII. Another room in the castle."); } @Test public void testFieldValuesNoContext () throws Exception { try { assertResultSequence ("lux:key('title')", ""); assertFalse ("expected exception not thrown", true); } catch (Exception e) { assertTrue (e.getMessage().contains("there is no context defined")); } try { assertResultSequence ("for $doc in collection() order by lux:key('title') return $doc"); assertFalse ("expected exception not thrown", true); } catch (Exception e) { e.printStackTrace(); assertTrue (e.getMessage().contains("there is no context defined")); } } @Test public void testFieldValuesNoField () throws Exception { // no error, just return empty sequence assertResultSequence ("collection()[1]/lux:key('bogus')"); } @Test public void testAtomizingEmptySequence () throws Exception { String query = "subsequence (for $doc in collection() return string ($doc/*/TITLE), 1, 3)"; // should return titles of the first three nodes in document order assertResultSequence (query, "The Tragedy of Hamlet, Prince of Denmark", "", ""); } private void assertResultSequence (String query, String ... results) throws TransformerException { XdmResultSet resultSet = eval.evaluate(query); if (! resultSet.getErrors().isEmpty()) { throw resultSet.getErrors().get(0); } Iterator<XdmItem> iter = resultSet.iterator(); for (String result : results) { assertTrue ("too few results returned", iter.hasNext()); assertEquals (result, iter.next().getStringValue()); } assertFalse ("query returned extra results", iter.hasNext()); } }