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:

function writeRow(store, uid, map) {

   var row = {
       uid: uid,
       testMap: map
   };
   store.put('myMapTable', row,
           function (err) {
                if (err)
                    throw err;
                else {
                    console.log("Row inserted.");
                }
           });
}

...
// Store handle configuration and open skipped for brevity
...

store.on('open', function () {
   console.log('Store opened');

   writeRow(store, 12345, {field1: 1, field2: 2, field3: 3});
   writeRow(store, 12, {field1: 1, field2: 2});
   writeRow(store, 666, {field1: 1, field3: 4});

   store.close();

}).on('close', function() {
    console.log('Store closed.');
}).on('error', function(error) {
    console.log(error);
});
store.open(); 

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

Note that we use a simple Javascript object to represent the map. Because the index we are using is based on the map's key, we can provide any value we want for the map's key value. Here we just use null, but any other value would provide identical results.

function getIndexByFieldName(store, fieldName) {
   var obj = {}
   obj[fieldName] = null;
   var indexKey1 = {testMap: obj}

   store.indexIterator('myMapTable', 'mapKeyIndex',      
        {indexKey: indexKey1},
        function (err, iterator) {
              iterator.forEach(function (err, currentRow) {
                 console.log(currentRow.row);
              });
        }
    );

}

...
// Store handle configuration and open skipped for brevity
...

store.on('open', function () {
   console.log('Store opened');

   getIndexByFieldName(store, "field3");

   store.close();

}).on('close', function() {
    console.log('Store closed.');
}).on('error', function(error) {
    console.log(error);
});
store.open(); 

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:

function writeRow(store, uid, map) {

   var row = {
       uid: uid,
       testMap: map
   };
   store.put('myMapTable', row,
           function (err) {
                if (err)
                    throw err;
                else {
                    console.log("Row inserted.");
                }
           });
}

...
// Store handle configuration and open skipped for brevity
...

store.on('open', function () {
   console.log('Store opened');

   writeRow(store, 12345, {field1: 1, field2: 2, field3: 3});
   writeRow(store, 12, {field1: 1, field2: 2});
   writeRow(store, 666, {field1: 1, field3: 4});

   store.close();

}).on('close', function() {
    console.log('Store closed.');
}).on('error', function(error) {
    console.log(error);
});
store.open(); 

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".

Notice in the following example that we use the special string "[]" for the index key's field value. The field name must be that string or we will not access the proper index.

function getIndexByMapValue(store, mv) {
   var obj = {};
   obj["[]"] = mv;
   var indexKey1 = {testMap: obj };

   store.indexIterator('myMapTable', 'mapElementIndex',
        {indexKey: indexKey1},
        function (err, iterator) {
              iterator.forEach(function (err, currentRow) {
                 console.log(currentRow.row);
              });
        }
    );

}

...
// Store handle configuration and open skipped for brevity
...

store.on('open', function () {
   getIndexByMapValue(store, 2);
   store.close();
}).on('close', function() {
    console.log('Store closed.');
}).on('error', function(error) {
    console.log(error);
});
store.open(); 

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 mapField3Index 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:

function writeRow(store, uid, map) {

   var row = {
       uid: uid,
       testMap: map
   };
   store.put('myMapTable', row,
           function (err) {
                if (err)
                    throw err;
                else {
                    console.log("Row inserted.");
                }
           });
}

...
// Store handle configuration and open skipped for brevity
...

store.on('open', function () {
   console.log('Store opened');

   writeRow(store, 12345, {field1: 1, field2: 2, field3: 3});
   writeRow(store, 12, {field1: 1, field2: 2});
   writeRow(store, 666, {field1: 1, field3: 4});

   store.close();

}).on('close', function() {
    console.log('Store closed.');
}).on('error', function(error) {
    console.log(error);
});
store.open(); 

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".

function getIndexByField3KeyName(store, mv) {
  var indexKey1 = {testMap: {field3: mv}};

   store.indexIterator('myMapTable', 'mapField3Index',
        {indexKey: indexKey1},
        function (err, iterator) {
              iterator.forEach(function (err, currentRow) {
                 console.log(currentRow.row);
              });
        }
    );

}

...
// Store handle configuration and open skipped for brevity
...

store.on('open', function () {
   console.log('Store opened');
   getIndexByField3KeyName(store, 3);
   store.close();
}).on('close', function() {
    console.log('Store closed.');
}).on('error', function(error) {
    console.log(error);
});
store.open(); 

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:

function writeRow(store, uid, map) {

   var row = {
       uid: uid,
       testMap: map
   };
   store.put('myMapTable', row,
           function (err) {
                if (err)
                    throw err;
                else {
                    console.log("Row inserted.");
                }
           });
}

...
// Store handle configuration and open skipped for brevity
...

store.on('open', function () {
   console.log('Store opened');

   writeRow(store, 12345, {field1: 1, field2: 2, field3: 3});
   writeRow(store, 12, {field1: 1, field2: 2});
   writeRow(store, 666, {field1: 1, field3: 4});

   store.close();

}).on('close', function() {
    console.log('Store closed.');
}).on('error', function(error) {
    console.log(error);
});
store.open(); 

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 based on this kind of an index, you must provide:

  • the special string '[]' with the desired map value; and

  • the field name with null for a map value.

You do this in node.js in the following way:

function getIndexByKeyAndValue(store, fn, mv) {

   // declare and initialize the object
   var indexKey1 = {};
   indexKey1["testMap"] = {};

   // Now set the information to the object so that the
   // proper index is used, and data is retrieved by our
   // desired values.
   indexKey1.testMap["[]"] = mv;  // set the map value
   indexKey1.testMap[fn] = null;  // set the field name

   store.indexIterator('myMapTable', 'mapKeyValueIndex',
        {indexKey: indexKey1},
        function (err, iterator) {
              iterator.forEach(function (err, currentRow) {
                 console.log(currentRow.row);
              });
        }
    );

}

...
// Store handle configuration and open skipped for brevity
...

store.on('open', function () {
   console.log('Store opened');
   getIndexByKeyAndValue(store, "field3", 3);
   store.close();
}).on('close', function() {
    console.log('Store closed.');
}).on('error', function(error) {
    console.log(error);
});
store.open();