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