Indexing Maps

Indexing by Map Keys
Indexing by Map Values
Indexing by a Specific Map Key Name
Indexing by Map Key and Value

You can create an index on a map field so long as the map contains scalar data, or contains a record with scalar fields.

Note

You cannot index a map or array that is nested beneath another map or array. This is not allowed because of the potential for an excessively large number of index entries.

To create the index, define the map as normal. Once the map is defined for the table, there are several different ways to index it:

Indexing by Map Keys

You can create indexes based on a map's keys without regard to the corresponding values.

Be aware that creating an index like this can potentially result in multiple index entries for each row, which can lead to very large indexes. In addition, the same row can appear in a result set, so the duplicate entries must be handled by your application.

First create the table:

CREATE TABLE myMapTable (
    uid INTEGER,
    testMap MAP(INTEGER),
    PRIMARY KEY(uid)
) 

Once the table has been added to the store, create the index using the KEYOF statement:

CREATE INDEX mapKeyIndex on myMapTable (KEYOF(testMap)) 

Data is retrieved if the table row contains the identified map with the identified key. So, for example, if you create a series of table rows like this:

TableAPI tableH = kvstore.getTableAPI();

Table myTable = tableH.getTable("myMapTable");

Row row = myTable.createRow();
row.put("uid", 12345);
MapValue mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field2", 2);
mv.put("field3", 3);
tableH.put(row, null, null);

row = myTable.createRow();
row.put("uid", 12);
mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field2", 2);
tableH.put(row, null, null);

row = myTable.createRow();
row.put("uid", 666);
mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field3", 4);
tableH.put(row, null, null); 

then you can retrieve any table rows that contain the map with any key currently in use by the map. For example, "field3".

To retrieve data using a map index, you first retrieve the index using its name, and create an instance of IndexKey that you will use to perform the index lookup:

Index mapIndex = myTable.getIndex("mapKeyIndex");
IndexKey indexKey = mapIndex.createIndexKey(); 

Next you create and populate a MapValue instance, using the MapValue.putNull() method. This method allows you to retrieve the map entry without regard to the value part of the entry. When you perform the index lookup, the only records that will be returned will be those which have a map with the specified key name:

MapValue mv = indexKey.putMap("testMap");
mv.putNull("field3"); 

After that, you retrieve the matching table rows, and iterate over them in the same way you would any other index type. For example:

TableIterator<Row> iter = tableH.tableIterator(indexKey, null, null);
System.out.println("Results for testMap field3: ");
try {
    while (iter.hasNext()) {
        Row rowRet = iter.next();
        int uid = rowRet.get("uid").asInteger().get();
        System.out.println("uid: " + uid);
        MapValue mapRet = rowRet.get("testMap").asMap();
        System.out.println("testMap: " + mapRet.toString());
    }
} finally {
    if (iter != null) {
        iter.close();
    }
} 

Indexing by Map Values

You can create indexes based on the values contained in a map without regard to the keys in use.

Be aware that creating an index like this can potentially result in multiple index entries for each row, which can lead to very large indexes. In addition, the same row can appear in a result set, so the duplicate entries must be handled by your application.

First create the table:

CREATE TABLE myMapTable (
    uid INTEGER,
    testMap MAP(INTEGER),
    PRIMARY KEY(uid)
) 

Once the table has been added to the store, create the index using the ELEMENTOF statement:

CREATE INDEX mapElementIndex on myMapTable (ELEMENTOF(testMap)) 

Data is retrieved if the table row contains the identified map with the identified value. So, for example, if you create a series of table rows like this:

TableAPI tableH = kvstore.getTableAPI();

Table myTable = tableH.getTable("myMapTable");

Row row = myTable.createRow();
row.put("uid", 12345);
MapValue mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field2", 2);
mv.put("field3", 3);
tableH.put(row, null, null);

row = myTable.createRow();
row.put("uid", 12);
mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field2", 2);
tableH.put(row, null, null);

row = myTable.createRow();
row.put("uid", 666);
mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field3", 4);
tableH.put(row, null, null); 

then you can retrieve any table rows that contain the map with any value currently in use by the map. For example, a value of "2".

To retrieve data using a map index, you first retrieve the index using its name, and create an instance of IndexKey that you will use to perform the index lookup:

Index mapIndex = myTable.getIndex("mapElementIndex");
IndexKey indexKey = mapIndex.createIndexKey(); 

Next you create and populate a MapValue instance, using the MapValue.put() method. For the key value for this method, use MapValue.ANONYMOUS. For the value field, specify 2. When you perform the index lookup, the only records that will be returned will be those which have a map with a value of 2.

MapValue mv = indexKey.putMap("testMap");
mv.put(MapValue.ANONYMOUS, 2); 

After that, you retrieve the matching table rows, and iterate over them in the same way you would any other index type. For example:

TableIterator<Row> iter = tableH.tableIterator(indexKey, null, null);
System.out.println("Results for testMap value 2: ");
try {
    while (iter.hasNext()) {
        Row rowRet = iter.next();
        int uid = rowRet.get("uid").asInteger().get();
        System.out.println("uid: " + uid);
        MapValue mapRet = rowRet.get("testMap").asMap();
        System.out.println("testMap: " + mapRet.toString());
    }
} finally {
    if (iter != null) {
        iter.close();
    }
} 

Indexing by a Specific Map Key Name

You can create an index based on a specified map key name. Any map entries containing the specified key name are indexed. This can create a small and very efficient index because the index does not contain every key/value pair contained by the map fields. Instead, it just contains those map entries using the identified key, which results in at most a single index entry per row.

To create the index, first create the table:

CREATE TABLE myMapTable (
    uid INTEGER,
    testMap MAP(INTEGER),
    PRIMARY KEY(uid)
) 

Once the table has been added to the store, create the index by specifying the key name you want indexed using dot notation. In this example, we will index the key name of "field3":

CREATE INDEX mapFieldIndex on myMapTable (testMap.field3) 

Data is retrieved if the table row contains the identified map with the indexed key and a specified value. So, for example, if you create a series of table rows like this:

TableAPI tableH = kvstore.getTableAPI();

Table myTable = tableH.getTable("myMapTable");

Row row = myTable.createRow();
row.put("uid", 12345);
MapValue mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field2", 2);
mv.put("field3", 3);
tableH.put(row, null, null);

row = myTable.createRow();
row.put("uid", 12);
mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field2", 2);
tableH.put(row, null, null);

row = myTable.createRow();
row.put("uid", 666);
mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field3", 4);
tableH.put(row, null, null); 

then you can retrieve any table rows that contain the map with key "field3" (because that is what you indexed) when "field3" maps to a specified value — such as "3". If you try to do an index lookup on, for example, "field2" then that will fail because you did not index "field2".

To retrieve data using a map index, you first retrieve the index using its name and create an instance of IndexKey that you will use to perform the index lookup:

Index mapIndex = myTable.getIndex("mapFieldIndex");
IndexKey indexKey = mapIndex.createIndexKey(); 

Next you create and populate a MapValue instance using MapValue.put(). When you perform the index lookup, the only records that will be returned will be those which have a map with the matching key name and corresponding value.

MapValue mv = indexKey.putMap("testMap");
mv.put("field3", 3); 

After that, you retrieve the matching table rows, and iterate over them in the same way you would any other index type. For example:

TableIterator<Row> iter = tableH.tableIterator(indexKey, null, null);
System.out.println("Results for testMap field3, value 3: ");
try {
    while (iter.hasNext()) {
        Row rowRet = iter.next();
        int uid = rowRet.get("uid").asInteger().get();
        System.out.println("uid: " + uid);
        MapValue mapRet = rowRet.get("testMap").asMap();
        System.out.println("testMap: " + mapRet.toString());
    }
} finally {
    if (iter != null) {
        iter.close();
    }
} 

Indexing by Map Key and Value

In the previous section, we showed how to create a map index by specifying a pre-determined key name. This allows you to perform map index look ups by providing both key and value, but the index lookup will only be successful if the specified key is the key that you indexed.

You can do the same thing in a generic way by indexing every key/value pair in your map. The result is a more flexible index, but also an index that is potentially much larger than the previously described method. It is likely to result in multiple index entries per row.

To create an index based on every key/value pair used by the map field, first create the table:

CREATE TABLE myMapTable (
    uid INTEGER,
    testMap MAP(INTEGER),
    PRIMARY KEY(uid)
) 

Once the table has been added to the store, create the index by using both the KEYOF and ELEMENTOF keywords:

CREATE INDEX mapKeyValueIndex on myMapTable \
(KEYOF(testMap),ELEMENTOF(testmap)) 

Data is retrieved if the table row contains the identified map with the identified key and the identified value. So, for example, if you create a series of table rows like this:

TableAPI tableH = kvstore.getTableAPI();

Table myTable = tableH.getTable("myMapTable");

Row row = myTable.createRow();
row.put("uid", 12345);
MapValue mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field2", 2);
mv.put("field3", 3);
tableH.put(row, null, null);

row = myTable.createRow();
row.put("uid", 12);
mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field2", 2);
tableH.put(row, null, null);

row = myTable.createRow();
row.put("uid", 666);
mv = row.putMap("testMap");
mv.put("field1", 1);
mv.put("field3", 4);
tableH.put(row, null, null); 

then you can retrieve any table rows that contain the map with specified key/value pairs — for example, key "field3" and value "3".

To retrieve data using a map index, you first retrieve the index using its name and create an instance of IndexKey that you will use to perform the index lookup:

Index mapIndex = myTable.getIndex("mapKeyValueIndex");
IndexKey indexKey = mapIndex.createIndexKey(); 

Next you create and populate a MapValue instance using MapValue.put(). When you perform the index lookup, the only records that will be returned will be those which have a map with the matching key/value pair.

MapValue mv = indexKey.putMap("testMap");
mv.putNull("field3");
mv.put(MapValue.ANONYMOUS, 3); 

After that, you retrieve the matching table rows, and iterate over them in the same way you would any other index type. For example:

TableIterator<Row> iter = tableH.tableIterator(indexKey, null, null);
System.out.println("Results for testMap field3, value 3: ");
try {
    while (iter.hasNext()) {
        Row rowRet = iter.next();
        int uid = rowRet.get("uid").asInteger().get();
        System.out.println("uid: " + uid);
        MapValue mapRet = rowRet.get("testMap").asMap();
        System.out.println("testMap: " + mapRet.toString());
    }
} finally {
    if (iter != null) {
        iter.close();
    }
}