Read and Write in Kiji
Now that KijiSchema is managing your phonebook table, the next step is to start writing and reading contacts.
Writing to a Table
Clearly, you need a way to add your ever-increasing set of friends to the phonebook. KijiSchema supports writing to Kiji tables with the KijiTableWriter
class. The phonebook example includes code that uses a KijiTableWriter
to write to the phonebook table.
AddEntry.java
The class AddEntry.java
is included in the phonebook example source (located under $KIJI_HOME/examples/phonebook/src/main/java/org/kiji/examples/phonebook/
). It implements a command-line tool that asks a user for contact information and then uses that information to populate the columns in a row in the Kiji table phonebook
for that contact. To start, AddEntry.java
loads an HBase configuration.
setConf(HBaseConfiguration.addHbaseResources(getConf()));
The code then connects to Kiji and opens the phonebook table for writing. A Kiji
instance is specified by a KijiURI
. A Kiji URI specifies an HBase cluster to connect to (identified by its Zookeeper quorum) and a Kiji instance name. The value of KConstants.DEFAULT_INSTANCE_NAME
is "default"
. For example, if ZooKeeper is running on zkhost:2181
, the name of the default Kiji instance on the cluster would be kiji://zkhost:2181/default
.
Rather than specify a ZooKeeper cluster yourself, you can rely on the quorum specified in your hbase-site.xml
file by using the “hostname” of .env
, like this: kiji://.env/default
.
To create a KijiURI
, you use a KijiURI.KijiURIBuilder
instance. By default, this will use the ".env"
pseudo-host so that you connect to your normal HBase cluster.
kiji = Kiji.Factory.open(
KijiURI.newBuilder().withInstanceName(KConstants.DEFAULT_INSTANCE_NAME).build(),
getConf());
table = kiji.openTable(TABLE_NAME); // TABLE_NAME is "phonebook"
writer = table.openTableWriter();
Adding the phonebook entry
We then create an EntityId
using the contact’s first and last name. The EntityId
uniquely identifies the row for the contact in the Kiji table.
final EntityId user = table.getEntityId(first + "," + last);
We write the contact information gained from the user to the appropriate columns in the contact’s row of the Kiji table phonebook
. The column names are specified as constants in the Fields.java
class. For example, the first name is written as:
writer.put(user, Fields.INFO_FAMILY, Fields.FIRST_NAME, timestamp, first);
Finalization
We are done with the Kiji instance, table and writer we opened earlier. We close or release these objects to free resources (for example, connections to HBase) that they use. We close or release these objects in the reverse order we opened them in.
ResourceUtils.closeOrLog(writer);
ResourceUtils.releaseOrLog(table);
ResourceUtils.releaseOrLog(kiji);
Something important to note is that the Kiji instance and Kiji table are released rather than closed. Kiji instances and tables are often long-lived objects that many aspects of your system may hold reference to. Rather than require that you define a single “owner” of this object who closes it when the system is finished using it, you can use reference counting to manage this object’s lifetime.
When a Kiji
instance is created with Kiji.Factory.open()
, or a ‘KijiTable’ is opened with Kiji.openTable(name)
, it has an automatic reference count of 1. You should call kiji.release()
or table.release()
or use ResourceUtils
.releaseOrLog(kiji)
or ResourceUtils.releaseOrLog(table) to discard these reference.
If another class or method gets a reference to an already-opened Kiji instance, you should call kiji.retain()
to increment its reference count. That same class or method is responsible for calling kiji.release()
when it no longer holds the reference.
A Kiji
object or KijiTable
will close itself and free its underlying resources when its reference count drops to 0.
Running the Example
You run the class AddEntry
with the kiji
command-line tool as follows:
$KIJI_HOME/bin/kiji jar \
$KIJI_HOME/examples/phonebook/lib/kiji-phonebook-1.0.0-rc6.jar \
org.kiji.examples.phonebook.AddEntry
The syntax shown here is the preferred mechanism to run your own main(...)
method with Kiji and its dependencies properly on the classpath.
The interactive prompts (with sample responses) should look like:
First name: Renuka
Last name: Apte
Email address: ra@wibidata.com
Telephone: 415-111-2222
Address line 1: 375 Alabama St
Apartment:
Address line 2:
City: SF
State: CA
Zip: 94110
Verify
Now we can verify that our entry got into the phonebook table.
Beforehand, you must tell kiji scan
where the org.kiji.examples.phonebook.Address
Avro record class (mentioned in the DDL and used by AddEntry
) is. If you have not already done so, put the phonebook jar file on your Kiji classpath:
export KIJI_CLASSPATH="$KIJI_HOME/examples/phonebook/lib/*"
Now use kiji scan
:
$KIJI_HOME/bin/kiji scan kiji://.env/default/phonebook
Scanning kiji table: kiji://localhost:2181/default/phonebook/
entity-id=hbase=hex:17dce7850f7bb653469ab526a58c815b [1363224175538] info:firstname
Renuka
entity-id=hbase=hex:17dce7850f7bb653469ab526a58c815b [1363224175538] info:lastname
Apte
entity-id=hbase=hex:17dce7850f7bb653469ab526a58c815b [1363224175538] info:email
ra@wibidata.com
entity-id=hbase=hex:17dce7850f7bb653469ab526a58c815b [1363224175538] info:telephone
415-111-2222
entity-id=hbase=hex:17dce7850f7bb653469ab526a58c815b [1363224175538] info:address
{"addr1": "375 Alabama St", "apt": null, "addr2": null, "city": "SF", "state": "CA", "zip": 94110}
Reading From a Table
Now that we’ve added a contact to your phonebook, we should be able to read this contact from the table. KijiSchema supports reading from Kiji tables with the KijiTableReader
class. We have included an example of retrieving a single contact from the Kiji table using the contact’s first and last names.
Lookup.java
We connect to Kiji and our phonebook table in the same way we did above.
setConf(HBaseConfiguration.create(getConf()));
kiji = Kiji.Factory.open(
KijiURI.newBuilder().withInstanceName(KConstants.DEFAULT_INSTANCE_NAME).build(),
getConf());
table = kiji.openTable(TABLE_NAME); // TABLE_NAME is "phonebook"
Since we are interested in reading from our table, we open a KijiTableReader
.
reader = table.openTableReader();
Looking up the requested entry
Create an EntityId
to retrieve a contact using the contact’s first and last name:
final EntityId entityId = table.getEntityId(mFirst + "," + mLast);
Create a data request to specify the desired columns from the Kiji Table.
final KijiDataRequestBuilder reqBuilder = KijiDataRequest.builder();
reqBuilder.newColumnsDef()
.add(Fields.INFO_FAMILY, Fields.FIRST_NAME)
.add(Fields.INFO_FAMILY, Fields.LAST_NAME)
.add(Fields.INFO_FAMILY, Fields.EMAIL)
.add(Fields.INFO_FAMILY, Fields.TELEPHONE)
.add(Fields.INFO_FAMILY, Fields.ADDRESS);
final KijiDataRequest dataRequest = reqBuilder.build();
We now retrieve our result by passing the EntityId
and data request to our table reader. Doing so results in a KijiRowData
containing the data read from the table.
final KijiRowData rowData = reader.get(entityId, dataRequest);
Running the Example
You can run the following command to perform a lookup using the Lookup.java
example:
$KIJI_HOME/bin/kiji jar \
$KIJI_HOME/examples/phonebook/lib/kiji-phonebook-1.0.0-rc6.jar \
org.kiji.examples.phonebook.Lookup --first=Renuka --last=Apte
Renuka Apte: email=ra@wibidata.com, tel=415-111-2222, addr={"addr1": "375 Alabama St", "apt": null, "addr2": null, "city": "SF", "state": "CA", "zip": 94110}
Phonebook Tutorial
- Overview
- Setup
- Create a Table
- Read and Write in Kiji
- Import Data
- Derive Data
- Use Counters
- Delete Contacts