0

I have a Berkeley-db database where both 'key' and 'value' are of type integer. Is there any way to traverse the database in descending order of 'value'?

I'm using Berkeley-db je-5.0.58 API. The sample code that i'm using from its documentation is shown below.

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2004-2010 Oracle.  All rights reserved.
*
*/

package je;
import java.io.File;

import com.sleepycat.bind.tuple.IntegerBinding;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

/**
* SimpleExample creates a database environment, a database, and a database
* cursor, inserts and retrieves data.
*/
class SimpleExample {
private static final int EXIT_SUCCESS = 0;
private static final int EXIT_FAILURE = 1;

private int numRecords;   // num records to insert or retrieve
private int offset;       // where we want to start inserting
private boolean doInsert; // if true, insert, else retrieve
private File envDir;

public SimpleExample(int numRecords,
                     boolean doInsert,
                     File envDir,
                     int offset) {
    this.numRecords = numRecords;
    this.doInsert = doInsert;
    this.envDir = envDir;
    this.offset = offset;
}

/**
 * Usage string
 */
public static void usage() {
    System.out.println("usage: java " +
                       "je.SimpleExample " +
                       "<dbEnvHomeDirectory> " +
                       "<insert|retrieve> <numRecords> [offset]");
    System.exit(EXIT_FAILURE);
}

/**
 * Main
 */
public static void main(String argv[]) {

    if (argv.length < 2) {
        usage();
        return;
    }
    File envHomeDirectory = new File(argv[0]);

    boolean doInsertArg = false;
    if (argv[1].equalsIgnoreCase("insert")) {
        doInsertArg = true;
    } else if (argv[1].equalsIgnoreCase("retrieve")) {
        doInsertArg = false;
    } else {
        usage();
    }

    int startOffset = 0;
    int numRecordsVal = 0;

    if (doInsertArg) {

        if (argv.length > 2) {
            numRecordsVal = Integer.parseInt(argv[2]);
        } else {
            usage();
            return;
        }

        if (argv.length > 3) {
            startOffset = Integer.parseInt(argv[3]);
        }
    }

    try {
        SimpleExample app = new SimpleExample(numRecordsVal,
                                              doInsertArg,
                                              envHomeDirectory,
                                              startOffset);
        app.run();
    } catch (DatabaseException e) {
        e.printStackTrace();
        System.exit(EXIT_FAILURE);
    }
    System.exit(EXIT_SUCCESS);
}

/**
 * Insert or retrieve data
 */
public void run() throws DatabaseException {
    /* Create a new, transactional database environment */
    EnvironmentConfig envConfig = new EnvironmentConfig();
    envConfig.setTransactional(true);
    envConfig.setAllowCreate(true);
    Environment exampleEnv = new Environment(envDir, envConfig);

    /*
     * Make a database within that environment
     *
     * Notice that we use an explicit transaction to
     * perform this database open, and that we
     * immediately commit the transaction once the
     * database is opened. This is required if we
     * want transactional support for the database.
     * However, we could have used autocommit to
     * perform the same thing by simply passing a
     * null txn handle to openDatabase().
     */
    Transaction txn = exampleEnv.beginTransaction(null, null);
    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setTransactional(true);
    dbConfig.setAllowCreate(true);
    dbConfig.setSortedDuplicates(true);
    Database exampleDb = exampleEnv.openDatabase(txn,
                                                 "simpleDb",
                                                 dbConfig);
    txn.commit();

    /*
     * Insert or retrieve data. In our example, database records are
     * integer pairs.
     */

    /* DatabaseEntry represents the key and data of each record */
    DatabaseEntry keyEntry = new DatabaseEntry();
    DatabaseEntry dataEntry = new DatabaseEntry();

    if (doInsert) {

        /* put some data in */
        for (int i = offset; i < numRecords + offset; i++) {
            /*
             * Note that autocommit mode, described in the Getting
             * Started Guide, is an alternative to explicitly
             * creating the transaction object.
             */
            txn = exampleEnv.beginTransaction(null, null);

            /* Use a binding to convert the int into a DatabaseEntry. */

            IntegerBinding.intToEntry(i, keyEntry);
            IntegerBinding.intToEntry(i+1, dataEntry);
            OperationStatus status =
                exampleDb.put(txn, keyEntry, dataEntry);

            /*
             * Note that put will throw a DatabaseException when
             * error conditions are found such as deadlock.
             * However, the status return conveys a variety of
             * information. For example, the put might succeed,
             * or it might not succeed if the record alread exists
             * and the database was not configured for duplicate
             * records.
             */
            if (status != OperationStatus.SUCCESS) {
                throw new RuntimeException("Data insertion got status " +
                                           status);
            }
            txn.commit();
        }
    } else {
        /* retrieve the data */
        Cursor cursor = exampleDb.openCursor(null, null);

        while (cursor.getNext(keyEntry, dataEntry, LockMode.DEFAULT) ==
               OperationStatus.SUCCESS) {
            System.out.println("key=" +
                               IntegerBinding.entryToInt(keyEntry) +
                               " data=" +
                               IntegerBinding.entryToInt(dataEntry));

        }
        cursor.close();
    }

    exampleDb.close();
    exampleEnv.close();

  }
}

1 Answers1

0

you can use a custom comparator and use a cursor to traverse the data in descending order.

In java you have to implement a custom Comparator:

EDIT:

import java.util.Comparator;

import com.sleepycat.bind.tuple.IntegerBinding;
import com.sleepycat.je.DatabaseEntry;

public class IntegerSorter implements Comparator<byte[]>
    {
    @Override
    public int compare(byte[] o1, byte[] o2)
        {
        return
              IntegerBinding.entryToInt(new DatabaseEntry(o1)) - 
              IntegerBinding.entryToInt(new DatabaseEntry(o2));
        }
    }

(...)
 dbConfig.setBtreeComparator(IntegerSorter.class);
(...)
Pierre
  • 34,472
  • 31
  • 113
  • 192
  • Thanks for your prompt reply. After using the custom comparator as suggested by you, the program compiles without any error. But I'm getting following exceptions at run time: Exception in thread "main" java.lang.IllegalArgumentException: Btree comparator is not valid. Perhaps you have not implemented a zero-parameter constructor for the comparator or the comparator class cannot be found. I tried to google it, but could not find any solution. :( – user1444462 Sep 16 '12 at 17:36
  • The whole database must have been constructed from scratch using the comparator. You cannot build/fill the database and then add this comparator. – Pierre Sep 16 '12 at 17:57
  • I have deleted all previous database and class files before compiling but it still gives the same error. – user1444462 Sep 16 '12 at 18:03
  • is the IntegerSorter.class in your classpath ? – Pierre Sep 16 '12 at 18:44
  • Thanks. I figured out the problem. The problem was not of classpath, but I have declared IntegerSorter as an inner class of SimpleExample class. But the output in descending order of 'key'. Is there any way to traverse in descending order of 'value'? – user1444462 Sep 16 '12 at 19:16
  • yes, you can either negate the value of intSorter or traverse the keys using Cursor getLast ( http://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/Cursor.html#getLast%28com.sleepycat.je.DatabaseEntry,%20com.sleepycat.je.DatabaseEntry,%20com.sleepycat.je.LockMode%29 ) and then getPrev http://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/Cursor.html#getPrev%28com.sleepycat.je.DatabaseEntry,%20com.sleepycat.je.DatabaseEntry,%20com.sleepycat.je.LockMode%29 – Pierre Sep 16 '12 at 22:22
  • I guess you misunderstood my question. I would like to traverse database in descending order of 'data' value of pair, not 'key'. – user1444462 Sep 17 '12 at 05:19
  • then you have to create a SecondaryDatabase for the data: http://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/SecondaryDatabase.html – Pierre Sep 17 '12 at 09:38
  • Thanks. I guess Secondary Database is required. – user1444462 Sep 18 '12 at 20:26