Iterating with Nested Tables

When you are iterating over a table, or performing a multi-get operation, by default only rows are retrieved from the table on which you are operating. However, you can use MultiRowOptions to specify that parent and child tables are to be retrieved as well.

When you do this, parent tables are retrieved first, then the table you are operating on, then child tables. In other words, the tables' hierarchical order is observed.

The parent and child tables retrieved are identified using a list of table names, which is then provided to the MultiRowOpetions object's ONDB_INCLUDED_TABLES property.

When operating on rows retrieved from multiple tables, it is your responsibility to determine which table the row belongs to.

For example, suppose you create a table with a child and grandchild table like this:

CREATE TABLE prodTable (
    prodType STRING,
    typeDescription STRING,
    PRIMARY KEY (prodType)
) 
CREATE TABLE prodTable.prodCategory (
    categoryName STRING,
    categoryDescription STRING,
    PRIMARY KEY (categoryName)
) 
CREATE TABLE prodTable.prodCategory.item (
    itemSKU STRING,
    itemDescription STRING,
    itemPrice FLOAT,
    vendorUID STRING,
    inventoryCount INTEGER,
    PRIMARY KEY (itemSKU)
) 

With tables containing data like this:

from nosqldb import Factory
from nosqldb import IllegalArgumentException
from nosqldb import ProxyConfig
from nosqldb import StoreConfig
from nosqldb import ONDB_INCLUDED_TABLES

import logging
import os
import sys

# locations where our store and proxy can be found
kvlite = 'localhost:5000'
proxy = 'localhost:7010'

# set logging level to debug and log to stdout
def setup_logging():
    rootLogger = logging.getLogger()
    rootLogger.setLevel(logging.DEBUG)

    logger = logging.StreamHandler(sys.stdout)
    logger.setLevel(logging.DEBUG)
    formatter = logging.Formatter('\t%(levelname)s - %(message)s')
    logger.setFormatter(formatter)
    rootLogger.addHandler(logger)

# configure and open the store
def open_store():
    kvstoreconfig = StoreConfig('kvstore', [kvlite])
    return Factory.open(proxy, kvstoreconfig)

def display_row(row):
    try:
        ## Our code must track which table we are displaying.
        ## Use get_table_name() for this purpose.

        if row.get_table_name() == 'prodTable':
            print "\nType: %s" % row['prodType']
            print "Description: %s" % row['typeDescription']
        elif row.get_table_name() == 'prodTable.prodCategory':
            print "\tCategory: %s" % row['categoryName']
            print "\tDescription: %s" % row['categoryDescription']
        else:
            print "\t\tSKU: %s" % row['itemSKU']
            print "\t\tDescription: %s" % row['itemDescription']
            print "\t\tPrice: %s" % row['itemPrice']
            print "\t\tVendor UID: %s" % row['vendorUID']
            print "\t\tInventory Count: %s" % row['inventoryCount']
    except KeyError, ke:
        logging.error("Row display failed. Bad key: %s" % ke.message)

def do_store_ops(store):
    try:
        key_d = {}

        ## Identify the child tables to include in the retrieval.
        incTables = ["prodTable.prodCategory", 
                     "prodTable.prodCategory.item"]

        mro = {ONDB_INCLUDED_TABLES : incTables}

        row_list = store.table_iterator("prodTable", key_d, False, 
                mro)
        if not row_list:
            logging.debug("Table retrieval failed")
        else:
            logging.debug("Table retrieval succeeded.")
            for r in row_list:
                display_row(r)
    except IllegalArgumentException, iae:
        logging.error("Row retrieval failed.")
        logging.error(iae.message)
        return
    except KeyError, ke:
        logging.error("Row display failed. Bad key: %s" % ke.message)

if __name__ == '__main__':

    setup_logging()
    store = open_store()
    do_store_ops(store)
    store.close()