如何按“值”的降序遍历 berkeley-db 数据库?

Posted

技术标签:

【中文标题】如何按“值”的降序遍历 berkeley-db 数据库?【英文标题】:How to traverse berkeley-db database in descending order of 'value'? 【发布时间】:2012-09-16 13:46:47 【问题描述】:

我有一个 Berkeley-db 数据库,其中“键”和“值”都是整数类型。有没有办法按‘value’降序遍历数据库?

我正在使用 Berkeley-db je-5.0.58 API。我在其文档中使用的示例代码如下所示。

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

  

【问题讨论】:

可以提供更多细节吗?您使用的是哪个 Berkeley-db Java API?你有一些示例代码吗? 请看上面,我已经更新了问题。 【参考方案1】:

您可以使用custom comparator 和使用cursor 以降序遍历数据。

在java中你必须实现一个自定义的比较器:

编辑

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

【讨论】:

感谢您的及时回复。按照您的建议使用自定义比较器后,程序编译没有任何错误。但我在运行时遇到以下异常:线程“main”java.lang.IllegalArgumentException 中的异常:Btree 比较器无效。可能您没有为比较器实现零参数构造函数,或者找不到比较器类。我试图用谷歌搜索它,但找不到任何解决方案。 :( 整个数据库必须是使用比较器从头开始构建的。您无法构建/填充数据库,然后添加此比较器。 在编译之前我已经删除了所有以前的数据库和类文件,但它仍然给出了同样的错误。 是你类路径中的 IntegerSorter.class 吗? 谢谢。我解决了这个问题。问题不在于类路径,但我已将 IntegerSorter 声明为 SimpleExample 类的内部类。但输出按“键”的降序排列。有没有办法按“价值”的降序遍历?

以上是关于如何按“值”的降序遍历 berkeley-db 数据库?的主要内容,如果未能解决你的问题,请参考以下文章

如何按数字向量的降序显示ggplot2中的条形?

plotly:分组条形图中的降序

按特定键的降序对字典列表进行排序[重复]

c_cpp 按设置位的降序对数组进行排序

按 C 中元素出现频率的降序对数组进行排序

firebase 和 Ionic 2 上的降序 orderByChild()