#!/usr/bin/env python # coding: utf-8 #

Table of Contents

#
# # Aerospike Basic Operations # # Basic CRUD (Create, Read, Update, and Delete) operations in Aerospike, and how multiple operations on a record are performed in a single request. #
# This notebook requires Aerospike datbase running on localhost and that python and the Aerospike python client have been installed (`pip install aerospike`). Visit [Aerospike notebooks repo](https://github.com/aerospike-examples/interactive-notebooks) for additional details and the docker container. # ### Ensure the database is running # This notebook requires that Aerospike datbase is running. # In[1]: get_ipython().system('asd >& /dev/null') get_ipython().system('pgrep -x asd >/dev/null && echo "Aerospike database is running!" || echo "**Aerospike database is not running!**"') # ### Initialize the client # Initialize the client and connect it to the database. # In[2]: import aerospike import sys config = { 'hosts': [ ('127.0.0.1', 3000) ] } try: client = aerospike.client(config).connect() except: import sys print("failed to connect to the cluster with", config['hosts']) sys.exit(1) print('Client initialized and connected to database') # ## Understanding Records in Aerospike # Data in Aerospike consists of records. A record belongs to a namespace (equivalent to a database) and optionally to a set (equivalent to a table). A record has multiple bins (or fields), which are named, strongly-typed containers that hold both atomic (string, integer, bytes) and complex (map, list) data types. # # A record has two metadata values: # - generation: the number of times it has been modified # - ttl: seconds remaining until record expiration (default = 0; never expire) # # Expired records are garbage-collected by the database. On a write or touch operations, a record's ttl is updated based on the specified policy. # # ### Record Structure # The following code cell illustrates the record structure. # # Note: # - key: (namespace, set, user_key, digest), digest is computed from the first three elements. # - metadata: {'gen': generation, 'ttl': ttl} # - bins: key-value pairs of data bins dictionary # # And also: # - digest (the bytearray in output) is the actual record unique identifier. # - user_key input to produce digest may or may not be stored with the record, and is governed by the key policy. # # In[3]: # Record structure # key: (namespace, set, user_key, digest), digest is computed from first three values # metadata: {'gen': generation, 'ttl': ttl} # bins: key-value pairs of data bins dictionary namespace = 'test' demoset = 'demo' user_key = 'foo' meta = {'ttl': 0} bins = {'name': 'John Doe', 'age': 15, 'gpa': 4.3 } policy = {'key': aerospike.POLICY_KEY_SEND} # policy to store the user_key along with the record # insert/update the record try: client.put((namespace, demoset, user_key), bins, meta, policy) except: print('failed to put record') sys.exit(1) print('Successfully wrote the record.') # read back the record try: (key, metadata, bins)= client.get((namespace, demoset, user_key), policy) except: print('failed to get record') sys.exit(1) print('Successfully read the record.') print ('Key: ', key) print ('Metadata: ', metadata) print ('Bins: ', bins) # ## Writing Records # The Python client's "put" operations is used to write data to the Aerospike cluster. # # ### Defining the Key # As described above, the key tuple serves as the record's unique identifier. # # Below we define a key tuple in the set "characters" with the user key "bender" in namespace "test". # In[4]: # create the key tuple identifying the record key = ('test', 'characters', 'bender') # ### Specifying Record Data # Specify record data in a dict, where the top-level object fields represent the bin names, and field values correspond to bin values. # # This example writes six bins: name, serialnum, lastsentence, composition, apartment, and quote_cnt. # In[5]: # The record data to write to the cluster bins = { 'name': 'Bender', 'serialnum': 2716057, 'lastsentence': { 'BBS': "Well, we're boned", 'TBwaBB': 'I love you, meatbags!', 'BG': 'Whip harder, Professor!', 'ltWGY': 'Into the breach, meatbags. Or not, whatever'}, 'composition': [ "40% zinc", "40% titanium", "30% iron", "40% dolomite" ], 'apartment': bytearray(b'\x24'), 'quote_cnt': 47 } # ### Storing the Record # Store the record in the database with put. # In[6]: # Put the record to the database. try: client.put(key, bins) except: print('failed to put record') sys.exit(1) print('Successfully stored the record.') try: (key, metadata, bins) = client.get(key, policy) except: print('failed to get record') sys.exit(1) print ('Bins: ', bins) # ### Appending, Prepending and Incrementing a Bin # The following APIs are used to prepend and append string bins, and increment integer bins: # In[7]: try: client.prepend(key, 'name', 'Dr. ') client.append(key, 'name', ' Bending Rodriguez') client.increment(key, 'quote_cnt', 3) except: print('failed to get record') sys.exit(1) try: (key, metadata, bins)= client.get(key, policy) except: print('failed to get record') sys.exit(1) print ('Bins: ', bins) # ## Reading Records # There are multiple ways to read records from Aerospike database, "get" being the simplest one. # You will need the key as the record unique identifier as discussed above. Note the record was written above. # # It returns: # - key — The key tuple of the record that was read. # - meta — The dict containing the record metadata gen and ttl fields. # - bins — The dict containing the bins of the record. # # Meta and bins are None if the record is not found. # In[8]: # key of the record key = ('test', 'demo', 'foo') # Retrieve the record using the key. try: (key, meta, bins) = client.get(key, policy) except: print('failed to get record') sys.exit(1) print('Successfully read the record.') print ('Key: ', key) print ('Metadata: ', metadata) print ('Bins: ', bins) # ### Projecting the Bins of a Record # It is possible to project or read specific bins of a record. The following example illustrates this. Note that the second argument is a tuple of bin names to project. # In[9]: # retrieve only specified bins in the record try: (key, meta, bins) = client.select(('test','demo','foo'), ('age', 'gpa')) except: print('failed to get record') sys.exit(1) print ('Bins: ', bins) # ### Checking if a Record Exists # Use "exists" to check existence of a record in the database. Note, meta is None if the record is not found. # In[10]: # Retrieve the record using a non-existent key. (key, metadata) = client.exists(('test','demo','foo'), policy) print('User-key, Metadata: ', key[2], metadata) (key, metadata) = client.exists(('test','demo','nonexistent'), policy) print('User-key, Metadata: ', key[2], metadata) # ### Batch Read Operations # The get_many and exists_many batch operations allow the application to access multiple records. # In[11]: import pprint pp = pprint.PrettyPrinter(depth=4) keys = [] for i in range(1,3): key = ('test', 'demo', 'key' + str(i)) client.put(key, {'batch': i}, policy) for i in range(1,4): key = ('test', 'demo', 'key' + str(i)) keys.append(key) records = client.get_many(keys) pp.pprint (records) # ## Deleting Records # To delete records from the database, there is "remove" operation. # # An exception is thrown if the record does not exist. # In[12]: # Key of the record to be deleted key1 = ('test', 'demo', 'key1') # Delete the record try: client.remove(key1) except: print('failed to delete record: ', key1) sys.exit(1) (key, metadata) = client.exists(key1, policy) print('Key, metadata: ', key1, metadata) print('Successfully deleted ', key1) # will throw an exception nokey = ('test', 'demo', 'non-existent') try: client.remove(key) except: print('failed to delete record: ', nokey) # ## Multiple Operations on a Single Record # Multiple operations on a single a record can be conveniently performed in a single transaction. Multiple updates as well as reads of a record may be performed atomically in a single transaction. Operations are performed in the order they are specified. Below is a full example of a multi-op request. # In[13]: from __future__ import print_function import aerospike from aerospike_helpers.operations import operations as op_helpers from aerospike import exception as ex import sys config = { 'hosts': [('127.0.0.1', 3000)] } client = aerospike.client(config).connect() try: key = ('test', 'demo', 1) client.put(key, {'age': 25, 'career': 'delivery boy'}) ops = [ op_helpers.increment("age", 1000), op_helpers.write("name", "J."), op_helpers.prepend("name", "Phillip "), op_helpers.append("name", " Fry"), op_helpers.read("name"), op_helpers.read("career"), op_helpers.read("age") ] (key, metadata, bins) = client.operate(key, ops, {'ttl':0}, {'total_timeout':500}) except ex.AerospikeError as e: print("Error: {0} [{1}]".format(e.msg, e.code)) sys.exit(1) print('Key: ', key) print('--------------------------') print('Metadata: ', metadata) print('--------------------------') print('Bins: ', bins) # shows only bins specified in read operations # ## Multi-Op Example: Read and Delete in One Request # The example shows reading and deleting a record in a single operation. The record was create above. # In[14]: from aerospike_helpers.operations import operations key = ('test', 'demo', 1) ops = [ operations.read('name'), operations.delete() ] _, _, bins = client.operate(key, ops) print('Bins: ', bins) # will throw an exception (key_, metadata) = client.exists(key, policy) print('Key, metadata: ', key, metadata) print('Successfully deleted ', key) # ## Next steps # # Visit [Aerospike notebooks repo](https://github.com/aerospike-examples/interactive-notebooks) to run additional Aerospike notebooks. To run a different notebook, download the notebook from the repo to your local machine, and then click on File->Open, and select Upload. #