package org.exist.fulltext; import org.custommonkey.xmlunit.NamespaceContext; import org.custommonkey.xmlunit.SimpleNamespaceContext; import org.custommonkey.xmlunit.XMLAssert; import org.custommonkey.xmlunit.XMLUnit; import org.exist.TestUtils; import org.exist.collections.Collection; import org.exist.collections.CollectionConfigurationManager; import org.exist.collections.IndexInfo; import org.exist.security.xacml.AccessContext; import org.exist.storage.BrokerPool; import org.exist.storage.DBBroker; import org.exist.storage.serializers.EXistOutputKeys; import org.exist.storage.serializers.Serializer; import org.exist.storage.txn.TransactionManager; import org.exist.storage.txn.Txn; import org.exist.test.TestConstants; import org.exist.util.Configuration; import org.exist.util.ConfigurationHelper; import org.exist.xmldb.XmldbURI; import org.exist.xquery.XPathException; import org.exist.xquery.XQuery; import org.exist.xquery.value.NodeValue; import org.exist.xquery.value.Sequence; import org.junit.AfterClass; import static org.junit.Assert.*; import org.junit.BeforeClass; import org.junit.Test; import org.xml.sax.SAXException; import javax.xml.transform.OutputKeys; import java.io.File; import java.util.HashMap; import java.util.Properties; public class FTMatchListenerTest { private static String XML = "<root>" + " <para>some paragraph with <hi>mixed</hi> content.</para>" + " <para>another paragraph with <note><hi>nested</hi> inner</note> elements.</para>" + " <para>a third paragraph with <term>term</term>.</para>" + " <para>double match double match</para>" + "</root>"; private static String CONF1 = "<collection xmlns=\"http://exist-db.org/collection-config/1.0\">" + " <index>" + " <fulltext default=\"none\">" + " <include path=\"//para\"/>" + " </fulltext>" + " </index>" + "</collection>"; private static String CONF2 = "<collection xmlns=\"http://exist-db.org/collection-config/1.0\">" + " <index>" + " <fulltext default=\"none\">" + " <create qname=\"para\"/>" + " <create qname=\"term\"/>" + " </fulltext>" + " </index>" + "</collection>"; private static String MATCH_START = "<exist:match xmlns:exist=\"http://exist.sourceforge.net/NS/exist\">"; private static String MATCH_END = "</exist:match>"; private static BrokerPool pool; /** * Test match highlighting for index configured by path, e.g. * <include path="//a/b"/>. */ @Test public void indexByPath() { DBBroker broker = null; try { configureAndStore(CONF1); broker = pool.get(org.exist.security.SecurityManager.SYSTEM_USER); XQuery xquery = broker.getXQueryService(); assertNotNull(xquery); Sequence seq = xquery.execute("//para[. &= 'mixed']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); String result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>some paragraph with <hi>" + MATCH_START + "mixed" + MATCH_END + "</hi> content.</para>", result); seq = xquery.execute("//para[hi &= 'mixed']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>some paragraph with <hi>" + MATCH_START + "mixed" + MATCH_END + "</hi> content.</para>", result); seq = xquery.execute("//para[. &= 'another']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>" + MATCH_START + "another" + MATCH_END + " paragraph with <note><hi>nested</hi> " + "inner</note> elements.</para>", result); seq = xquery.execute("//para[. &= 'nested inner']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>another paragraph with <note><hi>" + MATCH_START + "nested" + MATCH_END + "</hi> " + MATCH_START + "inner" + MATCH_END + "</note> elements.</para>", result); seq = xquery.execute("//para[. &= 'nested inner elements']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>another paragraph with <note><hi>" + MATCH_START + "nested" + MATCH_END + "</hi> " + MATCH_START + "inner" + MATCH_END + "</note> " + MATCH_START + "elements" + MATCH_END + ".</para>", result); seq = xquery.execute( "for $para in //para[. &= 'nested inner elements'] return\n" + " <hit>{$para}</hit>", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<hit><para>another paragraph with <note><hi>" + MATCH_START + "nested" + MATCH_END + "</hi> " + MATCH_START + "inner" + MATCH_END + "</note> " + MATCH_START + "elements" + MATCH_END + ".</para></hit>", result); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } finally { pool.release(broker); } } @Test public void ancestorAxis() { DBBroker broker = null; try { configureAndStore(CONF1); broker = pool.get(org.exist.security.SecurityManager.SYSTEM_USER); XQuery xquery = broker.getXQueryService(); assertNotNull(xquery); Sequence seq = xquery.execute("//hi[. &= 'mixed']/parent::para", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); String result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertXpathEvaluatesTo("1", "count(//exist:match)", result); seq = xquery.execute("//hi[. &= 'mixed']/..", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertXpathEvaluatesTo("1", "count(//exist:match)", result); seq = xquery.execute("//hi[. &= 'nested']/ancestor::para", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); seq = xquery.execute("//hi[. &= 'nested']/ancestor::*[2]", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertXpathEvaluatesTo("1", "count(//exist:match)", result); String query = "let $results := for $m in //para[. &= 'mixed']/hi order by $m return $m/parent::para " + "return $results"; seq = xquery.execute(query, null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertXpathEvaluatesTo("1", "count(//exist:match)", result); query = "let $results := for $m in //hi[. &= 'nested'] return $m/ancestor::para " + "return $results"; seq = xquery.execute(query, null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertXpathEvaluatesTo("1", "count(//exist:match)", result); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } finally { pool.release(broker); } } /** * Test match highlighting for index configured by QName, e.g. * <create qname="a"/>. */ @Test public void indexByQName() { DBBroker broker = null; try { configureAndStore(CONF2); broker = pool.get(org.exist.security.SecurityManager.SYSTEM_USER); XQuery xquery = broker.getXQueryService(); assertNotNull(xquery); Sequence seq = xquery.execute("//para[. &= 'mixed']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); String result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>some paragraph with <hi>" + MATCH_START + "mixed" + MATCH_END + "</hi> content.</para>", result); seq = xquery.execute("//para[. &= 'nested inner elements']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>another paragraph with <note><hi>" + MATCH_START + "nested" + MATCH_END + "</hi> " + MATCH_START + "inner" + MATCH_END + "</note> " + MATCH_START + "elements" + MATCH_END + ".</para>", result); seq = xquery.execute("//para[term &= 'term']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>a third paragraph with <term>" + MATCH_START + "term" + MATCH_END + "</term>.</para>", result); seq = xquery.execute("//para[. &= 'double match']", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<para>" + MATCH_START + "double" + MATCH_END + " " + MATCH_START + "match" + MATCH_END + " " + MATCH_START + "double" + MATCH_END + " " + MATCH_START + "match" + MATCH_END + "</para>", result); seq = xquery.execute( "for $para in //para[. &= 'double match'] return\n" + " <hit>{$para}</hit>", null, AccessContext.TEST); assertNotNull(seq); assertEquals(1, seq.getItemCount()); result = queryResult2String(broker, seq); System.out.println("RESULT: " + result); XMLAssert.assertEquals("<hit><para>" + MATCH_START + "double" + MATCH_END + " " + MATCH_START + "match" + MATCH_END + " " + MATCH_START + "double" + MATCH_END + " " + MATCH_START + "match" + MATCH_END + "</para></hit>", result); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } finally { pool.release(broker); } } @BeforeClass public static void startDB() { DBBroker broker = null; TransactionManager transact = null; Txn transaction = null; try { File confFile = ConfigurationHelper.lookup("conf.xml"); Configuration config = new Configuration(confFile.getAbsolutePath()); BrokerPool.configure(1, 5, config); pool = BrokerPool.getInstance(); assertNotNull(pool); broker = pool.get(org.exist.security.SecurityManager.SYSTEM_USER); assertNotNull(broker); transact = pool.getTransactionManager(); assertNotNull(transact); transaction = transact.beginTransaction(); assertNotNull(transaction); System.out.println("Transaction started ..."); Collection root = broker.getOrCreateCollection(transaction, TestConstants.TEST_COLLECTION_URI); assertNotNull(root); broker.saveCollection(transaction, root); transact.commit(transaction); } catch (Exception e) { if (transact != null) transact.abort(transaction); e.printStackTrace(); fail(e.getMessage()); } finally { if (pool != null) pool.release(broker); } HashMap m = new HashMap(); m.put("exist", "http://exist.sourceforge.net/NS/exist"); NamespaceContext ctx = new SimpleNamespaceContext(m); XMLUnit.setXpathNamespaceContext(ctx); } @AfterClass public static void closeDB() { TestUtils.cleanupDB(); BrokerPool.stopAll(false); pool = null; } private void configureAndStore(String config) { DBBroker broker = null; TransactionManager transact = null; Txn transaction = null; try { broker = pool.get(org.exist.security.SecurityManager.SYSTEM_USER); assertNotNull(broker); transact = pool.getTransactionManager(); assertNotNull(transact); transaction = transact.beginTransaction(); assertNotNull(transaction); Collection root = broker.getOrCreateCollection(transaction, TestConstants.TEST_COLLECTION_URI); assertNotNull(root); CollectionConfigurationManager mgr = pool.getConfigurationManager(); mgr.addConfiguration(transaction, broker, root, config); IndexInfo info = root.validateXMLResource(transaction, broker, XmldbURI.create("test_matches.xml"), XML); assertNotNull(info); root.store(transaction, broker, info, XML, false); transact.commit(transaction); } catch (Exception e) { transact.abort(transaction); e.printStackTrace(); fail(e.getMessage()); } finally { pool.release(broker); } } private String queryResult2String(DBBroker broker, Sequence seq) throws SAXException, XPathException { Properties props = new Properties(); props.setProperty(OutputKeys.INDENT, "no"); props.setProperty(EXistOutputKeys.HIGHLIGHT_MATCHES, "elements"); Serializer serializer = broker.getSerializer(); serializer.reset(); serializer.setProperties(props); return serializer.serialize((NodeValue) seq.itemAt(0)); } }