/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library 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 3 of the License, or (at your option) any later version.
This library 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 library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.indexStructures.mtrees;
import java.io.File;
import java.util.Properties;
import xxl.core.collections.containers.io.BlockFileContainer;
import xxl.core.collections.containers.io.BufferedContainer;
import xxl.core.collections.containers.io.ConverterContainer;
import xxl.core.cursors.Cursor;
import xxl.core.cursors.Cursors;
import xxl.core.cursors.filters.Taker;
import xxl.core.cursors.mappers.Mapper;
import xxl.core.indexStructures.Common;
import xxl.core.indexStructures.MTree;
import xxl.core.indexStructures.ORTree;
import xxl.core.indexStructures.Sphere;
import xxl.core.indexStructures.ORTree.IndexEntry;
import xxl.core.io.Convertable;
import xxl.core.io.LRUBuffer;
import xxl.core.spatial.cursors.KPEInputCursor;
import xxl.core.spatial.rectangles.DoublePointRectangle;
import xxl.core.spatial.rectangles.Rectangle;
import xxl.core.spatial.rectangles.Rectangles;
/** This class tests the reopening of an MTree stored on external memory.
*/
public class MTreeReopenTest {
/**
* File containing the railroad input data.
*/
protected static String FILENAME_TREE = "rr_small.bin";
/**
* File containing the streets input data.
*/
protected static String FILENAME_QUERIES = "st_small.bin";
/**
* An rectangle containing all data rectangles.
* Needed for peano-/hilbert value computation.
*/
protected static Rectangle universe =
Common.getDataPath() != null ? Rectangles.readSingletonRectangle(new File(Common.getDataPath()+FILENAME_TREE+".universe"), new DoublePointRectangle(2)) : null;
/**
* This method is used to set the properties for the application.
*
* @param defaultProps default properties
* @param args user defined properties
* @return new properties for the application
*/
public static Properties argsToProperties (Properties defaultProps, String args[]) {
Properties props = new Properties(defaultProps);
for (int i = 0; i < args.length ; i++) {
String prop, val = null;
int indexEqual = args[i].indexOf('=');
if (indexEqual > 0) {
prop = args[i].substring(0, indexEqual);
val = args[i].substring(indexEqual+1);
}
else {
prop = args[i];
val = ""; // != null !
}
prop = prop.toLowerCase();
props.setProperty(prop,val);
}
return props;
}
/**
* This method perform several tests on an
* {@link xxl.core.indexStructures.MTree}:
* <ul>
* <li>1000 exact match queries</li>
* <li>1000 range queries</li>
* <li>a root descriptor query.</li>
* </ul>
*
* Additionally a check for consistence is performed.
*
* @param mTree the tree to test
*/
public static void performTests(MTree mTree) {
long t1,t2;
int targetLevel = 0;
System.out.print("\nChecking descriptors... ");
mTree.checkDescriptors();
System.out.println("done.\n");
/*********************************************************************/
/* EXACT MATCH QUERY */
/*********************************************************************/
// accessing input from file
Cursor input = new KPEInputCursor(
new File(Common.getDataPath()+FILENAME_QUERIES),
4096, // buffer size
2 // dimension of KPE's
);
// preparing to consume 1000 elements applying the mapping function
// (KPE ==> DoublePoint) to each of them
Cursor cursor1 = new Taker(
new Mapper(MTreeTest.LEAFENTRY_FACTORY,input),
1000
);
int hits = 0;
System.out.println("\nPerforming 1000 exact match queries against the M-tree: ");
t1 = System.currentTimeMillis();
while (cursor1.hasNext()) {
hits += Cursors.count(
// querying M-Tree using query(descriptor)
// targetLevel is 0 due to exact match queries can only
// be performed on leaf nodes.
mTree.query(cursor1.next())
);
}
t2 = System.currentTimeMillis();
System.out.println("Time for queries: "+(t2-t1) +" ms.");
System.out.println("Number of hits: "+hits);
/*********************************************************************/
/* RANGE QUERY */
/*********************************************************************/
// preparing to consume further 1000 elements applying the mapping function
// (KPE ==> Sphere) to each of them
Cursor cursor2 = new Taker(
new Mapper(MTreeTest.SPHERE_COVERING_RECTANGLE_FACTORY,input),
1000
);
hits = 0;
System.out.println("\nPerforming 1000 range queries against the M-tree: ");
t1 = System.currentTimeMillis();
while (cursor2.hasNext()) {
hits += Cursors.count(
// querying M-Tree using query(descriptor, targetLevel)
mTree.query((Sphere)cursor2.next(), targetLevel)
);
}
t2 = System.currentTimeMillis();
cursor1.close(); // also closes the input cursor
cursor2.close(); // closes the input cursor again (idem potent!)
System.out.println("Time for queries: "+(t2-t1) +" ms.");
System.out.println("Number of hits: "+hits);
/*********************************************************************/
/* RANGE QUERY WITH ROOT DESCRIPTOR */
/*********************************************************************/
System.out.println("\nQuerying root descriptor: ");
// range query with root descriptor should return all entries of the
// M-Tree at the specified target level
hits = Cursors.count(mTree.query(mTree.rootDescriptor(), targetLevel));
System.out.println("Number of results: "+hits);
/*********************************************************************/
/* ADDITIONAL CHECKS FOR CONSISTENCE */
/*********************************************************************/
System.out.print("\nChecking descriptors... ");
mTree.checkDescriptors();
// check if all 'distance to parent'-attributes are correct
System.out.println("\nChecking 'distance to parent' entries... ");
mTree.checkDistanceToParent();
// verify number of node entries
System.out.println("\nVerifying number of node entries within range [minCapcity, maxCapacity]... ");
mTree.checkNumberOfEntries();
}
/**
* The main method builds an M-Tree/Slim-Tree and executes various kinds of
* queries on it. Then the tree is dropped from memory and reread from
* external memory. Afterwards the tests are repeated.
*
* @param args command line parameters
* @throws Exception
*/
public static void main (String [] args) throws Exception {
int minCapacity = 10;
int maxCapacity = 25;
int bufferSize = 100;
boolean balanced = false;
/*********************************************************************/
/* BUILDING M-TREE or SLIM-TREE */
/*********************************************************************/
// generating a new instance of the class M-Tree or Slim-Tree
MTree mTree;
mTree = new MTree(balanced ? MTree.BALANCED_SPLIT : MTree.HYPERPLANE_SPLIT);
// an unbuffered container that counts the access to the MTree
BufferedContainer container =
new BufferedContainer(
new ConverterContainer(
// leaf nodes are 16+8 Bytes of size.
// index nodes are (16+8+8)+8 Bytes of size.
// ==> so take the maximum for the block size!
// additionally each node consumes further 4 bytes for
// node number and 2 bytes for level information.
new BlockFileContainer(Common.getOutPath()+"MTree", 2+4+40*maxCapacity),
// actually dimension of inserted points is '2'
mTree.nodeConverter(mTree.leafEntryConverter(MTreeTest.centerConverter(2)), mTree.indexEntryConverter(MTreeTest.descriptorConverter(2))) // define node converter
),
new LRUBuffer(bufferSize),
true
);
// initialize the MTree with the descriptor-factory method, a
// container for storing the nodes and their minimum and maximum
// capacity
mTree.initialize(MTreeTest.getDescriptor, container, minCapacity, maxCapacity);
/*********************************************************************/
/* INSERTION */
/*********************************************************************/
// accessing input from file and converting KPEs to DoublePoints
Cursor cursor = new Mapper(
MTreeTest.LEAFENTRY_FACTORY,
new KPEInputCursor(
new File(Common.getDataPath()+FILENAME_TREE),
4096, // buffer size
2 // dimension of KPE's
)
);
long t1,t2;
t1 = System.currentTimeMillis();
// inserting an iterator of objects by inserting every single object
while (cursor.hasNext()) {
Convertable c = (Convertable) cursor.next();
mTree.insert(c);
}
cursor.close();
t2 = System.currentTimeMillis();
System.out.println("Time for insertion: "+(t2-t1)+" ms.");
System.out.println("Insertion complete, height: "+mTree.height()+", universe: ");
System.out.println(mTree.rootDescriptor());
System.out.println("Flushing buffers.");
container.flush();
/*********************************************************************/
/* DO SOME TESTS */
/*********************************************************************/
System.out.println("###################");
System.out.println("## DO SOME TESTS ##");
System.out.println("###################");
performTests(mTree);
/*********************************************************************/
/* THROWING AWAY MTREE */
/*********************************************************************/
System.out.println("##################");
System.out.println("## RELOAD MTREE ##");
System.out.println("##################");
// Storing important information
// The following three variables are necessary to restore the mTree.
// You can store them wherever you want (for example a file).
Object rootPageId = mTree.rootEntry().id();
Sphere rootDescriptor = (Sphere) mTree.rootDescriptor();
int height = mTree.height();
mTree = null;
container.close();
container = null;
/*********************************************************************/
/* REOPENING MTREE */
/*********************************************************************/
mTree = new MTree(balanced ? MTree.BALANCED_SPLIT : MTree.HYPERPLANE_SPLIT);
// an unbuffered container that counts the access to the MTree
container =
new BufferedContainer(
new ConverterContainer(
// Open Container without blocksize!!! Else the information is overwritten!
new BlockFileContainer(Common.getOutPath()+"MTree"),
// actually dimension of inserted points is '2'
mTree.nodeConverter(mTree.leafEntryConverter(MTreeTest.centerConverter(2)), mTree.indexEntryConverter(MTreeTest.descriptorConverter(2))) // define node converter
),
new LRUBuffer(bufferSize),
true
);
ORTree.IndexEntry rootEntry = (ORTree.IndexEntry) ((ORTree.IndexEntry)mTree.createIndexEntry(height)).initialize(rootDescriptor).initialize(rootPageId);
mTree.initialize(rootEntry, MTreeTest.getDescriptor, container, minCapacity, maxCapacity);
/*********************************************************************/
/* DO SOME TESTS */
/*********************************************************************/
System.out.println("#########################");
System.out.println("## DO SOME TESTS AGAIN ##");
System.out.println("#########################");
performTests(mTree);
System.out.println("Closing application.");
container.close();
}
}