package lux.solr; import org.apache.solr.BaseDistributedSearchTestCase; /** * Basic test of Lux operation in a distributed ("cloud") setup. Inserts some test * documents and performs basic queries: ordered (by docid), sorted by field, and * sorted by relevance. TODO: Test both query parsers (user-supplied lux:search(string)). */ public class CloudTest extends BaseDistributedSearchTestCase { public CloudTest () { // shard by hashing the uri id = "lux_uri"; fixShardCount = true; shardCount = 2; } @Override public String getSolrHome () { return "zk-solr"; } @Override public void doTest() throws Exception { del("*:*"); CloudIndexSupport indexSupport = new CloudIndexSupport(controlClient, clients); indexSupport.setDocLimit(500); indexSupport.indexAllElements("lux/hamlet.xml"); // exclude certain result components from comparisons: initComparisonRegime(); // In the cloud, we run a full search and then discard unused docs in the retrieved page, // so response.numFound == num docs matching the query. // In local operation, we only retrieve as many docs as we need, and terminate the search // early, so we don't compute numFound, but just report the number we actually looked at // Is this going to be a problem? // We can't skip only response.numFound :( but the only other things in there are start // and docs[] anyway. handle.put("response", SKIP); // in cloud only // Note the "control" ie the non-cloud index is listed *second* in the failed comparison // logged if an assert fails // OK query("qt", "/xquery", "q", "/FM"); // Document ordering will only be the same in cloud if timestamps are very exactly synchronized across shards; // so this works in test on a single node, but is not guaranteed in general: // query("qt", "/xquery", "q", "collection()[250]/base-uri()"); query("qt", "/xquery", "q", "(for $doc in collection() order by $doc/lux:key('lux_uri')) return $doc)[250]/base-uri()"); query("qt", "/xquery", "q", "(//SPEECH)[250]"); // order by lux:key() FIXME // query ("qt", "/xquery", "q", "(for $sp in /SPEECH order by $sp/lux:key('title') return $sp)[30]"); // Test order by int-valued key, and make sure that the order is numeric, not string query ("qt", "/xquery", "q", "subsequence(for $doc in collection() order by $doc/lux:key('lux_docid') return $doc/base-uri(), 1, 20)"); query ("qt", "/xquery", "q", "subsequence(for $doc in collection() order by $doc/lux:key('lux_uri') return $doc/lux:key('title'), 1, 20)"); query ("qt", "/xquery", "q", "subsequence(for $doc in collection() order by $doc/lux:key('lux_docid') return $doc/lux:key('title'), 1, 20)"); query ("qt", "/xquery", "q", "(for $doc in collection() order by $doc/lux:key('lux_docid') return $doc/lux:key('title'))"); // order by value query ("qt", "/xquery", "q", "(for $act in /ACT order by $act/@act descending return $act/TITLE)[1]"); // join two queries query ("qt", "/xquery", "q", "count(for $act in /ACT, $actdesc in //ACT return $act is $actdesc)"); // runtime error (no context item) query ("qt", "/xquery", "q", "(for $sp in //SPEECH return .)[30]"); // runtime error #2 : multi-valued sortkey // FIXME: this does not cause an error in the single-server case // and it causes a weird exception in the cloud case // query ("qt", "/xquery", "q", "(for $sp in //SPEECH order by $sp/lux:key('title_multi') return $sp)[30]"); // lux:count() query ("qt", "/xquery", "q", "count(collection())"); query ("qt", "/xquery", "q", "count(/SPEECH)"); // lux:exists() query ("qt", "/xquery", "q", "exists(/ACT)"); // test an expression dependent on document ordering // StackOverflow in net.sf.saxon.expr.ForExpression.optimize()!!! This is filed as Saxon bug #1910; see saxonica.plan.io // query ("qt", "/xquery", "q", "count(for $act in /ACT, $actdesc in //ACT return $act intersect $actdesc)"); // some tests that rely on document identity and ordering: query("qt", "/xquery", "q", "count(//SPEECH[contains(., 'philosophy')] intersect /SPEECH[contains(., 'mercy')])"); query("qt", "/xquery", "q", "count(/ACT/SCENE intersect subsequence(//SCENE, 1, 31))"); // testing doc(): query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-4')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-10')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-439')"); // lux:key(): query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-4')/lux:key('lux_uri')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-10')/lux:key('lux_uri')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-10')/lux:key('nonexistent')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-439')/lux:key('title')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-439')/lux:key('actnum')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-439')/lux:key('scnlong')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-439')/lux:key('doctype')"); query("qt", "/xquery", "q", "doc('lux://lux/hamlet.xml-439')/lux:key('title')"); // TODO: lux:field-terms() query("qt", "/xquery", "q", "subsequence(lux:field-terms('title'), 1, 20)"); query("qt", "/xquery", "q", "subsequence(lux:field-terms('title'), 200, 300)"); query("qt", "/xquery", "q", "subsequence(lux:field-terms('title', 'M'), 1, 10)"); query("qt", "/xquery", "q", "subsequence(lux:field-terms('title', 'M'), 100, 30)"); query("qt", "/xquery", "q", "lux:field-terms('doctype')"); } private void initComparisonRegime() { // set some fields to ignore when comparing query results handle.clear(); handle.put("QTime", SKIPVAL); handle.put("timestamp", SKIPVAL); handle.put("_version_", SKIPVAL); handle.put("shards", SKIP); // in cloud response only handle.put("distrib", SKIP); // in control only handle.put("maxScore", SKIPVAL); // in cloud only } }