/* 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(); } }