/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-04 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.xquery;
import java.io.File;
import java.util.Properties;
import junit.framework.TestCase;
import org.exist.storage.DBBroker;
import org.exist.xmldb.DatabaseInstanceManager;
import org.exist.xmldb.IndexQueryService;
import org.exist.xmldb.XQueryService;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.ResourceIterator;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.CollectionManagementService;
import org.xmldb.api.modules.XMLResource;
import org.xmldb.api.modules.XPathQueryService;
import org.xmldb.api.modules.XUpdateQueryService;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.BasicConfigurator;
/**
* @author wolf
*/
public class ValueIndexTest extends TestCase {
private final static String URI = "xmldb:exist://" + DBBroker.ROOT_COLLECTION;
private String CONFIG =
"<collection xmlns=\"http://exist-db.org/collection-config/1.0\">" +
" <index xmlns:x=\"http://www.foo.com\" xmlns:xx=\"http://test.com\">" +
" <fulltext default=\"none\">" +
" <include path=\"//item/name\"/>" +
" <include path=\"//item/mixed\"/>" +
" </fulltext>" +
" <create path=\"//item/itemno\" type=\"xs:integer\"/>" +
" <create path=\"//item/@id\" type=\"xs:string\"/>" +
" <create path=\"//item/name\" type=\"xs:string\"/>" +
" <create path=\"//item/stock\" type=\"xs:integer\"/>" +
" <create path=\"//item/price\" type=\"xs:double\"/>" +
" <create path=\"//item/price/@specialprice\" type=\"xs:boolean\"/>" +
" <create path=\"//item/x:rating\" type=\"xs:double\"/>" +
" <create path=\"//item/@xx:test\" type=\"xs:integer\"/>" +
" <create path=\"//item/mixed\" type=\"xs:string\"/>" +
" <create path=\"//city/name\" type=\"xs:string\"/>" +
" </index>" +
"</collection>";
private String CONFIG_QNAME =
"<collection xmlns=\"http://exist-db.org/collection-config/1.0\">" +
" <index xmlns:x=\"http://www.foo.com\" xmlns:xx=\"http://test.com\">" +
" <fulltext default=\"none\">" +
" </fulltext>" +
" <create qname=\"itemno\" type=\"xs:integer\"/>" +
" <create qname=\"mixed\" type=\"xs:string\"/>" +
" <create qname=\"stock\" type=\"xs:integer\"/>" +
" <create qname=\"name\" type=\"xs:string\"/>" +
" <create qname=\"@id\" type=\"xs:string\"/>" +
" <create qname=\"price\" type=\"xs:double\"/>" +
" <create path=\"x:rating\" type=\"xs:double\"/>" +
" </index>" +
"</collection>";
private String CITY =
"<mondial>" +
" <city id=\"cty-Germany-Berlin\" is_country_cap=\"yes\" is_state_cap=\"yes\" " +
" country=\"D\" province=\"prov-cid-cia-Germany-4\">" +
" <name>Berlin</name>" +
" <longitude>13.3</longitude>" +
" <latitude>52.45</latitude>" +
" <population year=\"95\">3472009</population>" +
" </city>" +
" <city id=\"cty-cid-cia-Germany-85\" country=\"D\" province=\"prov-cid-cia-Germany-3\">" +
" <name>Erlangen</name>" +
" <population year=\"95\">101450</population>" +
" </city>" +
"</mondial>";
private Collection testCollection;
protected void setUp() {
try {
// initialize driver
Class cl = Class.forName("org.exist.xmldb.DatabaseImpl");
Database database = (Database) cl.newInstance();
database.setProperty("create-database", "true");
DatabaseManager.registerDatabase(database);
Collection root = DatabaseManager.getCollection(URI, "admin", null);
CollectionManagementService service = (CollectionManagementService) root
.getService("CollectionManagementService", "1.0");
testCollection = service.createCollection("test");
assertNotNull(testCollection);
} catch (ClassNotFoundException e) {
} catch (InstantiationException e) {
} catch (IllegalAccessException e) {
} catch (XMLDBException e) {
e.printStackTrace();
}
}
/*
* @see TestCase#tearDown()
*/
protected void tearDown() throws Exception {
Collection root = DatabaseManager.getCollection(URI, "admin", null);
CollectionManagementService service = (CollectionManagementService) root
.getService("CollectionManagementService", "1.0");
service.removeCollection("test");
DatabaseInstanceManager dim =
(DatabaseInstanceManager) testCollection.getService("DatabaseInstanceManager", "1.0");
dim.shutdown();
testCollection = null;
//System.out.println("tearDown PASSED");
}
/**
* @throws XMLDBException
*/
protected void configureCollection(String config) throws XMLDBException {
IndexQueryService idxConf = (IndexQueryService)
testCollection.getService("IndexQueryService", "1.0");
idxConf.configureCollection(config);
}
public void testStrings() throws Exception {
try {
configureCollection(CONFIG);
XPathQueryService service = storeXMLFileAndGetQueryService("items.xml", "test/src/org/exist/xquery/items.xml");
queryResource(service, "items.xml", "//item[@id = 'i2']", 1);
queryResource(service, "items.xml", "//item[name = 'Racing Bicycle']", 1);
queryResource(service, "items.xml", "//item[name > 'Racing Bicycle']", 4);
queryResource(service, "items.xml", "//item[itemno = 3]", 1);
queryResource(service, "items.xml", "//item[itemno eq 3]", 1);
ResourceSet result = queryResource(service, "items.xml", "for $i in //item[stock <= 10] return $i/itemno", 5);
for (long i = 0; i < result.getSize(); i++) {
Resource res = result.getResource(i);
System.out.println(res.getContent());
}
queryResource(service, "items.xml", "//item[stock > 20]", 1);
queryResource(service, "items.xml", "declare namespace x=\"http://www.foo.com\"; //item[x:rating > 8.0]", 2);
queryResource(service, "items.xml", "declare namespace xx=\"http://test.com\"; //item[@xx:test = 123]", 1); queryResource(service, "items.xml", "//item[name &= 'Racing Bicycle']", 1);
queryResource(service, "items.xml", "declare namespace xx=\"http://test.com\"; //item[@xx:test eq 123]", 1);
queryResource(service, "items.xml", "//item[mixed = 'uneven']", 1);
queryResource(service, "items.xml", "//item[mixed eq 'uneven']", 1);
queryResource(service, "items.xml", "//item[mixed = 'external']", 1);
queryResource(service, "items.xml", "//item[fn:matches(mixed, 'un.*')]", 2);
queryResource(service, "items.xml", "//item[price/@specialprice = false()]", 2);
queryResource(service, "items.xml", "//item[price/@specialprice = true()]", 1);
queryResource(service, "items.xml", "//item[price/@specialprice eq true()]", 1);
} catch (XMLDBException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
public void testStrFunctions() {
try {
configureCollection(CONFIG);
XMLResource resource = (XMLResource) testCollection.createResource("mondial-test.xml", "XMLResource");
resource.setContent(CITY);
testCollection.storeResource(resource);
XPathQueryService service = (XPathQueryService) testCollection.getService("XPathQueryService", "1.0");
queryResource(service, "mondial-test.xml", "//city[starts-with(name, 'Berl')]", 1);
queryResource(service, "mondial-test.xml", "//city[starts-with(name, 'Berlin')]", 1);
queryResource(service, "mondial-test.xml", "//city[starts-with(name, 'erlin')]", 0);
queryResource(service, "mondial-test.xml", "//city[starts-with(name, 'Erl')]", 1);
queryResource(service, "mondial-test.xml", "//city[contains(name, 'erl')]", 1);
queryResource(service, "mondial-test.xml", "//city[contains(name, 'Berlin')]", 1);
queryResource(service, "mondial-test.xml", "//city[contains(name, 'Erl')]", 1);
queryResource(service, "mondial-test.xml", "//city[ends-with(name, 'Berlin')]", 1);
queryResource(service, "mondial-test.xml", "//city[ends-with(name, 'erlin')]", 1);
queryResource(service, "mondial-test.xml", "//city[ends-with(name, 'Ber')]", 0);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'erl', 'i')]", 2);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'Erl')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'Berlin', 'i')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'berlin', 'i')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'berlin')]", 0);
queryResource(service, "mondial-test.xml", "//city[matches(name, '^Berlin$')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'lin$', 'i')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, '.*lin$', 'i')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, '^lin$', 'i')]", 0);
} catch (XMLDBException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
public void testStrFunctionsQName() {
try {
configureCollection(CONFIG_QNAME);
XMLResource resource = (XMLResource) testCollection.createResource("mondial-test.xml", "XMLResource");
resource.setContent(CITY);
testCollection.storeResource(resource);
XPathQueryService service = (XPathQueryService) testCollection.getService("XPathQueryService", "1.0");
queryResource(service, "mondial-test.xml", "//city[starts-with(name, 'Berl')]", 1);
queryResource(service, "mondial-test.xml", "//city[starts-with(name, 'Berlin')]", 1);
queryResource(service, "mondial-test.xml", "//city[starts-with(name, 'erlin')]", 0);
queryResource(service, "mondial-test.xml", "//city[starts-with(name, 'Erl')]", 1);
queryResource(service, "mondial-test.xml", "//city[contains(name, 'erl')]", 1);
queryResource(service, "mondial-test.xml", "//city[contains(name, 'Berlin')]", 1);
queryResource(service, "mondial-test.xml", "//city[contains(name, 'Erl')]", 1);
queryResource(service, "mondial-test.xml", "//city[ends-with(name, 'Berlin')]", 1);
queryResource(service, "mondial-test.xml", "//city[ends-with(name, 'erlin')]", 1);
queryResource(service, "mondial-test.xml", "//city[ends-with(name, 'Ber')]", 0);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'erl', 'i')]", 2);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'Erl')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'Berlin', 'i')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'berlin', 'i')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'berlin')]", 0);
queryResource(service, "mondial-test.xml", "//city[matches(name, '^Berlin$')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, 'lin$', 'i')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, '.*lin$', 'i')]", 1);
queryResource(service, "mondial-test.xml", "//city[matches(name, '^lin$', 'i')]", 0);
} catch (XMLDBException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
public void testQNameIndex() {
try {
configureCollection(CONFIG_QNAME);
XPathQueryService service = storeXMLFileAndGetQueryService("items.xml", "test/src/org/exist/xquery/items.xml");
queryResource(service, "items.xml", "//((#exist:optimize#) { item[stock = 10] })", 1);
queryResource(service, "items.xml", "//((#exist:optimize#) { item[stock > 20] })", 1);
queryResource(service, "items.xml", "//((#exist:optimize#) { item[stock < 16] })", 6);
queryResource(service, "items.xml", "declare namespace x=\"http://www.foo.com\"; " +
"//((#exist:optimize#) { item[x:rating > 8.0] })", 2);
queryResource(service, "items.xml", "//((#exist:optimize#) { item[mixed = 'uneven'] })", 1);
queryResource(service, "items.xml", "//((#exist:optimize#) { item[mixed = 'external'] })", 1);
queryResource(service, "items.xml", "//((#exist:optimize#) { item[@id = 'i1'] })",1);
queryResource(service, "items.xml", "declare namespace xx=\"http://test.com\";" +
"//((#exist:optimize#) { item[@xx:test = 123] })", 1);
} catch (XMLDBException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
public void testIndexScan() {
try {
System.out.println("----- testIndexScan -----");
configureCollection(CONFIG);
String queryBody =
"declare namespace f=\'http://exist-db.org/xquery/test\';\n" +
"declare namespace mods='http://www.loc.gov/mods/v3';\n" +
"import module namespace u=\'http://exist-db.org/xquery/util\';\n" +
"\n" +
"declare function f:term-callback($term as item(), $data as xs:int+)\n" +
"as element()+ {\n" +
" <item>\n" +
" <term>{$term}</term>\n" +
" <frequency>{$data[1]}</frequency>\n" +
" </item>\n" +
"};\n" +
"\n";
XPathQueryService service = storeXMLFileAndGetQueryService("items.xml", "test/src/org/exist/xquery/items.xml");
String query = queryBody + "u:index-keys(//item/name, \'\', util:function(xs:QName(\'f:term-callback\'), 2), 1000)";
ResourceSet result = service.query(query);
for (ResourceIterator i = result.getIterator(); i.hasMoreResources(); ) {
System.out.println(i.nextResource().getContent());
}
assertEquals(7, result.getSize());
query = queryBody + "u:index-keys(//item/stock, 0, util:function(xs:QName(\'f:term-callback\'), 2), 1000)";
result = service.query(query);
for (ResourceIterator i = result.getIterator(); i.hasMoreResources(); ) {
System.out.println(i.nextResource().getContent());
}
assertEquals(5, result.getSize());
} catch (XMLDBException e) {
fail(e.getMessage());
}
}
public void testUpdates() throws Exception {
try {
configureCollection(CONFIG);
storeXMLFileAndGetQueryService("items.xml", "test/src/org/exist/xquery/items.xml");
for (int i = 100; i <= 150; i++) {
String append =
"<xu:modifications xmlns:xu=\"http://www.xmldb.org/xupdate\" version=\"1.0\">" +
" <xu:append select=\"/items\">" +
" <item id=\"i" + i + "\">" +
" <itemno>" + i + "</itemno>" +
" <name>New Item</name>" +
" <price>55.50</price>" +
" </item>" +
" </xu:append>" +
"</xu:modifications>";
String remove =
"<xu:modifications xmlns:xu=\"http://www.xmldb.org/xupdate\" version=\"1.0\">" +
" <xu:remove select=\"/items/item[itemno=" + i + "]\"/>" +
"</xu:modifications>";
XPathQueryService query = (XPathQueryService) testCollection.getService("XPathQueryService", "1.0");
XUpdateQueryService update = (XUpdateQueryService) testCollection.getService("XUpdateQueryService", "1.0");
long mods = update.updateResource("items.xml", append);
assertEquals(mods, 1);
queryResource(query, "items.xml", "//item[price = 55.50]", 1);
queryResource(query, "items.xml", "//item[@id = 'i" + i + "']",1);
mods = update.updateResource("items.xml", remove);
assertEquals(mods, 1);
queryResource(query, "items.xml", "//item[itemno = " + i + "]", 0);
}
} catch (XMLDBException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
public void testUpdatesQName() throws Exception {
try {
configureCollection(CONFIG_QNAME);
storeXMLFileAndGetQueryService("items.xml", "test/src/org/exist/xquery/items.xml");
for (int i = 100; i <= 150; i++) {
String append =
"<xu:modifications xmlns:xu=\"http://www.xmldb.org/xupdate\" version=\"1.0\">" +
" <xu:append select=\"/items\">" +
" <item id=\"i" + i + "\">" +
" <itemno>" + i + "</itemno>" +
" <name>New Item</name>" +
" <price>55.50</price>" +
" </item>" +
" </xu:append>" +
"</xu:modifications>";
String remove =
"<xu:modifications xmlns:xu=\"http://www.xmldb.org/xupdate\" version=\"1.0\">" +
" <xu:remove select=\"/items/item[itemno=" + i + "]\"/>" +
"</xu:modifications>";
XPathQueryService query = (XPathQueryService) testCollection.getService("XPathQueryService", "1.0");
XUpdateQueryService update = (XUpdateQueryService) testCollection.getService("XUpdateQueryService", "1.0");
long mods = update.updateResource("items.xml", append);
assertEquals(mods, 1);
queryResource(query, "items.xml", "//((#exist:optimize#) { item[price = 55.50] })", 1);
queryResource(query, "items.xml", "//((#exist:optimize#) { item[@id = 'i" + i + "']})",1);
queryResource(query, "items.xml", "//((#exist:optimize#) { item[itemno = " + i + "] })", 1);
mods = update.updateResource("items.xml", remove);
assertEquals(mods, 1);
queryResource(query, "items.xml", "//((#exist:optimize#) { item[itemno = " + i + "] })", 0);
}
} catch (XMLDBException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
protected ResourceSet queryResource(XPathQueryService service,
String resource, String query, int expected) throws XMLDBException {
return queryResource(service, resource, query, expected, null);
}
/**
* @param service
* @throws XMLDBException
*/
private ResourceSet queryResource(XPathQueryService service,
String resource, String query, int expected, String message)
throws XMLDBException {
ResourceSet result = service.queryResource(resource, query);
if (message == null)
assertEquals(expected, result.getSize());
else
assertEquals(message, expected, result.getSize());
return result;
}
/**
* @return
* @throws XMLDBException
*/
protected XPathQueryService storeXMLFileAndGetQueryService(
String documentName, String path) throws XMLDBException {
XMLResource doc = (XMLResource) testCollection.createResource(
documentName, "XMLResource");
String existHome = System.getProperty("exist.home");
File existDir = existHome==null ? new File(".") : new File(existHome);
File f = new File(existDir,path);
doc.setContent(f);
testCollection.storeResource(doc);
XPathQueryService service = (XPathQueryService) testCollection
.getService("XPathQueryService", "1.0");
return service;
}
public static void main(String[] args) {
junit.textui.TestRunner.run(ValueIndexTest.class);
}
}