diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 0d71659b0b..6b91a4930a 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -255,8 +255,84 @@ The following example shows the three modes of serialization: 2. Class-level user functions 3. Instance-level user functions -.. include:: examples/serializer.py - :code: python +.. testcode:: + + import aerospike + import marshal + import json + + # Serializers and deserializers + # Both local and global serializers use json library + # Functions print which one is being used + + def classSerializer(obj): + print("Using class serializer") + return json.dumps(obj) + + def classDeserializer(bytes): + print("Using class deserializer") + return json.loads(bytes) + + def localSerializer(obj): + print("Using local serializer") + return json.dumps(obj) + + def localDeserializer(bytes): + print("Using local deserializer") + return json.loads(bytes) + + # First client has class-level serializer set in aerospike module + aerospike.set_serializer(classSerializer) + aerospike.set_deserializer(classDeserializer) + config = { + 'hosts': [('127.0.0.1', 3000)] + } + client = aerospike.client(config) + + # Second client has instance-level serializer set in client config + config['serialization'] = (localSerializer, localDeserializer) + client2 = aerospike.client(config) + + # Keys: foo1, foo2, foo3 + keys = [('test', 'demo', f'foo{i}') for i in range(1, 4)] + # Tuple is an unsupported type + tupleBin = {'bin': (1, 2, 3)} + + # Use the default, built-in serialization (cPickle) + client.put(keys[0], tupleBin) + (_, _, bins) = client.get(keys[0]) + print(bins) + + # Expected output: + # {'bin': (1, 2, 3)} + + # First client uses class-level, user-defined serialization + # No instance-level serializer was declared + client.put(keys[1], tupleBin, serializer=aerospike.SERIALIZER_USER) + (_, _, bins) = client.get(keys[1]) + # Notice that json-encoding a tuple produces a list + print(bins) + + # Expected output: + # Using class serializer + # Using class deserializer + # {'bin': [1, 2, 3]} + + # Second client uses instance-level, user-defined serialization + # Instance-level serializer overrides class-level serializer + client2.put(keys[2], tupleBin, serializer=aerospike.SERIALIZER_USER) + (_, _, bins) = client2.get(keys[2]) + print(bins) + + # Expected output: + # Using local serializer + # Using local deserializer + # {'bin': [1, 2, 3]} + + # Cleanup + client.batch_remove(keys) + client.close() + client2.close() Records ``foo1`` and ``foo2`` should have different encodings from each other since they use different serializers. (record ``foo3`` uses the same encoding as ``foo2``) @@ -301,8 +377,42 @@ By default: The following example shows several different methods to configuring logging for the Aerospike Python Client: -.. include:: examples/log.py - :code: python +.. testcode:: + + # Enable the logging at application start, before connecting to the server. + import aerospike + + ## SETTING THE LOG HANDLER ## + + # Clears saved log handler and disable logging + aerospike.set_log_handler(None) + + # Set default log handler to print to the console + aerospike.set_log_handler() + + def log_callback(level, func, path, line, msg): + print("[{}] {}".format(func, msg)) + + # Set log handler to custom callback function (defined above) + aerospike.set_log_handler(log_callback) + + + ## SETTING THE LOG LEVEL ## + + # disables log handling + aerospike.set_log_level(aerospike.LOG_LEVEL_OFF) + + # Enables log handling and sets level to LOG_LEVEL_TRACE + aerospike.set_log_level(aerospike.LOG_LEVEL_TRACE) + + # Create a client and connect it to the cluster + # This line will print use log_callback to print logs with a log level of TRACE + config = { + "hosts": [ + ("127.0.0.1", 3000) + ] + } + client = aerospike.client(config) .. py:function:: set_log_handler(log_handler: Optional[Callable[[int, str, str, int, str], None]]) diff --git a/doc/aerospike_helpers.expressions.rst b/doc/aerospike_helpers.expressions.rst index 48eb097167..a1f99beb50 100644 --- a/doc/aerospike_helpers.expressions.rst +++ b/doc/aerospike_helpers.expressions.rst @@ -36,8 +36,64 @@ the command will filter the results. Example: -.. include:: examples/expressions/top.py - :code: python +.. testcode:: + + import aerospike + from aerospike_helpers import expressions as exp + import pprint + + # Connect to database + config = {"hosts": [("127.0.0.1", 3000)]} + client = aerospike.client(config) + + # Write player records to database + keys = [("test", "demo", i) for i in range(1, 5)] + records = [ + {'user': "Chief" , 'scores': [6, 12, 4, 21], 'kd': 1.2}, + {'user': "Arbiter", 'scores': [5, 10, 5, 8] , 'kd': 1.0}, + {'user': "Johnson", 'scores': [8, 17, 20, 5], 'kd': 0.9}, + {'user': "Regret" , 'scores': [4, 2, 3, 5] , 'kd': 0.3} + ] + for key, record in zip(keys, records): + client.put(key, record) + + # Example #1: Get players with a K/D ratio >= 1.0 + + kdGreaterThan1 = exp.GE(exp.FloatBin("kd"), 1.0).compile() + policy = {"expressions": kdGreaterThan1} + brs = client.batch_read(keys, policy=policy) + + # Pretty print records' bins + for br in brs.batch_records: + # error code for FILTERED_OUT = 27 + pprint.pprint(br.record[2] if br.result != 27 else None) + # {'user': 'Chief', 'scores': [6, 12, 4, 21], 'kd': 1.2} + # {'user': 'Arbiter', 'scores': [5, 10, 5, 8], 'kd': 1.0} + # None + # None + + # Example #2: Get player with scores higher than 20 + # By nesting expressions, we can create complicated filters + + # Get top score + getTopScore = exp.ListGetByRank( + None, + aerospike.LIST_RETURN_VALUE, + exp.ResultType.INTEGER, + -1, + exp.ListBin("scores") + ) + # ...then compare it + scoreHigherThan20 = exp.GE(getTopScore, 20).compile() + policy = {"expressions": scoreHigherThan20} + brs = client.batch_read(keys, policy=policy) + + for br in brs.batch_records: + pprint.pprint(br.record[2] if br.result != 27 else None) + # {'user': 'Chief', 'scores': [6, 12, 4, 21], 'kd': 1.2} + # None + # {'user': 'Johnson', 'scores': [8, 17, 20, 5], 'kd': 0.9} + # None Currently, Aerospike expressions are supported for: * Record commands diff --git a/doc/client.rst b/doc/client.rst index 2e18adf07f..25d0c582ab 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -162,8 +162,19 @@ Record Commands Example: - .. include:: examples/put.py - :code: python + .. testcode:: + + # Insert a record with bin1 + client.put(keyTuple, {'bin1': 4}) + + # Insert another bin named bin2 + client.put(keyTuple, {'bin2': "value"}) + + # Remove bin1 from this record + client.put(keyTuple, {'bin2': aerospike.null()}) + + # Removing the last bin should delete this record + client.put(keyTuple, {'bin1': aerospike.null()}) .. method:: exists(key[, policy: dict]) -> (key, meta) @@ -180,8 +191,27 @@ Record Commands :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. include:: examples/exists.py - :code: python + .. testcode:: + + # Check non-existent record + (key, meta) = client.exists(keyTuple) + + print(key) + print(meta) + + # Check existing record + client.put(keyTuple, {'bin1': 4}) + (key, meta) = client.exists(keyTuple) + + print(key) + print(meta) + + .. testcode:: + + ('test', 'demo', 'key', bytearray(b'...')) + None + ('test', 'demo', 'key', bytearray(b'...')) + {'ttl': 2592000, 'gen': 1} .. versionchanged:: 2.0.3 @@ -196,8 +226,22 @@ Record Commands :raises: :exc:`~aerospike.exception.RecordNotFound`. - .. include:: examples/get.py - :code: python + .. testcode:: + + # Get nonexistent record + try: + client.get(keyTuple) + except ex.RecordNotFound as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + # Error: 127.0.0.1:3000 AEROSPIKE_ERR_RECORD_NOT_FOUND [2] + + # Get existing record + client.put(keyTuple, {'bin1': 4}) + (key, meta, bins) = client.get(keyTuple) + + print(key) # ('test', 'demo', None, bytearray(b'...')) + print(meta) # {'ttl': 2592000, 'gen': 1} + print(bins) # {'bin1': 4} .. versionchanged:: 2.0.0 @@ -215,8 +259,26 @@ Record Commands :raises: :exc:`~aerospike.exception.RecordNotFound`. - .. include:: examples/select.py - :code: python + .. testcode:: + + # Record to select from + client.put(keyTuple, {'bin1': 4, 'bin2': 3}) + + # Only get bin1 + (key, meta, bins) = client.select(keyTuple, ['bin1']) + + # Similar output to get() + print(key) # ('test', 'demo', 'key', bytearray(b'...')) + print(meta) # {'ttl': 2592000, 'gen': 1} + print(bins) # {'bin1': 4} + + # Get all bins + (key, meta, bins) = client.select(keyTuple, ['bin1', 'bin2']) + print(bins) # {'bin1': 4, 'bin2': 3} + + # Get nonexistent bin + (key, meta, bins) = client.select(keyTuple, ['bin3']) + print(bins) # {} .. versionchanged:: 2.0.0 @@ -238,8 +300,29 @@ Record Commands :return: a :ref:`aerospike_record_tuple`. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. include:: examples/operate.py - :code: python + .. testcode:: + + from aerospike_helpers.operations import operations + + # Add name, update age, and return attributes + client.put(keyTuple, {'age': 25, 'career': 'delivery boy'}) + ops = [ + operations.increment("age", 1000), + operations.write("name", "J."), + operations.prepend("name", "Phillip "), + operations.append("name", " Fry"), + operations.read("name"), + operations.read("career"), + operations.read("age") + ] + (key, meta, bins) = client.operate(key, ops) + + print(key) # ('test', 'demo', None, bytearray(b'...')) + # The generation should only increment once + # A transaction is *atomic* + print(meta) # {'ttl': 2592000, 'gen': 2} + print(bins) # Will display all bins selected by read operations + # {'name': 'Phillip J. Fry', 'career': 'delivery boy', 'age': 1025} .. note:: @@ -267,8 +350,27 @@ Record Commands :return: a :ref:`aerospike_record_tuple`. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. include:: examples/operate_ordered.py - :code: python + .. testcode:: + + from aerospike_helpers.operations import operations + + # Add name, update age, and return attributes + client.put(keyTuple, {'age': 25, 'career': 'delivery boy'}) + ops = [ + operations.increment("age", 1000), + operations.write("name", "J."), + operations.prepend("name", "Phillip "), + operations.append("name", " Fry"), + operations.read("name"), + operations.read("career"), + operations.read("age") + ] + (key, meta, bins) = client.operate_ordered(keyTuple, ops) + + # Same output for key and meta as operate() + # But read operations are outputted as bin-value pairs + print(bins) + # [('name': 'Phillip J. Fry'), ('career': 'delivery boy'), ('age': 1025)] .. versionchanged:: 2.1.3 @@ -287,8 +389,20 @@ Record Commands :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. include:: examples/touch.py - :code: python + .. testcode:: + + # Insert record and get its metadata + client.put(keyTuple, bins = {"bin1": 4}) + (key, meta) = client.exists(keyTuple) + print(meta) # {'ttl': 2592000, 'gen': 1} + + # Explicitly set TTL to 120 + # and increment generation + client.touch(keyTuple, 120) + + # Record metadata should be updated + (key, meta) = client.exists(keyTuple) + print(meta) # {'ttl': 120, 'gen': 2} .. method:: remove(key[meta: dict[, policy: dict]]) @@ -304,8 +418,20 @@ Record Commands :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. include:: examples/remove.py - :code: python + .. testcode:: + + # Insert a record + client.put(keyTuple, {"bin1": 4}) + + # Try to remove it with the wrong generation + try: + client.remove(keyTuple, meta={'gen': 5}, policy={'gen': aerospike.POLICY_GEN_EQ}) + except ex.AerospikeError as e: + # Error: AEROSPIKE_ERR_RECORD_GENERATION [3] + print("Error: {0} [{1}]".format(e.msg, e.code)) + + # Remove it ignoring generation + client.remove(keyTuple) .. method:: remove_bin(key, list[, meta: dict[, policy: dict]]) @@ -321,8 +447,19 @@ Record Commands :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. include:: examples/remove_bin.py - :code: python + .. testcode:: + + # Insert record + bins = {"bin1": 0, "bin2": 1} + client.put(keyTuple, bins) + + # Remove bin1 + client.remove_bin(keyTuple, ['bin1']) + + # Only bin2 shold remain + (keyTuple, meta, bins) = client.get(keyTuple) + print(bins) + # {'bin2': 1} .. index:: single: Batched Commands @@ -366,8 +503,64 @@ Batched Commands :raises: A subclass of :exc:`~aerospike.exception.AerospikeError`. See note above :meth:`batch_write` for details. - .. include:: examples/batch_write.py - :code: python + .. testcode:: + + from aerospike_helpers.batch import records as br + from aerospike_helpers.operations import operations as op + + # Keys + # Only insert two records with the first and second key + keyTuples = [ + ('test', 'demo', 'Robert'), + ('test', 'demo', 'Daniel'), + ('test', 'demo', 'Patrick'), + ] + client.put(keyTuples[0], {'id': 100, 'balance': 400}) + client.put(keyTuples[1], {'id': 101, 'balance': 200}) + client.put(keyTuples[2], {'id': 102, 'balance': 300}) + + # Apply different operations to different keys + batchRecords = br.BatchRecords( + [ + # Remove Robert from system + br.Remove( + key = keyTuples[0], + ), + # Modify Daniel's ID and balance + br.Write( + key = keyTuples[1], + ops = [ + op.write("id", 200), + op.write("balance", 100), + op.read("id"), + ], + ), + # Read Patrick's ID + br.Read( + key = keyTuples[2], + ops=[ + op.read("id") + ], + policy=None + ), + ] + ) + + client.batch_write(batchRecords) + + # batch_write modifies its BatchRecords argument. + # Results for each BatchRecord will be set in the result, record, and in_doubt fields. + for batchRecord in batchRecords.batch_records: + print(batchRecord.result) + print(batchRecord.record) + # Note how written bins return None if their values aren't read + # And removed records have an empty bins dictionary + # 0 + # (('test', 'demo', 'Robert', bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) + # 0 + # (('test', 'demo', 'Daniel', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'id': 200, 'balance': None}) + # 0 + # (('test', 'demo', 'Patrick', bytearray(b'...')), {'ttl': 2592000, 'gen': 1}, {'id': 102}) .. note:: Requires server version >= 6.0.0. @@ -413,8 +606,38 @@ Batched Commands :raises: A subclass of :exc:`~aerospike.exception.AerospikeError`. See note above :meth:`batch_write` for details. - .. include:: examples/batch_operate.py - :code: python + .. testcode:: + + from aerospike_helpers.operations import operations as op + + # Insert 3 records + keys = [("test", "demo", f"employee{i}") for i in range(1, 4)] + bins = [ + {"id": 100, "balance": 200}, + {"id": 101, "balance": 400}, + {"id": 102, "balance": 300} + ] + for key, bin in zip(keys, bins): + client.put(key, bin) + + # Increment ID by 100 and balance by 500 for all employees + ops = [ + op.increment("id", 100), + op.increment("balance", 500), + op.read("balance") + ] + + batchRecords = client.batch_operate(keys, ops) + print(batchRecords.result) + # 0 + + # Print each individual transaction's results + # and record if it was read from + for batchRecord in batchRecords.batch_records: + print(f"{batchRecord.result}: {batchRecord.record}") + # 0: (('test', 'demo', 'employee1', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'id': None, 'balance': 700}) + # 0: (('test', 'demo', 'employee2', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'id': None, 'balance': 900}) + # 0: (('test', 'demo', 'employee3', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'id': None, 'balance': 800}) .. note:: Requires server version >= 6.0.0. @@ -432,8 +655,33 @@ Batched Commands :return: an instance of :class:`BatchRecords `. :raises: A subclass of :exc:`~aerospike.exception.AerospikeError`. See note above :meth:`batch_write` for details. - .. include:: examples/batch_apply.py - :code: python + .. testcode:: + + # Insert 3 records + keys = [("test", "demo", f"employee{i}") for i in range(1, 4)] + bins = [ + {"id": 100, "balance": 200}, + {"id": 101, "balance": 400}, + {"id": 102, "balance": 300} + ] + for key, bin in zip(keys, bins): + client.put(key, bin) + + # Apply a user defined function (UDF) to a batch + # of records using batch_apply. + client.udf_put("batch_apply.lua") + + args = ["balance", 0.5, 100] + batchRecords = client.batch_apply(keys, "batch_apply", "tax", args) + + print(batchRecords.result) + # 0 + + for batchRecord in batchRecords.batch_records: + print(f"{batchRecord.result}: {batchRecord.record}") + # 0: (('test', 'demo', 'employee1', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'SUCCESS': 0}) + # 0: (('test', 'demo', 'employee2', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'SUCCESS': 100}) + # 0: (('test', 'demo', 'employee3', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'SUCCESS': 50}) .. include:: examples/batch_apply.lua :code: lua @@ -452,8 +700,29 @@ Batched Commands :return: an instance of :class:`BatchRecords `. :raises: A subclass of :exc:`~aerospike.exception.AerospikeError`. See note above :meth:`batch_write` for details. - .. include:: examples/batch_remove.py - :code: python + .. testcode:: + + # Insert 3 records + keys = [("test", "demo", f"employee{i}") for i in range(1, 4)] + bins = [ + {"id": 100, "balance": 200}, + {"id": 101, "balance": 400}, + {"id": 102, "balance": 300} + ] + for key, bin in zip(keys, bins): + client.put(key, bin) + + batchRecords = client.batch_remove(keys) + + # A result of 0 means success + print(batchRecords.result) + # 0 + for batchRecord in batchRecords.batch_records: + print(batchRecord.result) + print(batchRecord.record) + # 0: (('test', 'demo', 'employee1', bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) + # 0: (('test', 'demo', 'employee2', bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) + # 0: (('test', 'demo', 'employee3', bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) .. index:: single: String Operations @@ -875,8 +1144,16 @@ Info Operations :param TypeExpression expression: the compiled expression. See expressions at :py:mod:`aerospike_helpers`. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. include:: examples/get_expression_base64.py - :code: python + .. testcode:: + + from aerospike_helpers import expressions as exp + + # Compile expression + expr = exp.Eq(exp.IntBin("bin1"), 6).compile() + + base64 = client.get_expression_base64(expr) + print(base64) + # kwGTUQKkYmluMQY= .. versionchanged:: 7.0.0 @@ -910,8 +1187,26 @@ Info Operations .. note:: Requires Aerospike server version >= 3.12 - .. include:: examples/truncate.py - :code: python + .. testcode:: + + import time + + client.put(("test", "demo", "key1"), {"bin": 4}) + + time.sleep(1) + # Take threshold time + current_time = time.time() + time.sleep(1) + + client.put(("test", "demo", "key2"), {"bin": 5}) + + threshold_ns = int(current_time * 10**9) + # Remove all items in set `demo` created before threshold time + # Record using key1 should be removed + client.truncate('test', 'demo', threshold_ns) + + # Remove all items in namespace + # client.truncate('test', None, 0) .. index:: single: Index Operations @@ -1034,8 +1329,19 @@ Index Operations :param list ctx: Aerospike CDT context: generated by aerospike CDT ctx helper :mod:`aerospike_helpers`. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. include:: examples/get_cdtctx_base64.py - :code: python + .. testcode:: + + import aerospike + from aerospike_helpers import cdt_ctx + + config = {'hosts': [('127.0.0.1', 3000)]} + client = aerospike.client(config) + + ctxs = [cdt_ctx.cdt_ctx_list_index(0)] + ctxs_base64 = client.get_cdtctx_base64(ctxs) + print("Base64 encoding of ctxs:", ctxs_base64) + + client.close() .. versionchanged:: 7.1.1 diff --git a/doc/examples/batch_apply.py b/doc/examples/batch_apply.py deleted file mode 100644 index 5b9f8fe6b4..0000000000 --- a/doc/examples/batch_apply.py +++ /dev/null @@ -1,25 +0,0 @@ -# Insert 3 records -keys = [("test", "demo", f"employee{i}") for i in range(1, 4)] -bins = [ - {"id": 100, "balance": 200}, - {"id": 101, "balance": 400}, - {"id": 102, "balance": 300} -] -for key, bin in zip(keys, bins): - client.put(key, bin) - -# Apply a user defined function (UDF) to a batch -# of records using batch_apply. -client.udf_put("batch_apply.lua") - -args = ["balance", 0.5, 100] -batchRecords = client.batch_apply(keys, "batch_apply", "tax", args) - -print(batchRecords.result) -# 0 - -for batchRecord in batchRecords.batch_records: - print(f"{batchRecord.result}: {batchRecord.record}") -# 0: (('test', 'demo', 'employee1', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'SUCCESS': 0}) -# 0: (('test', 'demo', 'employee2', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'SUCCESS': 100}) -# 0: (('test', 'demo', 'employee3', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'SUCCESS': 50}) diff --git a/doc/examples/batch_operate.py b/doc/examples/batch_operate.py deleted file mode 100644 index 5c9fa0eca7..0000000000 --- a/doc/examples/batch_operate.py +++ /dev/null @@ -1,30 +0,0 @@ -from aerospike_helpers.operations import operations as op - -# Insert 3 records -keys = [("test", "demo", f"employee{i}") for i in range(1, 4)] -bins = [ - {"id": 100, "balance": 200}, - {"id": 101, "balance": 400}, - {"id": 102, "balance": 300} -] -for key, bin in zip(keys, bins): - client.put(key, bin) - -# Increment ID by 100 and balance by 500 for all employees -ops = [ - op.increment("id", 100), - op.increment("balance", 500), - op.read("balance") -] - -batchRecords = client.batch_operate(keys, ops) -print(batchRecords.result) -# 0 - -# Print each individual transaction's results -# and record if it was read from -for batchRecord in batchRecords.batch_records: - print(f"{batchRecord.result}: {batchRecord.record}") -# 0: (('test', 'demo', 'employee1', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'id': None, 'balance': 700}) -# 0: (('test', 'demo', 'employee2', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'id': None, 'balance': 900}) -# 0: (('test', 'demo', 'employee3', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'id': None, 'balance': 800}) diff --git a/doc/examples/batch_remove.py b/doc/examples/batch_remove.py deleted file mode 100644 index 14198b0897..0000000000 --- a/doc/examples/batch_remove.py +++ /dev/null @@ -1,21 +0,0 @@ -# Insert 3 records -keys = [("test", "demo", f"employee{i}") for i in range(1, 4)] -bins = [ - {"id": 100, "balance": 200}, - {"id": 101, "balance": 400}, - {"id": 102, "balance": 300} -] -for key, bin in zip(keys, bins): - client.put(key, bin) - -batchRecords = client.batch_remove(keys) - -# A result of 0 means success -print(batchRecords.result) -# 0 -for batchRecord in batchRecords.batch_records: - print(batchRecord.result) - print(batchRecord.record) -# 0: (('test', 'demo', 'employee1', bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) -# 0: (('test', 'demo', 'employee2', bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) -# 0: (('test', 'demo', 'employee3', bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) diff --git a/doc/examples/batch_write.py b/doc/examples/batch_write.py deleted file mode 100644 index 12c1522d2d..0000000000 --- a/doc/examples/batch_write.py +++ /dev/null @@ -1,56 +0,0 @@ -from aerospike_helpers.batch import records as br -from aerospike_helpers.operations import operations as op - -# Keys -# Only insert two records with the first and second key -keyTuples = [ - ('test', 'demo', 'Robert'), - ('test', 'demo', 'Daniel'), - ('test', 'demo', 'Patrick'), -] -client.put(keyTuples[0], {'id': 100, 'balance': 400}) -client.put(keyTuples[1], {'id': 101, 'balance': 200}) -client.put(keyTuples[2], {'id': 102, 'balance': 300}) - -# Apply different operations to different keys -batchRecords = br.BatchRecords( - [ - # Remove Robert from system - br.Remove( - key = keyTuples[0], - ), - # Modify Daniel's ID and balance - br.Write( - key = keyTuples[1], - ops = [ - op.write("id", 200), - op.write("balance", 100), - op.read("id"), - ], - ), - # Read Patrick's ID - br.Read( - key = keyTuples[2], - ops=[ - op.read("id") - ], - policy=None - ), - ] -) - -client.batch_write(batchRecords) - -# batch_write modifies its BatchRecords argument. -# Results for each BatchRecord will be set in the result, record, and in_doubt fields. -for batchRecord in batchRecords.batch_records: - print(batchRecord.result) - print(batchRecord.record) -# Note how written bins return None if their values aren't read -# And removed records have an empty bins dictionary -# 0 -# (('test', 'demo', 'Robert', bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) -# 0 -# (('test', 'demo', 'Daniel', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'id': 200, 'balance': None}) -# 0 -# (('test', 'demo', 'Patrick', bytearray(b'...')), {'ttl': 2592000, 'gen': 1}, {'id': 102}) diff --git a/doc/examples/exists.py b/doc/examples/exists.py deleted file mode 100644 index 5f82f14be7..0000000000 --- a/doc/examples/exists.py +++ /dev/null @@ -1,12 +0,0 @@ -# Check non-existent record -(key, meta) = client.exists(keyTuple) - -print(key) # ('test', 'demo', 'key', bytearray(b'...')) -print(meta) # None - -# Check existing record -client.put(keyTuple, {'bin1': 4}) -(key, meta) = client.exists(keyTuple) - -print(key) # ('test', 'demo', 'key', bytearray(b'...')) -print(meta) # {'ttl': 2592000, 'gen': 1} diff --git a/doc/examples/expressions/top.py b/doc/examples/expressions/top.py deleted file mode 100644 index 60d1415918..0000000000 --- a/doc/examples/expressions/top.py +++ /dev/null @@ -1,56 +0,0 @@ -import aerospike -from aerospike_helpers import expressions as exp -import pprint - -# Connect to database -config = {"hosts": [("127.0.0.1", 3000)]} -client = aerospike.client(config) - -# Write player records to database -keys = [("test", "demo", i) for i in range(1, 5)] -records = [ - {'user': "Chief" , 'scores': [6, 12, 4, 21], 'kd': 1.2}, - {'user': "Arbiter", 'scores': [5, 10, 5, 8] , 'kd': 1.0}, - {'user': "Johnson", 'scores': [8, 17, 20, 5], 'kd': 0.9}, - {'user': "Regret" , 'scores': [4, 2, 3, 5] , 'kd': 0.3} - ] -for key, record in zip(keys, records): - client.put(key, record) - -# Example #1: Get players with a K/D ratio >= 1.0 - -kdGreaterThan1 = exp.GE(exp.FloatBin("kd"), 1.0).compile() -policy = {"expressions": kdGreaterThan1} -brs = client.batch_read(keys, policy=policy) - -# Pretty print records' bins -for br in brs.batch_records: - # error code for FILTERED_OUT = 27 - pprint.pprint(br.record[2] if br.result != 27 else None) -# {'user': 'Chief', 'scores': [6, 12, 4, 21], 'kd': 1.2} -# {'user': 'Arbiter', 'scores': [5, 10, 5, 8], 'kd': 1.0} -# None -# None - -# Example #2: Get player with scores higher than 20 -# By nesting expressions, we can create complicated filters - -# Get top score -getTopScore = exp.ListGetByRank( - None, - aerospike.LIST_RETURN_VALUE, - exp.ResultType.INTEGER, - -1, - exp.ListBin("scores") - ) -# ...then compare it -scoreHigherThan20 = exp.GE(getTopScore, 20).compile() -policy = {"expressions": scoreHigherThan20} -brs = client.batch_read(keys, policy=policy) - -for br in brs.batch_records: - pprint.pprint(br.record[2] if br.result != 27 else None) -# {'user': 'Chief', 'scores': [6, 12, 4, 21], 'kd': 1.2} -# None -# {'user': 'Johnson', 'scores': [8, 17, 20, 5], 'kd': 0.9} -# None diff --git a/doc/examples/get.py b/doc/examples/get.py deleted file mode 100644 index 313f6cddc6..0000000000 --- a/doc/examples/get.py +++ /dev/null @@ -1,14 +0,0 @@ -# Get nonexistent record -try: - client.get(keyTuple) -except ex.RecordNotFound as e: - print("Error: {0} [{1}]".format(e.msg, e.code)) - # Error: 127.0.0.1:3000 AEROSPIKE_ERR_RECORD_NOT_FOUND [2] - -# Get existing record -client.put(keyTuple, {'bin1': 4}) -(key, meta, bins) = client.get(keyTuple) - -print(key) # ('test', 'demo', None, bytearray(b'...')) -print(meta) # {'ttl': 2592000, 'gen': 1} -print(bins) # {'bin1': 4} diff --git a/doc/examples/get_cdtctx_base64.py b/doc/examples/get_cdtctx_base64.py deleted file mode 100644 index 0df1d7daec..0000000000 --- a/doc/examples/get_cdtctx_base64.py +++ /dev/null @@ -1,11 +0,0 @@ -import aerospike -from aerospike_helpers import cdt_ctx - -config = {'hosts': [('127.0.0.1', 3000)]} -client = aerospike.client(config) - -ctxs = [cdt_ctx.cdt_ctx_list_index(0)] -ctxs_base64 = client.get_cdtctx_base64(ctxs) -print("Base64 encoding of ctxs:", ctxs_base64) - -client.close() diff --git a/doc/examples/get_expression_base64.py b/doc/examples/get_expression_base64.py deleted file mode 100644 index e0c4663804..0000000000 --- a/doc/examples/get_expression_base64.py +++ /dev/null @@ -1,8 +0,0 @@ -from aerospike_helpers import expressions as exp - -# Compile expression -expr = exp.Eq(exp.IntBin("bin1"), 6).compile() - -base64 = client.get_expression_base64(expr) -print(base64) -# kwGTUQKkYmluMQY= diff --git a/doc/examples/keyordereddict.py b/doc/examples/keyordereddict.py deleted file mode 100644 index 7c8cb65d89..0000000000 --- a/doc/examples/keyordereddict.py +++ /dev/null @@ -1,46 +0,0 @@ -import aerospike -from aerospike_helpers.operations import map_operations as mop -from aerospike_helpers.operations import list_operations as lop -import aerospike_helpers.cdt_ctx as ctx -from aerospike import KeyOrderedDict - -config = { 'hosts': [ ("localhost", 3000), ] } -client = aerospike.client(config) -map_policy={'map_order': aerospike.MAP_KEY_VALUE_ORDERED} - -key = ("test", "demo", 100) -client.put(key, {'map_list': []}) - -map_ctx1 = ctx.cdt_ctx_list_index(0) -map_ctx2 = ctx.cdt_ctx_list_index(1) -map_ctx3 = ctx.cdt_ctx_list_index(2) - -my_dict1 = {'a': 1, 'b': 2, 'c': 3} -my_dict2 = {'d': 4, 'e': 5, 'f': 6} -my_dict3 = {'g': 7, 'h': 8, 'i': 9} - -ops = [ - lop.list_append_items('map_list', [my_dict1, my_dict2, my_dict3]), - mop.map_set_policy('map_list', map_policy, [map_ctx1]), - mop.map_set_policy('map_list', map_policy, [map_ctx2]), - mop.map_set_policy('map_list', map_policy, [map_ctx3]) - ] -client.operate(key, ops) - -_, _, res = client.get(key) -print(res) - -element = KeyOrderedDict({'f': 6, 'e': 5, 'd': 4}) # this will match my_dict2 because it will be converted to key ordered. - -ops = [ - lop.list_get_by_value('map_list', element, aerospike.LIST_RETURN_COUNT) - ] -_, _, res = client.operate(key, ops) -print(res) - -client.remove(key) -client.close() - -# EXPECTED OUTPUT: -# {'map_list': [{'a': 1, 'b': 2, 'c': 3}, {'d': 4, 'e': 5, 'f': 6}, {'g': 7, 'h': 8, 'i': 9}]} -# {'map_list': 1} diff --git a/doc/examples/log.py b/doc/examples/log.py deleted file mode 100644 index e52b5daceb..0000000000 --- a/doc/examples/log.py +++ /dev/null @@ -1,34 +0,0 @@ -# Enable the logging at application start, before connecting to the server. -import aerospike - -## SETTING THE LOG HANDLER ## - -# Clears saved log handler and disable logging -aerospike.set_log_handler(None) - -# Set default log handler to print to the console -aerospike.set_log_handler() - -def log_callback(level, func, path, line, msg): - print("[{}] {}".format(func, msg)) - -# Set log handler to custom callback function (defined above) -aerospike.set_log_handler(log_callback) - - -## SETTING THE LOG LEVEL ## - -# disables log handling -aerospike.set_log_level(aerospike.LOG_LEVEL_OFF) - -# Enables log handling and sets level to LOG_LEVEL_TRACE -aerospike.set_log_level(aerospike.LOG_LEVEL_TRACE) - -# Create a client and connect it to the cluster -# This line will print use log_callback to print logs with a log level of TRACE -config = { - "hosts": [ - ("127.0.0.1", 3000) - ] -} -client = aerospike.client(config) diff --git a/doc/examples/lua/lua.py b/doc/examples/lua/lua.py deleted file mode 100644 index caaa541b64..0000000000 --- a/doc/examples/lua/lua.py +++ /dev/null @@ -1,41 +0,0 @@ -import aerospike - -config = {'hosts': [('127.0.0.1', 3000)], - 'lua': {'system_path':'/usr/local/aerospike/lua/', - 'user_path':'./'}} -client = aerospike.client(config) -client.udf_put("example.lua") - -# Remove index if it already exists -from aerospike import exception as ex -try: - client.index_remove("test", "ageIndex") -except ex.IndexNotFound: - pass - -bins = [ - {"name": "Jeff", "age": 20}, - {"name": "Derek", "age": 24}, - {"name": "Derek", "age": 21}, - {"name": "Derek", "age": 29}, - {"name": "Jeff", "age": 29}, -] -keys = [("test", "users", f"user{i}") for i in range(len(bins))] -for key, recordBins in zip(keys, bins): - client.put(key, recordBins) - -client.index_integer_create("test", "users", "age", "ageIndex") - -query = client.query('test', 'users') -query.apply('example', 'group_count', ['name', 'age', 21]) -names = query.results() - -# we expect a dict (map) whose keys are names, each with a count value -print(names) -# One of the Jeffs is excluded because he is under 21 -# [{'Derek': 3, 'Jeff': 1}] - -# Cleanup -client.index_remove("test", "ageIndex") -client.batch_remove(keys) -client.close() diff --git a/doc/examples/operate.py b/doc/examples/operate.py deleted file mode 100644 index 8c43c805ab..0000000000 --- a/doc/examples/operate.py +++ /dev/null @@ -1,21 +0,0 @@ -from aerospike_helpers.operations import operations - -# Add name, update age, and return attributes -client.put(keyTuple, {'age': 25, 'career': 'delivery boy'}) -ops = [ - operations.increment("age", 1000), - operations.write("name", "J."), - operations.prepend("name", "Phillip "), - operations.append("name", " Fry"), - operations.read("name"), - operations.read("career"), - operations.read("age") -] -(key, meta, bins) = client.operate(key, ops) - -print(key) # ('test', 'demo', None, bytearray(b'...')) -# The generation should only increment once -# A transaction is *atomic* -print(meta) # {'ttl': 2592000, 'gen': 2} -print(bins) # Will display all bins selected by read operations -# {'name': 'Phillip J. Fry', 'career': 'delivery boy', 'age': 1025} diff --git a/doc/examples/operate_ordered.py b/doc/examples/operate_ordered.py deleted file mode 100644 index 1f4536d478..0000000000 --- a/doc/examples/operate_ordered.py +++ /dev/null @@ -1,19 +0,0 @@ -from aerospike_helpers.operations import operations - -# Add name, update age, and return attributes -client.put(keyTuple, {'age': 25, 'career': 'delivery boy'}) -ops = [ - operations.increment("age", 1000), - operations.write("name", "J."), - operations.prepend("name", "Phillip "), - operations.append("name", " Fry"), - operations.read("name"), - operations.read("career"), - operations.read("age") -] -(key, meta, bins) = client.operate_ordered(keyTuple, ops) - -# Same output for key and meta as operate() -# But read operations are outputted as bin-value pairs -print(bins) -# [('name': 'Phillip J. Fry'), ('career': 'delivery boy'), ('age': 1025)] diff --git a/doc/examples/put.py b/doc/examples/put.py deleted file mode 100644 index 017e102fd6..0000000000 --- a/doc/examples/put.py +++ /dev/null @@ -1,11 +0,0 @@ -# Insert a record with bin1 -client.put(keyTuple, {'bin1': 4}) - -# Insert another bin named bin2 -client.put(keyTuple, {'bin2': "value"}) - -# Remove bin1 from this record -client.put(keyTuple, {'bin2': aerospike.null()}) - -# Removing the last bin should delete this record -client.put(keyTuple, {'bin1': aerospike.null()}) diff --git a/doc/examples/query/boilerplate.py b/doc/examples/query/boilerplate.py deleted file mode 100644 index fb5503a050..0000000000 --- a/doc/examples/query/boilerplate.py +++ /dev/null @@ -1,38 +0,0 @@ -import aerospike -import sys -from aerospike import exception as ex - -config = {'hosts': [('127.0.0.1', 3000)]} - -# Create a client and connect it to the cluster -try: - client = aerospike.client(config) - client.truncate('test', "demo", 0) -except ex.ClientError as e: - print("Error: {0} [{1}]".format(e.msg, e.code)) - sys.exit(1) - -# Remove old indices -try: - client.index_remove("test", "scoreIndex") - client.index_remove("test", "eloIndex") -except ex.AerospikeError as e: - # Ignore if no indices found - pass - -# Insert 4 records -keyTuples = [("test", "demo", f"player{i}") for i in range(4)] -bins = [ - {"score": 100, "elo": 1400}, - {"score": 20, "elo": 1500}, - {"score": 10, "elo": 1100}, - {"score": 200, "elo": 900} -] -for keyTuple, bin in zip(keyTuples, bins): - client.put(keyTuple, bin) - -query = client.query('test', 'demo') - -# Queries require a secondary index for each bin name -client.index_integer_create("test", "demo", "score", "scoreIndex") -client.index_integer_create("test", "demo", "elo", "eloIndex") diff --git a/doc/examples/query/foreach.py b/doc/examples/query/foreach.py deleted file mode 100644 index a73bf4044c..0000000000 --- a/doc/examples/query/foreach.py +++ /dev/null @@ -1,19 +0,0 @@ -# Callback function -# Calculates new elo for a player -def updateElo(record): - keyTuple, _, bins = record - # Add score to elo - bins["elo"] = bins["elo"] + bins["score"] - client.put(keyTuple, bins) - -query.foreach(updateElo) - -# Player elos should be updated -brs = client.batch_read(keyTuples) -for br in brs.batch_records: - # Print record bin - print(br.record[2]) -# {'score': 100, 'elo': 1500} -# {'score': 20, 'elo': 1520} -# {'score': 10, 'elo': 1110} -# {'score': 200, 'elo': 1100} diff --git a/doc/examples/query/foreachfalse.py b/doc/examples/query/foreachfalse.py deleted file mode 100644 index 9b824b1566..0000000000 --- a/doc/examples/query/foreachfalse.py +++ /dev/null @@ -1,20 +0,0 @@ -# Adds record keys from a stream to a list -# But limits the number of keys to "lim" -def limit(lim: int, result: list): - # Integers are immutable - # so a list (mutable) is used for the counter - c = [0] - def key_add(record): - key, metadata, bins = record - if c[0] < lim: - result.append(key) - c[0] = c[0] + 1 - else: - return False - return key_add - -from aerospike import predicates as p - -keys = [] -query.foreach(limit(2, keys)) -print(len(keys)) # 2 diff --git a/doc/examples/query/results.py b/doc/examples/query/results.py deleted file mode 100644 index 24e9c149e9..0000000000 --- a/doc/examples/query/results.py +++ /dev/null @@ -1,9 +0,0 @@ -from aerospike import predicates - -query.select('score') -query.where(predicates.equals('score', 100)) - -records = query.results() -# Matches one record -print(records) -# [(('test', 'demo', None, bytearray(b'...')), {'ttl': 2592000, 'gen': 1}, {'score': 100})] diff --git a/doc/examples/remove.py b/doc/examples/remove.py deleted file mode 100644 index f4e94f7465..0000000000 --- a/doc/examples/remove.py +++ /dev/null @@ -1,12 +0,0 @@ -# Insert a record -client.put(keyTuple, {"bin1": 4}) - -# Try to remove it with the wrong generation -try: - client.remove(keyTuple, meta={'gen': 5}, policy={'gen': aerospike.POLICY_GEN_EQ}) -except ex.AerospikeError as e: - # Error: AEROSPIKE_ERR_RECORD_GENERATION [3] - print("Error: {0} [{1}]".format(e.msg, e.code)) - -# Remove it ignoring generation -client.remove(keyTuple) diff --git a/doc/examples/remove_bin.py b/doc/examples/remove_bin.py deleted file mode 100644 index 8619f3c1b4..0000000000 --- a/doc/examples/remove_bin.py +++ /dev/null @@ -1,11 +0,0 @@ -# Insert record -bins = {"bin1": 0, "bin2": 1} -client.put(keyTuple, bins) - -# Remove bin1 -client.remove_bin(keyTuple, ['bin1']) - -# Only bin2 shold remain -(keyTuple, meta, bins) = client.get(keyTuple) -print(bins) -# {'bin2': 1} diff --git a/doc/examples/scan/top.py b/doc/examples/scan/top.py deleted file mode 100644 index 1923a7df32..0000000000 --- a/doc/examples/scan/top.py +++ /dev/null @@ -1,46 +0,0 @@ -import aerospike -from aerospike import exception as ex -import sys -import time - -config = {"hosts": [("127.0.0.1", 3000)]} -client = aerospike.client(config) - -# register udf -try: - client.udf_put("./my_udf.lua") -except ex.AerospikeError as e: - print("Error: {0} [{1}]".format(e.msg, e.code)) - client.close() - sys.exit(1) - -# put records and apply udf -try: - keys = [("test", "demo", 1), ("test", "demo", 2), ("test", "demo", 3)] - records = [{"number": 1}, {"number": 2}, {"number": 3}] - for i in range(3): - client.put(keys[i], records[i]) - - scan = client.scan("test", "demo") - scan.apply("my_udf", "my_udf", ["number", 10]) - job_id = scan.execute_background() - - # wait for job to finish - while True: - response = client.job_info(job_id, aerospike.JOB_SCAN) - if response["status"] != aerospike.JOB_STATUS_INPROGRESS: - break - time.sleep(0.25) - - brs = client.batch_read(keys) - for br in brs.batch_records: - print(br.record[2]) -except ex.AerospikeError as e: - print("Error: {0} [{1}]".format(e.msg, e.code)) - sys.exit(1) -finally: - client.close() -# EXPECTED OUTPUT: -# {'number': 11} -# {'number': 12} -# {'number': 13} diff --git a/doc/examples/select.py b/doc/examples/select.py deleted file mode 100644 index 9ec98d0a39..0000000000 --- a/doc/examples/select.py +++ /dev/null @@ -1,18 +0,0 @@ -# Record to select from -client.put(keyTuple, {'bin1': 4, 'bin2': 3}) - -# Only get bin1 -(key, meta, bins) = client.select(keyTuple, ['bin1']) - -# Similar output to get() -print(key) # ('test', 'demo', 'key', bytearray(b'...')) -print(meta) # {'ttl': 2592000, 'gen': 1} -print(bins) # {'bin1': 4} - -# Get all bins -(key, meta, bins) = client.select(keyTuple, ['bin1', 'bin2']) -print(bins) # {'bin1': 4, 'bin2': 3} - -# Get nonexistent bin -(key, meta, bins) = client.select(keyTuple, ['bin3']) -print(bins) # {} diff --git a/doc/examples/serializer.py b/doc/examples/serializer.py deleted file mode 100644 index 5c08b711f7..0000000000 --- a/doc/examples/serializer.py +++ /dev/null @@ -1,76 +0,0 @@ -import aerospike -import marshal -import json - -# Serializers and deserializers -# Both local and global serializers use json library -# Functions print which one is being used - -def classSerializer(obj): - print("Using class serializer") - return json.dumps(obj) - -def classDeserializer(bytes): - print("Using class deserializer") - return json.loads(bytes) - -def localSerializer(obj): - print("Using local serializer") - return json.dumps(obj) - -def localDeserializer(bytes): - print("Using local deserializer") - return json.loads(bytes) - -# First client has class-level serializer set in aerospike module -aerospike.set_serializer(classSerializer) -aerospike.set_deserializer(classDeserializer) -config = { - 'hosts': [('127.0.0.1', 3000)] -} -client = aerospike.client(config) - -# Second client has instance-level serializer set in client config -config['serialization'] = (localSerializer, localDeserializer) -client2 = aerospike.client(config) - -# Keys: foo1, foo2, foo3 -keys = [('test', 'demo', f'foo{i}') for i in range(1, 4)] -# Tuple is an unsupported type -tupleBin = {'bin': (1, 2, 3)} - -# Use the default, built-in serialization (cPickle) -client.put(keys[0], tupleBin) -(_, _, bins) = client.get(keys[0]) -print(bins) - -# Expected output: -# {'bin': (1, 2, 3)} - -# First client uses class-level, user-defined serialization -# No instance-level serializer was declared -client.put(keys[1], tupleBin, serializer=aerospike.SERIALIZER_USER) -(_, _, bins) = client.get(keys[1]) -# Notice that json-encoding a tuple produces a list -print(bins) - -# Expected output: -# Using class serializer -# Using class deserializer -# {'bin': [1, 2, 3]} - -# Second client uses instance-level, user-defined serialization -# Instance-level serializer overrides class-level serializer -client2.put(keys[2], tupleBin, serializer=aerospike.SERIALIZER_USER) -(_, _, bins) = client2.get(keys[2]) -print(bins) - -# Expected output: -# Using local serializer -# Using local deserializer -# {'bin': [1, 2, 3]} - -# Cleanup -client.batch_remove(keys) -client.close() -client2.close() diff --git a/doc/examples/touch.py b/doc/examples/touch.py deleted file mode 100644 index 1b9021c1a7..0000000000 --- a/doc/examples/touch.py +++ /dev/null @@ -1,12 +0,0 @@ -# Insert record and get its metadata -client.put(keyTuple, bins = {"bin1": 4}) -(key, meta) = client.exists(keyTuple) -print(meta) # {'ttl': 2592000, 'gen': 1} - -# Explicitly set TTL to 120 -# and increment generation -client.touch(keyTuple, 120) - -# Record metadata should be updated -(key, meta) = client.exists(keyTuple) -print(meta) # {'ttl': 120, 'gen': 2} diff --git a/doc/examples/truncate.py b/doc/examples/truncate.py deleted file mode 100644 index c1b7d7f356..0000000000 --- a/doc/examples/truncate.py +++ /dev/null @@ -1,18 +0,0 @@ -import time - -client.put(("test", "demo", "key1"), {"bin": 4}) - -time.sleep(1) -# Take threshold time -current_time = time.time() -time.sleep(1) - -client.put(("test", "demo", "key2"), {"bin": 5}) - -threshold_ns = int(current_time * 10**9) -# Remove all items in set `demo` created before threshold time -# Record using key1 should be removed -client.truncate('test', 'demo', threshold_ns) - -# Remove all items in namespace -# client.truncate('test', None, 0) diff --git a/doc/exception.rst b/doc/exception.rst index 3ad3f3b2f1..20379255c2 100644 --- a/doc/exception.rst +++ b/doc/exception.rst @@ -13,18 +13,23 @@ Example This is a simple example on how to catch an exception thrown by the Aerospike client: -.. code-block:: python +.. testcode:: import aerospike from aerospike import exception as ex try: - config = { 'hosts': [ ('127.0.0.1', 3000)], 'policies': { 'total_timeout': 1200}} + # Invalid port number + config = { 'hosts': [ ('127.0.0.1', 5000)], 'policies': { 'total_timeout': 1200}} client = aerospike.client(config) client.close() except ex.AerospikeError as e: print("Error: {0} [{1}]".format(e.msg, e.code)) +.. testoutput:: + + Error: Failed to connect [-10] + .. versionadded:: 1.0.44 Base Class @@ -749,13 +754,22 @@ In Doubt Status --------------- The ``in-doubt`` status of a caught exception can be checked by looking at the 5th element of its `args` tuple: - .. code-block:: python + .. testcode:: - key = 'test', 'demo', 1 - record = {'some': 'thing'} - try: + import aerospike + from aerospike import exception as ex + + # Configure the client + config = { + 'hosts': [ ('127.0.0.1', 3000)] + } + client = aerospike.client(config) + + key = 'test', 'demo', 1 + record = {'some': 'thing'} + try: client.put(key, record) - except AerospikeError as exc: - print("The in doubt nature of the operation is: {}".format(exc.args[4]) + except ex.AerospikeError as exc: + print("The in doubt nature of the operation is: {}".format(exc.args[4])) .. versionadded:: 3.0.1 diff --git a/doc/geojson.rst b/doc/geojson.rst index d8e616486b..385f793873 100755 --- a/doc/geojson.rst +++ b/doc/geojson.rst @@ -37,7 +37,7 @@ deserialized into a :class:`~aerospike.GeoJSON` instance. Example ------- -.. code-block:: python +.. testcode:: import aerospike from aerospike import GeoJSON @@ -54,9 +54,6 @@ Example 'coordinates': [longitude, latitude]}) print(loc) - # Expected output: - # {"type": "Point", "coordinates": [-80.604333, 28.608389]} - # Alternatively, create the GeoJSON point from a string loc = aerospike.geojson('{"type": "Point", "coordinates": [-80.604333, 28.608389]}') @@ -69,13 +66,15 @@ Example (k, m, b) = client.get(('test', 'pads', 'launchpad1')) print(b) - # Expected output: - # {'pad_id': 1, 'loc': '{"type": "Point", "coordinates": [-80.604333, 28.608389]}'} - # Cleanup client.remove(('test', 'pads', 'launchpad1')) client.close() +.. testoutput:: + + {"type": "Point", "coordinates": [-80.604333, 28.608389]} + {'pad_id': 1, 'loc': '{"type": "Point", "coordinates": [-80.604333, 28.608389]}'} + Methods ======= diff --git a/doc/key_ordered_dict.rst b/doc/key_ordered_dict.rst index 0c30a51671..5edcaae850 100644 --- a/doc/key_ordered_dict.rst +++ b/doc/key_ordered_dict.rst @@ -11,8 +11,54 @@ The KeyOrderedDict class is a dictionary that directly maps to a key ordered map on the Aerospike server. This assists in matching key ordered maps through various read operations. See the example snippet below. -.. include:: examples/keyordereddict.py - :code: python +.. testcode:: + + import aerospike + from aerospike_helpers.operations import map_operations as mop + from aerospike_helpers.operations import list_operations as lop + import aerospike_helpers.cdt_ctx as ctx + from aerospike import KeyOrderedDict + + config = { 'hosts': [ ("localhost", 3000), ] } + client = aerospike.client(config) + map_policy={'map_order': aerospike.MAP_KEY_VALUE_ORDERED} + + key = ("test", "demo", 100) + client.put(key, {'map_list': []}) + + map_ctx1 = ctx.cdt_ctx_list_index(0) + map_ctx2 = ctx.cdt_ctx_list_index(1) + map_ctx3 = ctx.cdt_ctx_list_index(2) + + my_dict1 = {'a': 1, 'b': 2, 'c': 3} + my_dict2 = {'d': 4, 'e': 5, 'f': 6} + my_dict3 = {'g': 7, 'h': 8, 'i': 9} + + ops = [ + lop.list_append_items('map_list', [my_dict1, my_dict2, my_dict3]), + mop.map_set_policy('map_list', map_policy, [map_ctx1]), + mop.map_set_policy('map_list', map_policy, [map_ctx2]), + mop.map_set_policy('map_list', map_policy, [map_ctx3]) + ] + client.operate(key, ops) + + _, _, res = client.get(key) + print(res) + + element = KeyOrderedDict({'f': 6, 'e': 5, 'd': 4}) # this will match my_dict2 because it will be converted to key ordered. + + ops = [ + lop.list_get_by_value('map_list', element, aerospike.LIST_RETURN_COUNT) + ] + _, _, res = client.operate(key, ops) + print(res) + + client.remove(key) + client.close() + + # EXPECTED OUTPUT: + # {'map_list': [{'a': 1, 'b': 2, 'c': 3}, {'d': 4, 'e': 5, 'f': 6}, {'g': 7, 'h': 8, 'i': 9}]} + # {'map_list': 1} KeyOrderedDict inherits from :class:`dict` and has no extra functionality. The only difference is its mapping to a key ordered map. diff --git a/doc/predicates.rst b/doc/predicates.rst index 00e6979e36..5815420b93 100644 --- a/doc/predicates.rst +++ b/doc/predicates.rst @@ -22,7 +22,7 @@ Bin Predicates :param int max: the maximum value to be matched with the between operator. :return: `tuple` to be used in :meth:`aerospike.Query.where`. - .. code-block:: python + .. code-block:: Python import aerospike from aerospike import predicates as p @@ -45,7 +45,7 @@ Bin Predicates :type val: :py:class:`str` or :py:class:`int` :return: `tuple` to be used in :meth:`aerospike.Query.where`. - .. code-block:: python + .. code-block:: Python import aerospike from aerospike import predicates as p @@ -75,7 +75,7 @@ GeoJSON Predicates .. note:: Requires server version >= 3.7.0 - .. code-block:: python + .. testcode:: import aerospike from aerospike import GeoJSON @@ -126,7 +126,7 @@ GeoJSON Predicates .. note:: Requires server version >= 3.8.1 - .. code-block:: python + .. testcode:: import aerospike from aerospike import GeoJSON @@ -163,7 +163,7 @@ GeoJSON Predicates .. note:: Requires server version >= 3.7.0 - .. code-block:: python + .. testcode:: import aerospike from aerospike import GeoJSON @@ -210,7 +210,7 @@ GeoJSON Predicates .. note:: Requires server version >= 3.7.0 - .. code-block:: python + .. testcode:: import aerospike from aerospike import GeoJSON @@ -255,7 +255,7 @@ Map and List Predicates .. note:: Requires server version >= 3.8.1 - .. code-block:: python + .. testcode:: import aerospike from aerospike import predicates as p @@ -292,7 +292,7 @@ Map and List Predicates .. note:: Requires server version >= 3.8.1 - .. code-block:: python + .. testcode:: import aerospike from aerospike import predicates as p diff --git a/doc/query.rst b/doc/query.rst index 6070bd600e..cd8aa39566 100755 --- a/doc/query.rst +++ b/doc/query.rst @@ -111,8 +111,87 @@ Methods Assume this boilerplate code is run before all examples below: -.. include:: examples/query/boilerplate.py - :code: python +.. testsetup:: + + import aerospike + import sys + from aerospike import exception as ex + + config = {'hosts': [('127.0.0.1', 3000)]} + + # Create a client and connect it to the cluster + try: + client = aerospike.client(config) + client.truncate('test', "demo", 0) + except ex.ClientError as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + sys.exit(1) + + # Remove old indices + try: + client.index_remove("test", "scoreIndex") + client.index_remove("test", "eloIndex") + except ex.AerospikeError as e: + # Ignore if no indices found + pass + + # Insert 4 records + keyTuples = [("test", "demo", f"player{i}") for i in range(4)] + bins = [ + {"score": 100, "elo": 1400}, + {"score": 20, "elo": 1500}, + {"score": 10, "elo": 1100}, + {"score": 200, "elo": 900} + ] + for keyTuple, bin in zip(keyTuples, bins): + client.put(keyTuple, bin) + + query = client.query('test', 'demo') + + # Queries require a secondary index for each bin name + client.index_integer_create("test", "demo", "score", "scoreIndex") + client.index_integer_create("test", "demo", "elo", "eloIndex") + +.. code-block:: Python + + import aerospike + import sys + from aerospike import exception as ex + + config = {'hosts': [('127.0.0.1', 3000)]} + + # Create a client and connect it to the cluster + try: + client = aerospike.client(config) + client.truncate('test', "demo", 0) + except ex.ClientError as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + sys.exit(1) + + # Remove old indices + try: + client.index_remove("test", "scoreIndex") + client.index_remove("test", "eloIndex") + except ex.AerospikeError as e: + # Ignore if no indices found + pass + + # Insert 4 records + keyTuples = [("test", "demo", f"player{i}") for i in range(4)] + bins = [ + {"score": 100, "elo": 1400}, + {"score": 20, "elo": 1500}, + {"score": 10, "elo": 1100}, + {"score": 200, "elo": 900} + ] + for keyTuple, bin in zip(keyTuples, bins): + client.put(keyTuple, bin) + + query = client.query('test', 'demo') + + # Queries require a secondary index for each bin name + client.index_integer_create("test", "demo", "score", "scoreIndex") + client.index_integer_create("test", "demo", "elo", "eloIndex") .. class:: Query :noindex: @@ -177,14 +256,23 @@ Assume this boilerplate code is run before all examples below: :param dict options: optional :ref:`aerospike_query_options`. :return: a :class:`list` of :ref:`aerospike_record_tuple`. - .. include:: examples/query/results.py - :code: python + .. testcode:: + + from aerospike import predicates + + query.select('score') + query.where(predicates.equals('score', 100)) + + records = query.results() + # Matches one record + print(records) + # [(('test', 'demo', None, bytearray(b'...')), {'ttl': 2592000, 'gen': 1}, {'score': 100})] .. note:: As of client 7.0.0 and with server >= 6.0 results and the query policy "partition_filter" see :ref:`aerospike_partition_objects` can be used to specify which partitions/records results will query. See the example below. - .. code-block:: python + .. testcode:: # This is an example of querying partitions 1000 - 1003. import aerospike @@ -217,19 +305,58 @@ Assume this boilerplate code is run before all examples below: :param dict policy: optional :ref:`aerospike_query_policies`. :param dict options: optional :ref:`aerospike_query_options`. - .. include:: examples/query/foreach.py - :code: python + .. testcode:: + + # Callback function + # Calculates new elo for a player + def updateElo(record): + keyTuple, _, bins = record + # Add score to elo + bins["elo"] = bins["elo"] + bins["score"] + client.put(keyTuple, bins) + + query.foreach(updateElo) + + # Player elos should be updated + brs = client.batch_read(keyTuples) + for br in brs.batch_records: + # Print record bin + print(br.record[2]) + # {'score': 100, 'elo': 1500} + # {'score': 20, 'elo': 1520} + # {'score': 10, 'elo': 1110} + # {'score': 200, 'elo': 1100} .. note:: To stop the stream return ``False`` from the callback function. - .. include:: examples/query/foreachfalse.py - :code: python + .. testcode:: + + # Adds record keys from a stream to a list + # But limits the number of keys to "lim" + def limit(lim: int, result: list): + # Integers are immutable + # so a list (mutable) is used for the counter + c = [0] + def key_add(record): + key, metadata, bins = record + if c[0] < lim: + result.append(key) + c[0] = c[0] + 1 + else: + return False + return key_add + + from aerospike import predicates as p + + keys = [] + query.foreach(limit(2, keys)) + print(len(keys)) # 2 .. note:: As of client 7.0.0 and with server >= 6.0 foreach and the query policy "partition_filter" see :ref:`aerospike_partition_objects` can be used to specify which partitions/records foreach will query. See the example below. - .. code-block:: python + .. testcode:: # This is an example of querying partitions 1000 - 1003. import aerospike @@ -284,8 +411,49 @@ Assume this boilerplate code is run before all examples below: Assume the example code above is in a file called "example.lua", and is the same folder as the following script. - .. include:: examples/lua/lua.py - :code: python + .. testcode:: + + import aerospike + + config = {'hosts': [('127.0.0.1', 3000)], + 'lua': {'system_path':'/usr/local/aerospike/lua/', + 'user_path':'./'}} + client = aerospike.client(config) + client.udf_put("example.lua") + + # Remove index if it already exists + from aerospike import exception as ex + try: + client.index_remove("test", "ageIndex") + except ex.IndexNotFound: + pass + + bins = [ + {"name": "Jeff", "age": 20}, + {"name": "Derek", "age": 24}, + {"name": "Derek", "age": 21}, + {"name": "Derek", "age": 29}, + {"name": "Jeff", "age": 29}, + ] + keys = [("test", "users", f"user{i}") for i in range(len(bins))] + for key, recordBins in zip(keys, bins): + client.put(key, recordBins) + + client.index_integer_create("test", "users", "age", "ageIndex") + + query = client.query('test', 'users') + query.apply('example', 'group_count', ['name', 'age', 21]) + names = query.results() + + # we expect a dict (map) whose keys are names, each with a count value + print(names) + # One of the Jeffs is excluded because he is under 21 + # [{'Derek': 3, 'Jeff': 1}] + + # Cleanup + client.index_remove("test", "ageIndex") + client.batch_remove(keys) + client.close() With stream UDFs, the final reduce steps (which ties the results from the reducers of the cluster nodes) executes on the @@ -328,7 +496,7 @@ Assume this boilerplate code is run before all examples below: :return: a job ID that can be used with :meth:`~aerospike.Client.job_info` to track the status of the ``aerospike.JOB_QUERY`` , as it runs in the background. - .. code-block:: python + .. testcode:: # EXAMPLE 1: Increase everyone's score by 100 @@ -346,10 +514,6 @@ Assume this boilerplate code is run before all examples below: for key in keyTuples: _, _, bins = client.get(key) print(bins) - # {"score": 200, "elo": 1400} - # {"score": 120, "elo": 1500} - # {"score": 110, "elo": 1100} - # {"score": 300, "elo": 900} # EXAMPLE 2: Increase score by 100 again for those with elos > 1000 # Use write policy to select players by elo @@ -365,16 +529,23 @@ Assume this boilerplate code is run before all examples below: for i, key in enumerate(keyTuples): _, _, bins = client.get(key) print(bins) - # {"score": 300, "elo": 1400} <-- - # {"score": 220, "elo": 1500} <-- - # {"score": 210, "elo": 1100} <-- - # {"score": 300, "elo": 900} # Cleanup and close the connection to the Aerospike cluster. for key in keyTuples: client.remove(key) client.close() + .. testoutput:: + + {"score": 200, "elo": 1400} + {"score": 120, "elo": 1500} + {"score": 110, "elo": 1100} + {"score": 300, "elo": 900} + {"score": 300, "elo": 1400} + {"score": 220, "elo": 1500} + {"score": 210, "elo": 1100} + {"score": 300, "elo": 900} + .. method:: paginate() Makes a query instance a paginated query. @@ -385,7 +556,7 @@ Assume this boilerplate code is run before all examples below: This can be retrieved later using .get_partitions_status(). This can also been done by using the partition_filter policy. - .. code-block:: python + .. testcode:: # After inserting 4 records... # Query 3 pages of 2 records each. @@ -409,14 +580,17 @@ Assume this boilerplate code is run before all examples below: if query.is_done(): print("all done") break - # got page: 0 - # (('test', 'demo', None, bytearray(b'HD\xd1\xfa$L\xa0\xf5\xa2~\xd6\x1dv\x91\x9f\xd6\xfa\xad\x18\x00')), {'ttl': 2591996, 'gen': 1}, {'score': 20, 'elo': 1500}) - # (('test', 'demo', None, bytearray(b'f\xa4\t"\xa9uc\xf5\xce\x97\xf0\x16\x9eI\xab\x89Q\xb8\xef\x0b')), {'ttl': 2591996, 'gen': 1}, {'score': 10, 'elo': 1100}) - # got page: 1 - # (('test', 'demo', None, bytearray(b'\xb6\x9f\xf5\x7f\xfarb.IeaVc\x17n\xf4\x9b\xad\xa7T')), {'ttl': 2591996, 'gen': 1}, {'score': 200, 'elo': 900}) - # (('test', 'demo', None, bytearray(b'j>@\xfe\xe0\x94\xd5?\n\xd7\xc3\xf2\xd7\x045\xbc*\x07 \x1a')), {'ttl': 2591996, 'gen': 1}, {'score': 100, 'elo': 1400}) - # got page: 2 - # all done + + .. testoutput:: + + got page: 0 + (('test', 'demo', None, bytearray(b'HD\xd1\xfa$L\xa0\xf5\xa2~\xd6\x1dv\x91\x9f\xd6\xfa\xad\x18\x00')), {'ttl': 2591996, 'gen': 1}, {'score': 20, 'elo': 1500}) + (('test', 'demo', None, bytearray(b'f\xa4\t"\xa9uc\xf5\xce\x97\xf0\x16\x9eI\xab\x89Q\xb8\xef\x0b')), {'ttl': 2591996, 'gen': 1}, {'score': 10, 'elo': 1100}) + got page: 1 + (('test', 'demo', None, bytearray(b'\xb6\x9f\xf5\x7f\xfarb.IeaVc\x17n\xf4\x9b\xad\xa7T')), {'ttl': 2591996, 'gen': 1}, {'score': 200, 'elo': 900}) + (('test', 'demo', None, bytearray(b'j>@\xfe\xe0\x94\xd5?\n\xd7\xc3\xf2\xd7\x045\xbc*\x07 \x1a')), {'ttl': 2591996, 'gen': 1}, {'score': 100, 'elo': 1400}) + got page: 2 + all done .. method:: is_done() @@ -435,7 +609,16 @@ Assume this boilerplate code is run before all examples below: :return: See :ref:`aerospike_partition_objects` for a description of the partition status return value. - .. code-block:: python + .. testcode:: + + import aerospike + # Configure the client + config = { + 'hosts': [('127.0.0.1', 3000)] + } + + # Create a client and connect it to the cluster + client = aerospike.client(config) # Only read 2 records @@ -452,8 +635,6 @@ Assume this boilerplate code is run before all examples below: query = client.query("test", "demo") query.paginate() query.foreach(callback) - # (('test', 'demo', None, bytearray(b'...')), {'ttl': 2591996, 'gen': 1}, {'score': 10, 'elo': 1100}) - # (('test', 'demo', None, bytearray(b'...')), {'ttl': 2591996, 'gen': 1}, {'score': 20, 'elo': 1500}) # Use this to resume query where we left off @@ -471,8 +652,13 @@ Assume this boilerplate code is run before all examples below: } query.foreach(resume_callback, policy) - # 1096 -> (('test', 'demo', None, bytearray(b'...')), {'ttl': 2591996, 'gen': 1}, {'score': 100, 'elo': 1400}) - # 3690 -> (('test', 'demo', None, bytearray(b'...')), {'ttl': 2591996, 'gen': 1}, {'score': 200, 'elo': 900}) + + .. testoutput:: + + (('test', 'demo', None, bytearray(b'...')), {'ttl': 2591996, 'gen': 1}, {'score': 10, 'elo': 1100}) + (('test', 'demo', None, bytearray(b'...')), {'ttl': 2591996, 'gen': 1}, {'score': 20, 'elo': 1500}) + 1096 -> (('test', 'demo', None, bytearray(b'...')), {'ttl': 2591996, 'gen': 1}, {'score': 100, 'elo': 1400}) + 3690 -> (('test', 'demo', None, bytearray(b'...')), {'ttl': 2591996, 'gen': 1}, {'score': 200, 'elo': 900}) .. _aerospike_query_policies: diff --git a/doc/scan.rst b/doc/scan.rst index 258172fc31..fd46d96a83 100755 --- a/doc/scan.rst +++ b/doc/scan.rst @@ -303,8 +303,54 @@ Methods .. note:: Python client version 3.10.0 implemented scan execute_background. - .. include:: examples/scan/top.py - :code: python + .. testcode:: + + import aerospike + from aerospike import exception as ex + import sys + import time + + config = {"hosts": [("127.0.0.1", 3000)]} + client = aerospike.client(config) + + # register udf + try: + client.udf_put("./my_udf.lua") + except ex.AerospikeError as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + client.close() + sys.exit(1) + + # put records and apply udf + try: + keys = [("test", "demo", 1), ("test", "demo", 2), ("test", "demo", 3)] + records = [{"number": 1}, {"number": 2}, {"number": 3}] + for i in range(3): + client.put(keys[i], records[i]) + + scan = client.scan("test", "demo") + scan.apply("my_udf", "my_udf", ["number", 10]) + job_id = scan.execute_background() + + # wait for job to finish + while True: + response = client.job_info(job_id, aerospike.JOB_SCAN) + if response["status"] != aerospike.JOB_STATUS_INPROGRESS: + break + time.sleep(0.25) + + brs = client.batch_read(keys) + for br in brs.batch_records: + print(br.record[2]) + except ex.AerospikeError as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + sys.exit(1) + finally: + client.close() + # EXPECTED OUTPUT: + # {'number': 11} + # {'number': 12} + # {'number': 13} .. include:: examples/scan/my_udf.lua :code: lua