Search code examples
javadatabaselmdb

LMDB Java retrieve all values with same key


I want to store multiple values with the same key. I made sure to include the MDB_DUPSORT flag when creating the database. I am also aware this limits the value size but in this specific case this is not a problem.

My problem starts when I want to read the values with the same keys. I searched but could not find a clear answer on how to do this.

So basically: how to retrieve all values with the same key?

I use lmdbjava to read/write from the database.

I tried this but the iterator continues on with the next key and does not stop when the last value is read:

try(Txn<ByteBuffer> txn = env.txnRead()) {
    CursorIterator<ByteBuffer> cursor = db.iterate(txn, KeyRange.atLeast(key));

    for(CursorIterator.KeyVal<ByteBuffer> kv : cursor.iterable()) {
        ByteBuffer value = kv.val();

        byte[] bytes = new byte[value.remaining()];

        value.get(bytes);

        System.out.println(bytes);
    }
}

Solution

  • Rather than KeyRange.atLeast which according to the javadoc

    starts on the passed key (or the first key immediately after it) and iterate forward until no keys remain

    I think you'd want to use KeyRange.closed

    Iterate forward between the passed keys, matching on the first keys directly equal to the passed key (or immediately following it in the case of the "start" key, or immediately preceding it in the case of the "stop" key).

    Test for it

      @Test
      public void dupSortKeyRange() {
    
        final Dbi<ByteBuffer> db = env.openDbi(DB_1, MDB_CREATE, MDB_DUPSORT);
    
        try (Txn<ByteBuffer> txn = env.txnWrite()) {
          db.put(txn, bb(5), bb(6));
          db.put(txn, bb(5), bb(7));
          db.put(txn, bb(5), bb(8));
          db.put(txn, bb(6), bb(9));
          txn.commit();
        }
    
        try (Txn<ByteBuffer> txn = env.txnRead()) {
          ByteBuffer key = bb(5);
    
          List<Integer> keyValues = new ArrayList<>();
          CursorIterator<ByteBuffer> cursor = db.iterate(txn, KeyRange.closed(key, key));
          for (CursorIterator.KeyVal<ByteBuffer> kv : cursor.iterable()) {
            ByteBuffer value = kv.val().get(new byte[kv.val().remaining()]);
            keyValues.add(value.getInt(0));
          }
    
          assertEquals(3, keyValues.size(), 0);
          assertTrue(keyValues.containsAll(Arrays.asList(6, 7, 8)));
        }
      }