From 60759791939dc6db701107123bc3b3e867c408ff Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 24 Jul 2024 19:03:03 +0200 Subject: [PATCH 01/13] [CLIENT-3055] Remove Unsupported Server Features (Predexp and BatchDirect) --- Gemfile | 1 + README.md | 22 +- examples/pred_exp.rb | 206 ------- lib/aerospike.rb | 12 - lib/aerospike/client.rb | 77 +-- lib/aerospike/command/batch_direct_command.rb | 105 ---- .../command/batch_direct_exists_command.rb | 51 -- lib/aerospike/command/batch_direct_node.rb | 40 -- lib/aerospike/command/batch_index_command.rb | 16 +- lib/aerospike/command/command.rb | 92 +-- lib/aerospike/info.rb | 6 +- lib/aerospike/policy/batch_policy.rb | 7 +- lib/aerospike/policy/policy.rb | 41 +- lib/aerospike/query/pred_exp.rb | 192 ------- lib/aerospike/query/pred_exp/and_or.rb | 32 -- .../query/pred_exp/geo_json_value.rb | 41 -- lib/aerospike/query/pred_exp/integer_value.rb | 32 -- lib/aerospike/query/pred_exp/op.rb | 27 - lib/aerospike/query/pred_exp/regex.rb | 32 -- lib/aerospike/query/pred_exp/regex_flags.rb | 23 - lib/aerospike/query/pred_exp/string_value.rb | 29 - lib/aerospike/query/statement.rb | 26 +- spec/aerospike/batch_spec.rb | 70 +-- spec/aerospike/cdt/cdt_list_spec.rb | 96 ++-- spec/aerospike/exp/exp_bit_spec.rb | 2 +- spec/aerospike/exp/exp_hll_spec.rb | 2 +- spec/aerospike/exp/exp_list_spec.rb | 2 +- spec/aerospike/exp/exp_map_spec.rb | 2 +- spec/aerospike/exp/expression_spec.rb | 154 ++--- spec/aerospike/pred_exp_spec.rb | 536 ------------------ spec/aerospike/predexp_ops_spec.rb | 329 ----------- spec/aerospike/udf_spec.rb | 10 +- spec/support/utils.rb | 55 +- 33 files changed, 251 insertions(+), 2117 deletions(-) delete mode 100644 examples/pred_exp.rb delete mode 100644 lib/aerospike/command/batch_direct_command.rb delete mode 100644 lib/aerospike/command/batch_direct_exists_command.rb delete mode 100644 lib/aerospike/command/batch_direct_node.rb delete mode 100644 lib/aerospike/query/pred_exp.rb delete mode 100644 lib/aerospike/query/pred_exp/and_or.rb delete mode 100644 lib/aerospike/query/pred_exp/geo_json_value.rb delete mode 100644 lib/aerospike/query/pred_exp/integer_value.rb delete mode 100644 lib/aerospike/query/pred_exp/op.rb delete mode 100644 lib/aerospike/query/pred_exp/regex.rb delete mode 100644 lib/aerospike/query/pred_exp/regex_flags.rb delete mode 100644 lib/aerospike/query/pred_exp/string_value.rb delete mode 100644 spec/aerospike/pred_exp_spec.rb delete mode 100644 spec/aerospike/predexp_ops_spec.rb diff --git a/Gemfile b/Gemfile index 084d1765..ad605391 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,7 @@ group :development do gem "rubocop-rspec", require: false end +gem "base64" gem "bcrypt" gem "msgpack", "~> 1.2" gem "rake" diff --git a/README.md b/README.md index 55ea697b..8f75d441 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,18 @@ An Aerospike library for Ruby. This library is compatible with Ruby 2.3+ and supports Linux, Mac OS X and various other BSDs. -- [Usage](#Usage) -- [Prerequisites](#Prerequisites) -- [Installation](#Installation) -- [Benchmarks](#Benchmarks) -- [API Documentaion](#API-Documentation) -- [Tests](#Tests) -- [Examples](#Examples) - - [Tools](#Tools) +- [Aerospike Ruby Client ](#aerospike-ruby-client---) + - [Usage:](#usage) + - [Prerequisites](#prerequisites) + - [Installation](#installation) + - [Installation from Ruby gems](#installation-from-ruby-gems) + - [Installation from source](#installation-from-source) + - [Tests](#tests) + - [Examples](#examples) + - [Tools](#tools) + - [Benchmarks](#benchmarks) + - [API Documentation](#api-documentation) + - [License](#license) ## Usage: @@ -97,7 +101,7 @@ This library is packaged with a number of tests. To run all the test cases: - $ AEROSPIKE_HOSTS="[,]" AEROSPIKE_USER="" AEROSPIKE_PASSWORD="" bundle exec rspec + $ AEROSPIKE_HOSTS="[,]" AEROSPIKE_USER="" AEROSPIKE_PASSWORD="" bundle exec rspec ## Examples diff --git a/examples/pred_exp.rb b/examples/pred_exp.rb deleted file mode 100644 index 647d9ad5..00000000 --- a/examples/pred_exp.rb +++ /dev/null @@ -1,206 +0,0 @@ -# frozen_string_literal: true - -require "rubygems" -require "aerospike" -require './shared/shared' - -include Aerospike -include Shared - -def main - Shared.init - setup(Shared.client) - teardown(Shared.client) - - run_integer_predexp_example(Shared.client) - run_string_predexp_example(Shared.client) - run_regex_predexp_example(Shared.client) - run_mapval_predexp_example(Shared.client) - run_list_predexp_example(Shared.client) - run_geojson_predexp_example(Shared.client) - run_void_time_predexp_example(Shared.client) - - Shared.logger.info("Example finished successfully.") -end - -def setup(client) - records = 100 - Shared.logger.info("Creating #{records} records with random properties.") - records.times do |idx| - key = Key.new(Shared.namespace, Shared.set_name, "user#{idx}") - record = { - 'name' => %w[Timmy Alice John Arthur Mike Diana Emily Laura Nicole].sample + "_#{idx}", - 'race' => %w[Squid Octopus].sample, - 'level' => rand(1..100), - 'rank' => ['C-', 'C', 'C+', 'B-', 'B', 'B+', 'A-', 'A', 'A+', 'S', 'S+', 'X'].sample, - 'gear' => { - 'clothes' => ['Green Hoodie', 'White Tee', 'Blue Jersey', 'Black Tee', 'Mountain Coat'].sample - }, - 'weapons' => ['Water Gun', 'Paint Roller', 'Paintbrush', 'Aerospray', 'Bucket'].sample(3), - 'loc' => GeoJSON.new(type: 'Point', coordinates: [(3 + (idx * 0.003)), (4 + (idx * 0.003))]) - } - client.put(key, record, ttl: (idx + 1) * 5) - end - - task = client.create_index(Shared.namespace, Shared.set_name, "name_index", "name", :string) - task.wait_till_completed or fail "Could not create secondary 'name' index" - - task = client.create_index(Shared.namespace, Shared.set_name, "level_index", "level", :numeric) - task.wait_till_completed or fail "Could not create secondary 'level' index" - - task = client.create_index(Shared.namespace, Shared.set_name, "loc_index", "loc", :geo2dsphere) - task.wait_till_completed or fail "Could not create secondary 'loc' index" -end - -def teardown(client) - client.drop_index(Shared.namespace, Shared.set_name, "name_index") - client.drop_index(Shared.namespace, Shared.set_name, "level_index") - client.drop_index(Shared.namespace, Shared.set_name, "loc_index") -end - -def run_integer_predexp_example(client) - Shared.logger.info("Querying set using predicate expressions to return users with level > 30") - - statement = Statement.new(Shared.namespace, Shared.set_name) - statement.predexp = [ - PredExp.integer_bin('level'), - PredExp.integer_value(30), - PredExp.integer_greater - ] - - records = client.query(statement) - results = [] - records.each do |r| - results << r.bins['name'] - end - - Shared.logger.info("Found #{results.length} records with level > 30.") -end - -def run_string_predexp_example(client) - Shared.logger.info("Querying set using predicate expressions to return Squids") - - statement = Statement.new(Shared.namespace, Shared.set_name) - statement.predexp = [ - PredExp.string_bin('race'), - PredExp.string_value('Squid'), - PredExp.string_equal - ] - - records = client.query(statement) - results = [] - records.each do |r| - results << r.bins['name'] - end - - Shared.logger.info("Found #{results.length} Squids.") -end - -def run_regex_predexp_example(client) - Shared.logger.info("Querying set using predicate expressions to return B rank users") - - statement = Statement.new(Shared.namespace, Shared.set_name) - statement.predexp = [ - PredExp.string_bin('rank'), - PredExp.string_value('B'), - PredExp.string_regex(PredExp::RegexFlags::NONE) - ] - - records = client.query(statement) - results = [] - records.each do |r| - results << r.bins['name'] - end - - Shared.logger.info("Found #{results.length} users with B rank.") -end - -def run_mapval_predexp_example(client) - Shared.logger.info("Querying set using predicate expressions to return all users wearing White Tees") - - statement = Statement.new(Shared.namespace, Shared.set_name) - statement.predexp = [ - PredExp.string_value('White Tee'), - PredExp.string_var('x'), - PredExp.string_equal, - PredExp.map_bin('gear'), - PredExp.mapval_iterate_or('x') - ] - - records = client.query(statement) - results = [] - records.each do |r| - results << r.bins['name'] - end - - Shared.logger.info("Found #{results.length} users wearing White Tees.") -end - -def run_list_predexp_example(client) - Shared.logger.info("Querying set using predicate expressions to return users using buckets") - - statement = Statement.new(Shared.namespace, Shared.set_name) - statement.predexp = [ - PredExp.string_value('Bucket'), - PredExp.string_var('x'), - PredExp.string_equal, - PredExp.list_bin('weapons'), - PredExp.list_iterate_or('x') - ] - - records = client.query(statement) - results = [] - records.each do |r| - results << r.bins['name'] - end - - Shared.logger.info("Found #{results.length} users using buckets.") -end - -def run_geojson_predexp_example(client) - Shared.logger.info("Querying set using predicate expressions to return users in range of circle") - - circle_range = 1_000 - # circle with range of 1000 meters - circle = GeoJSON.new(type: 'AeroCircle', coordinates: [[3,4], circle_range]) - - statement = Statement.new(Shared.namespace, Shared.set_name) - statement.predexp = [ - PredExp.geojson_bin('loc'), - PredExp.geojson_value(circle), - PredExp.geojson_contains - ] - - records = client.query(statement) - results = [] - records.each do |r| - results << r.bins['name'] - end - - Shared.logger.info("Found #{results.length} users in a circle.") -end - -def run_void_time_predexp_example(client) - Shared.logger.info("Querying set using predicate expressions to return records expiring in less than a minute") - - minute_from_now = Time.now + 60 - # Provided time must be an Epoch in nanoseconds - minute_from_now = ("%10.9f" % minute_from_now.to_f).gsub('.', '').to_i - - statement = Statement.new(Shared.namespace, Shared.set_name) - statement.predexp = [ - Aerospike::PredExp.integer_value(minute_from_now), - Aerospike::PredExp.void_time, - Aerospike::PredExp.integer_greater - ] - - records = client.query(statement) - results = [] - records.each do |r| - results << r.bins['name'] - end - - Shared.logger.info("Found #{results.length} records expiring in less than a minute.") -end - -main diff --git a/lib/aerospike.rb b/lib/aerospike.rb index 17c1939f..b1323ce4 100644 --- a/lib/aerospike.rb +++ b/lib/aerospike.rb @@ -43,7 +43,6 @@ require "aerospike/value/particle_type" require "aerospike/value/value" require "aerospike/command/single_command" -require "aerospike/command/batch_direct_node" require "aerospike/command/batch_index_node" require "aerospike/command/field_type" require "aerospike/command/command" @@ -53,8 +52,6 @@ require "aerospike/command/operate_command" require "aerospike/command/exists_command" require "aerospike/command/multi_command" -require "aerospike/command/batch_direct_command" -require "aerospike/command/batch_direct_exists_command" require "aerospike/command/batch_index_command" require "aerospike/command/batch_index_exists_command" require "aerospike/command/read_header_command" @@ -160,7 +157,6 @@ require "aerospike/query/query_command" require "aerospike/query/scan_command" require "aerospike/query/statement" -require "aerospike/query/pred_exp" require "aerospike/query/partition_tracker" require "aerospike/query/partition_status" require "aerospike/query/partition_filter" @@ -178,14 +174,6 @@ require "aerospike/exp/exp_hll" require "aerospike/exp/operation" -require "aerospike/query/pred_exp/and_or" -require "aerospike/query/pred_exp/geo_json_value" -require "aerospike/query/pred_exp/integer_value" -require "aerospike/query/pred_exp/op" -require "aerospike/query/pred_exp/regex" -require "aerospike/query/pred_exp/regex_flags" -require "aerospike/query/pred_exp/string_value" - module Aerospike extend Loggable end diff --git a/lib/aerospike/client.rb b/lib/aerospike/client.rb index 1b56c9c3..d3a8fa1c 100644 --- a/lib/aerospike/client.rb +++ b/lib/aerospike/client.rb @@ -36,15 +36,7 @@ module Aerospike # +:fail_if_not_connected+ set to true class Client - attr_accessor :default_admin_policy - attr_accessor :default_batch_policy - attr_accessor :default_info_policy - attr_accessor :default_query_policy - attr_accessor :default_read_policy - attr_accessor :default_scan_policy - attr_accessor :default_write_policy - attr_accessor :default_operate_policy - attr_accessor :cluster + attr_accessor :default_admin_policy, :default_batch_policy, :default_info_policy, :default_query_policy, :default_read_policy, :default_scan_policy, :default_write_policy, :default_operate_policy, :cluster def initialize(hosts = nil, policy: ClientPolicy.new, connect: true) hosts = ::Aerospike::Host::Parse.(hosts || ENV["AEROSPIKE_HOSTS"] || "localhost") @@ -230,11 +222,11 @@ def truncate(namespace, set_name = nil, before_last_update = nil, options = {}) str_cmd = "truncate:namespace=#{namespace}" str_cmd << ";set=#{set_name}" unless set_name.to_s.strip.empty? else - if node.supports_feature?(Aerospike::Features::TRUNCATE_NAMESPACE) - str_cmd = "truncate-namespace:namespace=#{namespace}" - else - str_cmd = "truncate:namespace=#{namespace}" - end + str_cmd = if node.supports_feature?(Aerospike::Features::TRUNCATE_NAMESPACE) + "truncate-namespace:namespace=#{namespace}" + else + "truncate:namespace=#{namespace}" + end end if before_last_update @@ -329,15 +321,8 @@ def batch_get(keys, bin_names = nil, options = nil) bin_names = nil end - if policy.use_batch_direct - key_map = BatchItem.generate_map(keys) - execute_batch_direct_commands(policy, keys) do |node, batch| - BatchDirectCommand.new(node, batch, policy, key_map, bin_names, results, info_flags) - end - else - execute_batch_index_commands(policy, keys) do |node, batch| - BatchIndexCommand.new(node, batch, policy, bin_names, results, info_flags) - end + execute_batch_index_commands(policy, keys) do |node, batch| + BatchIndexCommand.new(node, batch, policy, bin_names, results, info_flags) end results @@ -358,15 +343,8 @@ def batch_exists(keys, options = nil) policy = create_policy(options, BatchPolicy, default_batch_policy) results = Array.new(keys.length) - if policy.use_batch_direct - key_map = BatchItem.generate_map(keys) - execute_batch_direct_commands(policy, keys) do |node, batch| - BatchDirectExistsCommand.new(node, batch, policy, key_map, results) - end - else - execute_batch_index_commands(policy, keys) do |node, batch| - BatchIndexExistsCommand.new(node, batch, policy, results) - end + execute_batch_index_commands(policy, keys) do |node, batch| + BatchIndexExistsCommand.new(node, batch, policy, results) end results @@ -430,7 +408,7 @@ def register_udf(udf_body, server_path, language, options = nil) end if res["error"] - raise Aerospike::Exceptions::CommandRejected.new("Registration failed: #{res["error"]}\nFile: #{res["file"]}\nLine: #{res["line"]}\nMessage: #{res["message"]}") + raise Aerospike::Exceptions::CommandRejected.new("Registration failed: #{res['error']}\nFile: #{res['file']}\nLine: #{res['line']}\nMessage: #{res['message']}") end UdfRegisterTask.new(@cluster, server_path) @@ -566,7 +544,8 @@ def execute_udf_on_query(statement, package_name, function_name, function_args = # ctx is an optional list of context. Supported on server v6.1+. def create_index(namespace, set_name, index_name, bin_name, index_type, collection_type = nil, options = nil, ctx: nil) if options.nil? && collection_type.is_a?(Hash) - options, collection_type = collection_type, nil + options = collection_type + collection_type = nil end policy = create_policy(options, Policy, default_info_policy) @@ -943,13 +922,11 @@ def create_policy(policy, policy_klass, default_policy = nil) when Hash policy_klass.new(policy) else - fail TypeError, "policy should be a #{policy_klass.name} instance or a Hash" + raise TypeError, "policy should be a #{policy_klass.name} instance or a Hash" end end - def cluster=(cluster) - @cluster = cluster - end + attr_writer :cluster def cluster_config_changed(cluster) Aerospike.logger.debug { "Cluster config change detected; active nodes: #{cluster.nodes.map(&:name)}" } @@ -998,29 +975,5 @@ def execute_batch_index_commands(policy, keys) threads.each(&:join) end - - def execute_batch_direct_commands(policy, keys) - if @cluster.nodes.empty? - raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Executing Batch Direct command failed because cluster is empty.") - end - - batch_nodes = BatchDirectNode.generate_list(@cluster, policy.replica, keys) - threads = [] - - # Use a thread per namespace per node - batch_nodes.each do |batch_node| - # copy to avoid race condition - bn = batch_node - bn.batch_namespaces.each do |batch| - threads << Thread.new do - Thread.current.abort_on_exception = true - command = yield batch_node.node, batch - execute_command(command) - end - end - end - - threads.each(&:join) - end end # class end # module diff --git a/lib/aerospike/command/batch_direct_command.rb b/lib/aerospike/command/batch_direct_command.rb deleted file mode 100644 index 3c96d82e..00000000 --- a/lib/aerospike/command/batch_direct_command.rb +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2014-2020 Aerospike, Inc. -# -# Portions may be licensed to Aerospike, Inc. under one or more contributor -# license agreements. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -require 'aerospike/command/multi_command' - -module Aerospike - - class BatchDirectCommand < MultiCommand #:nodoc: - - attr_accessor :batch - attr_accessor :policy - attr_accessor :key_map - attr_accessor :bin_names - attr_accessor :results - attr_accessor :read_attr - - def initialize(node, batch, policy, key_map, bin_names, results, read_attr) - super(node) - - @batch = batch - @policy = policy - @key_map = key_map - @bin_names = bin_names - @results = results - @read_attr = read_attr - end - - def write_buffer - # Estimate buffer size - begin_cmd - byte_size = batch.keys.length * DIGEST_SIZE - - @data_offset += batch.namespace.bytesize + - FIELD_HEADER_SIZE + byte_size + FIELD_HEADER_SIZE - - if bin_names - bin_names.each do |bin_name| - estimate_operation_size_for_bin_name(bin_name) - end - end - - size_buffer - - operation_count = 0 - if bin_names - operation_count = bin_names.length - end - - write_header_read(policy, read_attr, 0, 2, operation_count) - write_field_string(batch.namespace, Aerospike::FieldType::NAMESPACE) - write_field_header(byte_size, Aerospike::FieldType::DIGEST_RIPE_ARRAY) - - batch.keys.each do |key| - @data_offset += @data_buffer.write_binary(key.digest, @data_offset) - end - - if bin_names - bin_names.each do |bin_name| - write_operation_for_bin_name(bin_name, Aerospike::Operation::READ) - end - end - - end_cmd - mark_compressed(@policy) - end - - # Parse all results in the batch. Add records to shared list. - # If the record was not found, the bins will be nil. - def parse_row(result_code) - generation = @data_buffer.read_int32(6) - expiration = @data_buffer.read_int32(10) - field_count = @data_buffer.read_int16(18) - op_count = @data_buffer.read_int16(20) - - key = parse_key(field_count) - - item = key_map[key.digest] - if item - if result_code == 0 - index = item.index - key = item.key - results[index] = parse_record(key, op_count, generation, expiration) - end - else - Aerospike.logger.warn("Unexpected batch key returned: #{key}") - end - end - - end # class - -end # module diff --git a/lib/aerospike/command/batch_direct_exists_command.rb b/lib/aerospike/command/batch_direct_exists_command.rb deleted file mode 100644 index 803af8c9..00000000 --- a/lib/aerospike/command/batch_direct_exists_command.rb +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2014-2020 Aerospike, Inc. -# -# Portions may be licensed to Aerospike, Inc. under one or more contributor -# license agreements. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -require 'aerospike/command/batch_direct_command' - -module Aerospike - - class BatchDirectExistsCommand < BatchDirectCommand #:nodoc: - - def initialize(node, batch, policy, key_map, results) - super(node, batch, policy, key_map, nil, results, INFO1_READ | INFO1_NOBINDATA) - end - - # Parse all results in the batch. Add records to shared list. - # If the record was not found, the bins will be nil. - def parse_row(result_code) - field_count = @data_buffer.read_int16(18) - op_count = @data_buffer.read_int16(20) - - if op_count > 0 - raise Aerospike::Exceptions::Parse.new('Received bins that were not requested!') - end - - key = parse_key(field_count) - item = key_map[key.digest] - - if item - index = item.index - results[index] = (result_code == 0) - else - Aerospike::logger.debug("Unexpected batch key returned: #{key.namespace}, #{key.digest}") - end - end - - end # class - -end # module diff --git a/lib/aerospike/command/batch_direct_node.rb b/lib/aerospike/command/batch_direct_node.rb deleted file mode 100644 index dc1aab56..00000000 --- a/lib/aerospike/command/batch_direct_node.rb +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2014-2020 Aerospike, Inc. -# -# Portions may be licensed to Aerospike, Inc. under one or more contributor -# license agreements. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -module Aerospike - - BatchNamespace = Struct.new :namespace, :keys - - class BatchDirectNode #:nodoc: - - attr_accessor :node - attr_accessor :batch_namespaces - - def self.generate_list(cluster, replica_policy, keys) - keys.group_by { |key| cluster.get_node_for_key(replica_policy, key) } - .map { |node, keys_for_node| BatchDirectNode.new(node, keys_for_node) } - end - - def initialize(node, keys) - @node = node - @batch_namespaces = keys.group_by(&:namespace) - .map { |ns, keys_for_ns| BatchNamespace.new(ns, keys_for_ns) } - end - - end - -end diff --git a/lib/aerospike/command/batch_index_command.rb b/lib/aerospike/command/batch_index_command.rb index 65835279..495e52f5 100644 --- a/lib/aerospike/command/batch_index_command.rb +++ b/lib/aerospike/command/batch_index_command.rb @@ -21,11 +21,7 @@ module Aerospike class BatchIndexCommand < MultiCommand #:nodoc: - attr_accessor :batch - attr_accessor :policy - attr_accessor :bin_names - attr_accessor :results - attr_accessor :read_attr + attr_accessor :batch, :policy, :bin_names, :results, :read_attr def initialize(node, batch, policy, bin_names, results, read_attr) super(node) @@ -42,8 +38,8 @@ def write_buffer field_count_row = 1 field_count = 1 - predexp_size = estimate_predexp(@policy.predexp) - field_count += 1 if predexp_size > 0 + exp_size = estimate_expression_size(@policy.filter_exp) + field_count += 1 if exp_size > 0 if bin_names bin_names.each do |bin_name| @@ -58,7 +54,7 @@ def write_buffer batch.keys.each do |key| @data_offset += key.digest.length + 4 # 4 byte batch offset - if prev != nil && prev.namespace == key.namespace + if !prev.nil? && prev.namespace == key.namespace @data_offset += 1 else @data_offset += key.namespace.bytesize + FIELD_HEADER_SIZE + 1 + 1 + 2 + 2 # repeat/no-repeat flag + read_attr flags + field_count + operation_count @@ -68,7 +64,7 @@ def write_buffer size_buffer write_header_read(policy, read_attr | INFO1_BATCH, 0, field_count, 0) - write_predexp(@policy.predexp, predexp_size) + write_filter_exp(@policy.filter_exp, exp_size) write_field_header(0, Aerospike::FieldType::BATCH_INDEX) @data_offset += @data_buffer.write_int32(batch.keys.length, @data_offset) @@ -80,7 +76,7 @@ def write_buffer @data_offset += @data_buffer.write_int32(index, @data_offset) @data_offset += @data_buffer.write_binary(key.digest, @data_offset) - if (prev != nil && prev.namespace == key.namespace) + if !prev.nil? && prev.namespace == key.namespace @data_offset += @data_buffer.write_byte(1, @data_offset) else @data_offset += @data_buffer.write_byte(0, @data_offset) diff --git a/lib/aerospike/command/command.rb b/lib/aerospike/command/command.rb index 4bedc891..05807a3d 100644 --- a/lib/aerospike/command/command.rb +++ b/lib/aerospike/command/command.rb @@ -112,9 +112,6 @@ def set_write(policy, operation, key, bins) begin_cmd field_count = estimate_key_size(key, policy) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 @@ -126,7 +123,6 @@ def set_write(policy, operation, key, bins) write_header_write(policy, INFO2_WRITE, field_count, bins.length) write_key(key, policy) - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) bins.each do |bin| @@ -142,16 +138,12 @@ def set_delete(policy, key) begin_cmd field_count = estimate_key_size(key) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 size_buffer - write_header_write(policy, INFO2_WRITE | INFO2_DELETE, field_count, 0) + write_header_write(policy, INFO2_WRITE | INFO2_DELETE, field_count, 0) write_key(key) - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) end_cmd end @@ -161,17 +153,13 @@ def set_touch(policy, key) begin_cmd field_count = estimate_key_size(key) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 estimate_operation_size size_buffer - write_header_write(policy, INFO2_WRITE, field_count, 1) + write_header_write(policy, INFO2_WRITE, field_count, 1) write_key(key) - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) write_operation_for_operation_type(Aerospike::Operation::TOUCH) end_cmd @@ -182,16 +170,12 @@ def set_exists(policy, key) begin_cmd field_count = estimate_key_size(key) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 size_buffer write_header_read_header(policy, INFO1_READ | INFO1_NOBINDATA, field_count, 0) write_key(key) - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) end_cmd end @@ -201,16 +185,12 @@ def set_read_for_key_only(policy, key) begin_cmd field_count = estimate_key_size(key) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 size_buffer write_header_read(policy, INFO1_READ | INFO1_GET_ALL, 0, field_count, 0) write_key(key) - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) end_cmd end @@ -221,9 +201,6 @@ def set_read(policy, key, bin_names) begin_cmd field_count = estimate_key_size(key) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 @@ -239,7 +216,6 @@ def set_read(policy, key, bin_names) write_header_read(policy, attr, 0, field_count, bin_names.length) write_key(key) - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) bin_names.each do |bin_name| @@ -258,9 +234,6 @@ def set_read_header(policy, key) begin_cmd field_count = estimate_key_size(key) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 @@ -273,7 +246,6 @@ def set_read_header(policy, key) write_header_read_header(policy, INFO1_READ|INFO1_NOBINDATA, field_count, 0) write_key(key) - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) end_cmd mark_compressed(policy) @@ -284,9 +256,6 @@ def set_operate(policy, key, args) begin_cmd field_count = estimate_key_size(key, policy) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(policy.filter_exp) field_count += 1 if exp_size > 0 @@ -296,7 +265,6 @@ def set_operate(policy, key, args) write_header_read_write(policy, args.read_attr, args.write_attr, field_count, args.operations.length) write_key(key, policy) - write_predexp(policy.predexp, predexp_size) write_filter_exp(policy.filter_exp, exp_size) args.operations.each do |operation| @@ -311,9 +279,6 @@ def set_udf(policy, key, package_name, function_name, args) begin_cmd field_count = estimate_key_size(key, policy) - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 @@ -324,7 +289,6 @@ def set_udf(policy, key, package_name, function_name, args) write_header_write(policy, INFO2_WRITE, field_count, 0) write_key(key, policy) - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) write_field_string(package_name, Aerospike::FieldType::UDF_PACKAGE_NAME) write_field_string(function_name, Aerospike::FieldType::UDF_FUNCTION) @@ -373,9 +337,6 @@ def set_scan(cluster, policy, namespace, set_name, bin_names, node_partitions) field_count += 1 end - predexp_size = estimate_predexp(policy.predexp) - field_count += 1 if predexp_size > 0 - exp_size = estimate_expression_size(@policy.filter_exp) field_count += 1 if exp_size > 0 @@ -446,7 +407,6 @@ def set_scan(cluster, policy, namespace, set_name, bin_names, node_partitions) write_field_int(policy.records_per_second, Aerospike::FieldType::RECORDS_PER_SECOND) end - write_predexp(policy.predexp, predexp_size) write_filter_exp(@policy.filter_exp, exp_size) # write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS) @@ -475,7 +435,6 @@ def set_scan(cluster, policy, namespace, set_name, bin_names, node_partitions) end_cmd end - def set_query(cluster, policy, statement, background, node_partitions) function_arg_buffer = nil field_count = 0 @@ -525,7 +484,7 @@ def set_query(cluster, policy, statement, background, node_partitions) # Estimate INDEX_RANGE field. @data_offset += FIELD_HEADER_SIZE - filter_size += 1 # num filters + filter_size += 1 # num filters filter_size += filter.estimate_size @data_offset += filter_size @@ -539,14 +498,6 @@ def set_query(cluster, policy, statement, background, node_partitions) end statement.set_task_id - predexp = policy.predexp || statement.predexp - - if predexp - @data_offset += FIELD_HEADER_SIZE - pred_size = Aerospike::PredExp.estimate_size(predexp) - @data_offset += pred_size - field_count += 1 - end unless policy.filter_exp.nil? exp_size = estimate_expression_size(policy.filter_exp) @@ -656,13 +607,6 @@ def set_query(cluster, policy, statement, background, node_partitions) # Write task_id field write_field_int64(statement.task_id, FieldType::TRAN_ID) - unless predexp.nil? - write_field_header(pred_size, Aerospike::FieldType::PREDEXP) - @data_offset = Aerospike::PredExp.write( - predexp, @data_buffer, @data_offset - ) - end - if filter type = filter.collection_type @@ -725,7 +669,6 @@ def set_query(cluster, policy, statement, background, node_partitions) end_cmd end - def execute iterations = 0 @@ -752,7 +695,7 @@ def execute # Socket connection error has occurred. Decrease health and retry. @node.decrease_health - Aerospike.logger.error("Node #{@node.to_s}: #{e}") + Aerospike.logger.error("Node #{@node}: #{e}") else Aerospike.logger.error("No node available for transaction: #{e}") end @@ -785,7 +728,7 @@ def execute # Close socket to flush out possible garbage. Do not put back in pool. @conn.close if @conn - Aerospike.logger.error("Node #{@node.to_s}: #{e}") + Aerospike.logger.error("Node #{@node}: #{e}") # IO error means connection to server @node is unhealthy. # Reflect cmd status. @node.decrease_health @@ -853,14 +796,14 @@ def estimate_key_size(key, policy = nil) field_count += 1 end - return field_count + field_count end def estimate_udf_size(package_name, function_name, bytes) @data_offset += package_name.bytesize + FIELD_HEADER_SIZE @data_offset += function_name.bytesize + FIELD_HEADER_SIZE @data_offset += bytes.bytesize + FIELD_HEADER_SIZE - return 3 + 3 end def estimate_operation_size_for_bin(bin) @@ -890,16 +833,6 @@ def estimate_operation_size @data_offset += OPERATION_HEADER_SIZE end - def estimate_predexp(predexp) - if predexp && !predexp.empty? - @data_offset += FIELD_HEADER_SIZE - sz = Aerospike::PredExp.estimate_size(predexp) - @data_offset += sz - return sz - end - return 0 - end - def estimate_expression_size(exp) unless exp.nil? @data_offset += FIELD_HEADER_SIZE @@ -1199,15 +1132,6 @@ def write_field_header(size, ftype) @data_offset += 1 end - def write_predexp(predexp, predexp_size) - if predexp && !predexp.empty? - write_field_header(predexp_size, Aerospike::FieldType::FILTER_EXP) - @data_offset = Aerospike::PredExp.write( - predexp, @data_buffer, @data_offset - ) - end - end - def write_filter_exp(exp, exp_size) unless exp.nil? write_field_header(exp_size, Aerospike::FieldType::FILTER_EXP) @@ -1238,7 +1162,7 @@ def use_compression? def compress_buffer if @data_offset > COMPRESS_THRESHOLD - compressed = Zlib::deflate(@data_buffer.buf, Zlib::DEFAULT_COMPRESSION) + compressed = Zlib.deflate(@data_buffer.buf, Zlib::DEFAULT_COMPRESSION) # write original size as header proto_s = format("%08d", 0) diff --git a/lib/aerospike/info.rb b/lib/aerospike/info.rb index 30217e3b..b8e231e4 100644 --- a/lib/aerospike/info.rb +++ b/lib/aerospike/info.rb @@ -66,7 +66,6 @@ def self.parse_multiple_response(buf_length, buffer) private def self.send_command(conn, offset, buffer) - begin # Write size field. size = (offset - 8) | (2 << 56) | (1 << 48) @@ -82,12 +81,11 @@ def self.send_command(conn, offset, buffer) buffer.resize(length) conn.read(buffer, length) - return length - rescue => e + length + rescue => e Aerospike.logger.error(e) conn.close if conn raise e - end end end diff --git a/lib/aerospike/policy/batch_policy.rb b/lib/aerospike/policy/batch_policy.rb index a5800b83..c54247ab 100644 --- a/lib/aerospike/policy/batch_policy.rb +++ b/lib/aerospike/policy/batch_policy.rb @@ -22,11 +22,12 @@ module Aerospike # Container object for batch policy command. class BatchPolicy < Policy - attr_accessor :use_batch_direct - def initialize(opt={}) super(opt) + # [:nodoc:] + # DEPRECATED + # This setting does not have any effect anymore. # Use old batch direct protocol where batch reads are handled by direct # low-level batch server database routines. The batch direct protocol can # be faster when there is a single namespace. But there is one important @@ -39,7 +40,7 @@ def initialize(opt={}) # # Default: false (use new batch index protocol if server supports it) @use_batch_direct = opt.fetch(:use_batch_direct) { false } - + self end diff --git a/lib/aerospike/policy/policy.rb b/lib/aerospike/policy/policy.rb index 054f51f1..69edc810 100644 --- a/lib/aerospike/policy/policy.rb +++ b/lib/aerospike/policy/policy.rb @@ -22,7 +22,7 @@ module Aerospike # Container object for client policy command. class Policy attr_accessor :filter_exp, :priority, :timeout, :max_retries, :sleep_between_retries, :consistency_level, - :predexp, :fail_on_filtered_out, :replica, :use_compression, :socket_timeout + :fail_on_filtered_out, :replica, :use_compression, :socket_timeout alias total_timeout timeout alias total_timeout= timeout= @@ -57,44 +57,7 @@ def initialize(opt = {}) # TODO: Remove for next major release @priority = opt[:priority] || Priority::DEFAULT - # Set optional predicate expression filters in postfix notation. - # Predicate expression filters are applied on the query results on the server. - # Predicate expression filters may occur on any bin in the record. - # Requires Aerospike Server versions >= 3.12 - # - # Postfix notation is described here: http://wiki.c2.com/?PostfixNotation - # - # Example: - # - # (c >= 11 and c <= 20) or (d > 3 and (d < 5) - # policy.predexp = [ - # PredExp.integer_bin("c"), - # PredExp.integer_value(11), - # PredExp.integer_greater_eq(), - # PredExp.integer_bin("c"), - # PredExp.integer_value(20), - # PredExp.integer_less_eq(), - # PredExp.and(2), - # PredExp.integer_bin("d"), - # PredExp.integer_value(3), - # PredExp.integer_greater(), - # PredExp.integer_bin("d"), - # PredExp.integer_value(5), - # PredExp.integer_less(), - # PredExp.and(2), - # PredExp.or(2) - # ] - # - # # Record last update time > 2017-01-15 - # policy.predexp = [ - # PredExp.rec_last_update(), - # PredExp.integer_value(Time.new(2017, 1, 15).to_i), - # PredExp.integer_greater(), - # PredExp.integer_greater() - # ] - @predexp = opt[:predexp] || nil - - # Throw exception if @predexp is defined and that filter evaluates + # Throw exception if @filter_exp is defined and that filter evaluates # to false (transaction ignored). The Aerospike::Exceptions::Aerospike # will contain result code Aerospike::ResultCode::FILTERED_OUT. # This field is not applicable to batch, scan or query commands. diff --git a/lib/aerospike/query/pred_exp.rb b/lib/aerospike/query/pred_exp.rb deleted file mode 100644 index 761615cc..00000000 --- a/lib/aerospike/query/pred_exp.rb +++ /dev/null @@ -1,192 +0,0 @@ -# frozen_string_literal: true - -module Aerospike - class PredExp - AND = 1 - OR = 2 - NOT = 3 - INTEGER_VALUE = 10 - STRING_VALUE = 11 - GEOJSON_VALUE = 12 - INTEGER_BIN = 100 - STRING_BIN = 101 - GEOJSON_BIN = 102 - LIST_BIN = 103 - MAP_BIN = 104 - INTEGER_VAR = 120 - STRING_VAR = 121 - GEOJSON_VAR = 122 - RECSIZE = 150 - LAST_UPDATE = 151 - VOID_TIME = 152 - INTEGER_EQUAL = 200 - INTEGER_UNEQUAL = 201 - INTEGER_GREATER = 202 - INTEGER_GREATEREQ = 203 - INTEGER_LESS = 204 - INTEGER_LESSEQ = 205 - STRING_EQUAL = 210 - STRING_UNEQUAL = 211 - STRING_REGEX = 212 - GEOJSON_WITHIN = 220 - GEOJSON_CONTAINS = 221 - LIST_ITERATE_OR = 250 - MAPKEY_ITERATE_OR = 251 - MAPVAL_ITERATE_OR = 252 - LIST_ITERATE_AND = 253 - MAPKEY_ITERATE_AND = 254 - MAPVAL_ITERATE_AND = 255 - - def self.and(nexp) - AndOr.new(AND, nexp) - end - - def self.or(nexp) - AndOr.new(OR, nexp) - end - - def self.not - Op.new(NOT) - end - - def self.integer_value(value) - IntegerValue.new(value, INTEGER_VALUE) - end - - def self.string_value(value) - StringValue.new(value, STRING_VALUE) - end - - def self.geojson_value(value) - raise(ArgumentError, "value must be a GeoJSON object!") unless value.is_a?(Aerospike::GeoJSON) - GeoJsonValue.new(value.to_s, GEOJSON_VALUE) - end - - def self.integer_bin(name) - StringValue.new(name, INTEGER_BIN) - end - - def self.string_bin(name) - StringValue.new(name, STRING_BIN) - end - - def self.geojson_bin(name) - StringValue.new(name, GEOJSON_BIN) - end - - def self.list_bin(name) - StringValue.new(name, LIST_BIN) - end - - def self.map_bin(name) - StringValue.new(name, MAP_BIN) - end - - def self.integer_var(name) - StringValue.new(name, INTEGER_VAR) - end - - def self.string_var(name) - StringValue.new(name, STRING_VAR) - end - - def self.geojson_var(name) - StringValue.new(name, GEOJSON_VAR) - end - - def self.record_size - Op.new(RECSIZE) - end - - def self.last_update - Op.new(LAST_UPDATE) - end - - def self.void_time - Op.new(VOID_TIME) - end - - def self.integer_equal - Op.new(INTEGER_EQUAL) - end - - def self.integer_unequal - Op.new(INTEGER_UNEQUAL) - end - - def self.integer_greater - Op.new(INTEGER_GREATER) - end - - def self.integer_greater_eq - Op.new(INTEGER_GREATEREQ) - end - - def self.integer_less - Op.new(INTEGER_LESS) - end - - def self.integer_less_eq - Op.new(INTEGER_LESSEQ) - end - - def self.string_equal - Op.new(STRING_EQUAL) - end - - def self.string_unequal - Op.new(STRING_UNEQUAL) - end - - def self.string_regex(flags) - Regex.new(STRING_REGEX, flags) - end - - def self.geojson_within - Op.new(GEOJSON_WITHIN) - end - - def self.geojson_contains - Op.new(GEOJSON_CONTAINS) - end - - def self.list_iterate_or(var_name) - StringValue.new(var_name, LIST_ITERATE_OR) - end - - def self.list_iterate_and(var_name) - StringValue.new(var_name, LIST_ITERATE_AND) - end - - def self.mapkey_iterate_or(var_name) - StringValue.new(var_name, MAPKEY_ITERATE_OR) - end - - def self.mapkey_iterate_and(var_name) - StringValue.new(var_name, MAPKEY_ITERATE_AND) - end - - def self.mapval_iterate_or(var_name) - StringValue.new(var_name, MAPVAL_ITERATE_OR) - end - - def self.mapval_iterate_and(var_name) - StringValue.new(var_name, MAPVAL_ITERATE_AND) - end - - - - def self.estimate_size(predexp) - return 0 unless predexp - predexp.map(&:estimate_size).inject { |sum, size| sum + size } - end - - def self.write(predexp, buffer, offset) - predexp.each do |p| - offset = p.write(buffer, offset) - end - - offset - end - end -end diff --git a/lib/aerospike/query/pred_exp/and_or.rb b/lib/aerospike/query/pred_exp/and_or.rb deleted file mode 100644 index 4da893fd..00000000 --- a/lib/aerospike/query/pred_exp/and_or.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module Aerospike - class PredExp - class AndOr < PredExp - def initialize(op, nexp) - @op = op - @nexp = nexp - end - - def estimate_size - 8 - end - - def write(buffer, offset) - # write type - buffer.write_int16(@op, offset) - offset += 2 - - # write length - buffer.write_int32(2, offset) - offset += 4 - - # write predicate count - buffer.write_int16(@nexp, offset) - offset += 2 - - offset - end - end - end -end diff --git a/lib/aerospike/query/pred_exp/geo_json_value.rb b/lib/aerospike/query/pred_exp/geo_json_value.rb deleted file mode 100644 index da4a8dd8..00000000 --- a/lib/aerospike/query/pred_exp/geo_json_value.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module Aerospike - class PredExp - class GeoJsonValue < PredExp - def initialize(value, type) - @value = value - @type = type - end - - def estimate_size - @value.bytesize + 9 - end - - def write(buffer, offset) - # tag - buffer.write_uint16(@type, offset) - offset += 2 - - # len - buffer.write_uint32(@value.bytesize + 3, offset) - offset += 4 - - # flags - - buffer.write_byte(0, offset) - offset += 1 - - # ncells - buffer.write_uint16(0, offset) - offset += 2 - - # value - len = buffer.write_binary(@value, offset) - offset += len - - offset - end - end - end -end diff --git a/lib/aerospike/query/pred_exp/integer_value.rb b/lib/aerospike/query/pred_exp/integer_value.rb deleted file mode 100644 index 42d71b24..00000000 --- a/lib/aerospike/query/pred_exp/integer_value.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module Aerospike - class PredExp - class IntegerValue < PredExp - def initialize(value, type) - @value = value - @type = type - end - - def estimate_size - 14 - end - - def write(buffer, offset) - # Write type - buffer.write_int16(@type, offset) - offset += 2 - - # Write length - buffer.write_int32(8, offset) - offset += 4 - - # Write value. - buffer.write_int64(@value, offset) - offset += 8 - - offset - end - end - end -end diff --git a/lib/aerospike/query/pred_exp/op.rb b/lib/aerospike/query/pred_exp/op.rb deleted file mode 100644 index 53ca25f9..00000000 --- a/lib/aerospike/query/pred_exp/op.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module Aerospike - class PredExp - class Op < PredExp - def initialize(op) - @op = op - end - - def estimate_size - 6 - end - - def write(buffer, offset) - # write type - buffer.write_int16(@op, offset) - offset += 2 - - # write zero length - buffer.write_int32(0, offset) - offset += 4 - - offset - end - end - end -end diff --git a/lib/aerospike/query/pred_exp/regex.rb b/lib/aerospike/query/pred_exp/regex.rb deleted file mode 100644 index f74cd744..00000000 --- a/lib/aerospike/query/pred_exp/regex.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module Aerospike - class PredExp - class Regex < PredExp - def initialize(op, flag = Flags::NONE) - @op = op - @flag = flag - end - - def estimate_size - 10 - end - - def write(buffer, offset) - # write op type - buffer.write_int16(@op, offset) - offset += 2 - - # write length - buffer.write_int32(4, offset) - offset += 4 - - # write predicate count - buffer.write_int32(@flag, offset) - offset += 4 - - offset - end - end - end -end diff --git a/lib/aerospike/query/pred_exp/regex_flags.rb b/lib/aerospike/query/pred_exp/regex_flags.rb deleted file mode 100644 index 354d4131..00000000 --- a/lib/aerospike/query/pred_exp/regex_flags.rb +++ /dev/null @@ -1,23 +0,0 @@ -# fr# frozen_string_literal: true - -module Aerospike - class PredExp - # Regex bit flags - module RegexFlags - # Regex defaults - NONE = 0 - - # Use POSIX Extended Regular Expression syntax when interpreting regex. - EXTENDED = 1 - - # Do not differentiate case. - ICASE = 2 - - # Do not report position of matches. - NOSUB = 4 - - # Match-any-character operators don't match a newline. - NEWLINE = 8 - end - end -end diff --git a/lib/aerospike/query/pred_exp/string_value.rb b/lib/aerospike/query/pred_exp/string_value.rb deleted file mode 100644 index 723a6e3c..00000000 --- a/lib/aerospike/query/pred_exp/string_value.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Aerospike - class PredExp - class StringValue < PredExp - def initialize(value, type) - @value = value - @type = type - end - - def estimate_size - @value.bytesize + 6 - end - - def write(buffer, offset) - buffer.write_int16(@type, offset) - offset += 2 - - buffer.write_int32(@value.bytesize, offset) - offset += 4 - - len = buffer.write_binary(@value, offset) - offset += len - - offset - end - end - end -end diff --git a/lib/aerospike/query/statement.rb b/lib/aerospike/query/statement.rb index 56d4a560..c5f49895 100644 --- a/lib/aerospike/query/statement.rb +++ b/lib/aerospike/query/statement.rb @@ -21,9 +21,7 @@ module Aerospike # index name, filters, and operations. class Statement - attr_accessor :namespace, :set_name, :index_name, :bin_names, :task_id - attr_accessor :filters, :package_name, :function_name, :function_args, :operations - attr_accessor :predexp, :return_data, :records_per_second + attr_accessor :namespace, :set_name, :index_name, :bin_names, :task_id, :filters, :package_name, :function_name, :function_args, :operations, :return_data, :records_per_second def initialize(namespace, set_name, bin_names=[]) # Namespace determines query Namespace @@ -46,16 +44,6 @@ def initialize(namespace, set_name, bin_names=[]) # aggregation function. @filters = [] - # Predicate expressions in postfix notation. If the expression is evaluated to false, - # the record will be ommited in the results. - # - # This method is redundant because PredExp can now be set in the base Policy for - # any transaction (including queries). - # - # NOTE : Policy.predexp takes precedence to this value. This value will be - # deprecated in the future. - @predexp = nil - @package_name = nil @function_name = nil @function_args = nil @@ -83,27 +71,23 @@ def set_aggregate_function(package_name, function_name, function_args=[], return end def is_scan? - return (filters.nil? || (filters.empty?)) + filters.nil? || filters.empty? end def set_task_id - while @task_id == 0 - @task_id = rand(RAND_MAX) - end + @task_id = rand(RAND_MAX) while @task_id == 0 end def reset_task_id @task_id = rand(RAND_MAX) - while @task_id == 0 - @task_id = rand(RAND_MAX) - end + @task_id = rand(RAND_MAX) while @task_id == 0 end private - RAND_MAX = 2**63 - 1 + RAND_MAX = (2**63) - 1 end # class end diff --git a/spec/aerospike/batch_spec.rb b/spec/aerospike/batch_spec.rb index 964d2d73..bffd3283 100644 --- a/spec/aerospike/batch_spec.rb +++ b/spec/aerospike/batch_spec.rb @@ -23,9 +23,9 @@ describe "#batch_exists" do shared_examples_for 'a batch_exists request' do - let(:batch_policy) { - Aerospike::BatchPolicy.new(use_batch_direct: use_batch_direct) - } + let(:batch_policy) do + Aerospike::BatchPolicy.new + end let(:existing_keys) { Array.new(3) { Support.gen_random_key } } let(:no_such_key) { Support.gen_random_key } let(:keys) { existing_keys } @@ -34,10 +34,10 @@ before do existing_keys.each_with_index do |key, idx| client.put(key, { - 'idx' => idx, - 'key' => key.user_key, - 'rnd' => rand - }) + 'idx' => idx, + 'key' => key.user_key, + 'rnd' => rand + }) end end @@ -65,23 +65,15 @@ end context 'using batch index protocol' do - let(:use_batch_direct) { false } - - it_behaves_like 'a batch_exists request' - end - - context 'using batch direct protocol', skip: Support.min_version?('4.4.0') do - let(:use_batch_direct) { true } - it_behaves_like 'a batch_exists request' end end describe "#batch_get" do shared_examples_for 'a batch_get request' do - let(:batch_policy) { - Aerospike::BatchPolicy.new(use_batch_direct: use_batch_direct) - } + let(:batch_policy) do + Aerospike::BatchPolicy.new + end let(:existing_keys) { Array.new(3) { Support.gen_random_key } } let(:no_such_key) { Support.gen_random_key } let(:keys) { existing_keys } @@ -91,10 +83,10 @@ before do existing_keys.each_with_index do |key, idx| client.put(key, { - 'idx' => idx, - 'key' => key.user_key, - 'rnd' => rand - }) + 'idx' => idx, + 'key' => key.user_key, + 'rnd' => rand + }) end end @@ -133,7 +125,7 @@ end context 'when given a list of bin names' do - let(:bins) { %w[ idx rnd ] } + let(:bins) { %w[idx rnd] } it 'returns only the specified bins' do expect(result.first.bins.keys).to eql %w[idx rnd] @@ -158,23 +150,16 @@ end context 'using batch index protocol' do - let(:use_batch_direct) { false } - it_behaves_like 'a batch_get request' end - context 'using batch direct protocol', skip: Support.min_version?('4.4.0') do - let(:use_batch_direct) { true } - - it_behaves_like 'a batch_get request' - end end describe "#batch_get_header" do shared_examples_for 'a batch_get_header request' do - let(:batch_policy) { - Aerospike::BatchPolicy.new(use_batch_direct: use_batch_direct) - } + let(:batch_policy) do + Aerospike::BatchPolicy.new + end let(:existing_keys) { Array.new(3) { Support.gen_random_key } } let(:no_such_key) { Support.gen_random_key } let(:keys) { existing_keys } @@ -183,12 +168,10 @@ before do existing_keys.each_with_index do |key, idx| client.put(key, { - 'idx' => idx, - 'key' => key.user_key, - 'rnd' => rand - }, { - ttl: 1000 - }) + 'idx' => idx, + 'key' => key.user_key, + 'rnd' => rand + }, {}) end end @@ -201,7 +184,7 @@ expect(result.map(&:key)).to eql keys end - it 'returns the meta-data for each record' do + it 'returns the meta-data for each record', skip: !Support.ttl_supported? do expect(result.first.generation).to eq 1 expect(result.first.ttl).to be_within(100).of(1000) end @@ -225,15 +208,8 @@ end context 'using batch index protocol' do - let(:use_batch_direct) { false } - it_behaves_like 'a batch_get_header request' end - context 'using batch direct protocol', skip: Support.min_version?('4.4.0') do - let(:use_batch_direct) { true } - - it_behaves_like 'a batch_get_header request' - end end end diff --git a/spec/aerospike/cdt/cdt_list_spec.rb b/spec/aerospike/cdt/cdt_list_spec.rb index ac491edb..618b2368 100644 --- a/spec/aerospike/cdt/cdt_list_spec.rb +++ b/spec/aerospike/cdt/cdt_list_spec.rb @@ -275,7 +275,7 @@ def list_post_op it "returns the value at the specified index" do operation = ListOperation.get_by_index(list_bin, 2) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to be 3 @@ -293,7 +293,7 @@ def list_post_op it "returns the value at the specified index range" do operation = ListOperation.get_by_index_range(list_bin, 1, 3) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([2, 3, 4]) @@ -301,7 +301,7 @@ def list_post_op it "returns all values starting at the specified index if count is not specified" do operation = ListOperation.get_by_index_range(list_bin, 1) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([2, 3, 4, 5]) @@ -313,7 +313,7 @@ def list_post_op it "returns the value at the specified rank" do operation = ListOperation.get_by_rank(list_bin, 0) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to be 1 @@ -325,7 +325,7 @@ def list_post_op it "returns the value at the specified rank range" do operation = ListOperation.get_by_rank_range(list_bin, 1, 3) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(2, 3, 4) @@ -333,7 +333,7 @@ def list_post_op it "returns all values starting at the specified index if count is not specified" do operation = ListOperation.get_by_rank_range(list_bin, 3) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(4, 5) @@ -346,7 +346,7 @@ def list_post_op it "returns the index of the specified value" do operation = ListOperation.get_by_value(list_bin, 2) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(2, 6) @@ -359,7 +359,7 @@ def list_post_op it "returns the indeces of the items in the specified value range" do operation = ListOperation.get_by_value_range(list_bin, 2, 4) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(2, 3, 6) @@ -367,7 +367,7 @@ def list_post_op it "returns the indeces of the items starting with the specified value" do operation = ListOperation.get_by_value_range(list_bin, 2) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(1, 2, 3, 4, 6) @@ -380,7 +380,7 @@ def list_post_op it "returns the indeces of the items in the specified list" do operation = ListOperation.get_by_value_list(list_bin, [2, 4]) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(1, 2, 6) @@ -392,7 +392,7 @@ def list_post_op it "returns the values of the items nearest to and greater than the specified value, by relative rank range" do operation = ListOperation.get_by_value_rel_rank_range(list_bin, 5, 0, 2) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(5, 9) @@ -400,7 +400,7 @@ def list_post_op it "returns the values of the items nearest to and greater than the specified value, starting with the specified relative rank" do operation = ListOperation.get_by_value_rel_rank_range(list_bin, 5, 0) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(5, 9, 11, 15) @@ -412,7 +412,7 @@ def list_post_op it "removes the value at the specified index" do operation = ListOperation.remove_by_index(list_bin, 2) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to be 3 @@ -431,7 +431,7 @@ def list_post_op it "removes the values at the specified index range" do operation = ListOperation.remove_by_index_range(list_bin, 1, 3) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([2, 3, 4]) @@ -440,7 +440,7 @@ def list_post_op it "returns all values starting at the specified index if count is not specified" do operation = ListOperation.remove_by_index_range(list_bin, 1) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([2, 3, 4, 5]) @@ -453,7 +453,7 @@ def list_post_op it "removes the value at the specified rank" do operation = ListOperation.remove_by_rank(list_bin, 0) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to be 1 @@ -466,7 +466,7 @@ def list_post_op it "removes the value at the specified rank range" do operation = ListOperation.remove_by_rank_range(list_bin, 1, 3) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(2, 3, 4) @@ -475,7 +475,7 @@ def list_post_op it "returns all values starting at the specified index if count is not specified" do operation = ListOperation.remove_by_rank_range(list_bin, 3) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(4, 5) @@ -489,7 +489,7 @@ def list_post_op it "removes the index of the specified value" do operation = ListOperation.remove_by_value(list_bin, 2) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(2, 6) @@ -503,7 +503,7 @@ def list_post_op it "removes the indeces of the items in the specified value range" do operation = ListOperation.remove_by_value_range(list_bin, 2, 4) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(2, 3, 6) @@ -512,7 +512,7 @@ def list_post_op it "removes the indeces of the items starting with the specified value" do operation = ListOperation.remove_by_value_range(list_bin, 2) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(1, 2, 3, 4, 6) @@ -526,7 +526,7 @@ def list_post_op it "removes the indeces of the items in the specified list" do operation = ListOperation.remove_by_value_list(list_bin, [2, 4]) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(1, 2, 6) @@ -539,7 +539,7 @@ def list_post_op it "removes the values of the items nearest to and greater than the specified value, by relative rank range" do operation = ListOperation.remove_by_value_rel_rank_range(list_bin, 5, 0, 2) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(5, 9) @@ -548,7 +548,7 @@ def list_post_op it "removes the values of the items nearest to and greater than the specified value, starting with the specified relative rank" do operation = ListOperation.remove_by_value_rel_rank_range(list_bin, 5, 0) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to contain_exactly(5, 9, 11, 15) @@ -561,7 +561,7 @@ def list_post_op it "returns nothing by default" do operation = ListOperation.remove_by_index_range(list_bin, 2, 4) - .and_return(ListReturnType::DEFAULT) + .and_return(ListReturnType::DEFAULT) result = client.operate(key, [operation]) expected = { list_bin => nil } @@ -570,7 +570,7 @@ def list_post_op it "returns the list index" do operation = ListOperation.remove_by_index_range(list_bin, 2, 4) - .and_return(ListReturnType::INDEX) + .and_return(ListReturnType::INDEX) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([2, 3, 4, 5]) @@ -578,7 +578,7 @@ def list_post_op it "returns the reverse list index" do operation = ListOperation.remove_by_index_range(list_bin, 2, 4) - .and_return(ListReturnType::REVERSE_INDEX) + .and_return(ListReturnType::REVERSE_INDEX) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([1, 2, 3, 4]) @@ -586,7 +586,7 @@ def list_post_op it "returns the list rank" do operation = ListOperation.remove_by_index_range(list_bin, 2, 4) - .and_return(ListReturnType::RANK) + .and_return(ListReturnType::RANK) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([2, 4, 6, 0]) @@ -594,7 +594,7 @@ def list_post_op it "returns the reverse list rank" do operation = ListOperation.remove_by_index_range(list_bin, 2, 4) - .and_return(ListReturnType::REVERSE_RANK) + .and_return(ListReturnType::REVERSE_RANK) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([4, 2, 0, 6]) @@ -602,7 +602,7 @@ def list_post_op it "returns the number of items" do operation = ListOperation.remove_by_index_range(list_bin, 2, 4) - .and_return(ListReturnType::COUNT) + .and_return(ListReturnType::COUNT) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to be 4 @@ -610,7 +610,7 @@ def list_post_op it "returns the value of the items" do operation = ListOperation.remove_by_index_range(list_bin, 2, 4) - .and_return(ListReturnType::VALUE) + .and_return(ListReturnType::VALUE) result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([2, 3, 5, 1]) @@ -622,8 +622,8 @@ def list_post_op it "inverts the selection of items affected by the operation" do operation = ListOperation.remove_by_index_range(list_bin, 2, 4) - .and_return(return_type) - .invert_selection + .and_return(return_type) + .invert_selection result = client.operate(key, [operation]) expect(result.bins[list_bin]).to eql([1, 4, 2]) @@ -638,7 +638,7 @@ def list_post_op list = [ [7, 9, 5], [1, 2, 3], - [6, 5, 4, 1], + [6, 5, 4, 1] ] client.put(key, Aerospike::Bin.new(list_bin, list)) @@ -646,7 +646,7 @@ def list_post_op # Append value to new list created after the original 3 lists. operation = [ ListOperation.append(list_bin, 2, ctx: [Context.list_index_create(3, ListOrder::ORDERED, false)], policy: ListPolicy.new(order: ListOrder::ORDERED)), - Aerospike::Operation.get(list_bin), + Aerospike::Operation.get(list_bin) ] record = client.operate(key, operation) @@ -660,7 +660,7 @@ def list_post_op list = [ [7, 9, 5], [1, 2, 3], - [6, 5, 4, 1], + [6, 5, 4, 1] ] client.put(key, Aerospike::Bin.new(list_bin, list)) @@ -670,10 +670,10 @@ def list_post_op record = client.operate(key, [ListOperation.append(list_bin, 11, ctx: [Context.list_index(-1)]), Aerospike::Operation.get(list_bin)]) expect(record.bins[list_bin]).to eq([ - [7, 9, 5], - [1, 2, 3], - [6, 5, 4, 1, 11], - ]) + [7, 9, 5], + [1, 2, 3], + [6, 5, 4, 1, 11] + ]) end it "is used to change a map in nested list" do @@ -682,13 +682,13 @@ def list_post_op m = { "key1" => [ [7, 9, 5], - [13], + [13] ], "key2" => [ [9], [2, 4], - [6, 1, 9], - ], + [6, 1, 9] + ] } client.put(key, Aerospike::Bin.new(list_bin, m)) @@ -701,13 +701,13 @@ def list_post_op { "key1" => [ [7, 9, 5], - [13], + [13] ], "key2" => [ [9], [2, 4, 11], - [6, 1, 9], - ], + [6, 1, 9] + ] } ) end @@ -789,7 +789,7 @@ def list_post_op it "returns all list elements from 10 to Infinity" do operation = ListOperation.get_by_value_range(list_bin, 10, Aerospike::Value::INFINITY) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) @@ -802,7 +802,7 @@ def list_post_op it "returns all list elements that match a wildcard" do operation = ListOperation.get_by_value(list_bin, ["Jim", Aerospike::Value::WILDCARD]) - .and_return(return_type) + .and_return(return_type) result = client.operate(key, [operation]) diff --git a/spec/aerospike/exp/exp_bit_spec.rb b/spec/aerospike/exp/exp_bit_spec.rb index 7dc72374..18ecc926 100644 --- a/spec/aerospike/exp/exp_bit_spec.rb +++ b/spec/aerospike/exp/exp_bit_spec.rb @@ -27,7 +27,7 @@ @set = "query1000" Support.client.truncate(@namespace, @set) - opts = { expiration: 24 * 60 * 60 } + opts = { } @key_count.times do |ii| key = Aerospike::Key.new(@namespace, @set, ii) bytes = bytes_to_str([0b00000001, 0b01000010]) diff --git a/spec/aerospike/exp/exp_hll_spec.rb b/spec/aerospike/exp/exp_hll_spec.rb index 2f14cc63..ebde871e 100644 --- a/spec/aerospike/exp/exp_hll_spec.rb +++ b/spec/aerospike/exp/exp_hll_spec.rb @@ -28,7 +28,7 @@ Support.client.truncate(@namespace, @set) - opts = { expiration: 24 * 60 * 60 } + opts = { } @key_count.times do |ii| key = Aerospike::Key.new(@namespace, @set, ii) bin = { "bin" => ii, "lbin" => [ii, "a"] } diff --git a/spec/aerospike/exp/exp_list_spec.rb b/spec/aerospike/exp/exp_list_spec.rb index cf8eadd6..00d7c702 100644 --- a/spec/aerospike/exp/exp_list_spec.rb +++ b/spec/aerospike/exp/exp_list_spec.rb @@ -26,7 +26,7 @@ @namespace = "test" @set = "query1000" - opts = { expiration: 24 * 60 * 60 } + opts = { } @key_count.times do |ii| key = Aerospike::Key.new(@namespace, @set, ii) ibin = { "bin" => [1, 2, 3, ii] } diff --git a/spec/aerospike/exp/exp_map_spec.rb b/spec/aerospike/exp/exp_map_spec.rb index 3e0a4eee..4ee12468 100644 --- a/spec/aerospike/exp/exp_map_spec.rb +++ b/spec/aerospike/exp/exp_map_spec.rb @@ -26,7 +26,7 @@ @namespace = "test" @set = "query1000" - opts = { expiration: 24 * 60 * 60 } + opts = { } @key_count.times do |ii| key = Aerospike::Key.new(@namespace, @set, ii) ibin = { "bin" => { "test" => ii, "test2" => "a" } } diff --git a/spec/aerospike/exp/expression_spec.rb b/spec/aerospike/exp/expression_spec.rb index f57f5143..f044b4e6 100644 --- a/spec/aerospike/exp/expression_spec.rb +++ b/spec/aerospike/exp/expression_spec.rb @@ -31,7 +31,7 @@ "bin1" => "value#{i}", "bin2" => i, "bin3" => [i, i + 1_000, i + 1_000_000], - "bin4" => { "key#{i}" => i }, + "bin4" => { "key#{i}" => i } } Support.client.put(key, bin_map) end @@ -39,7 +39,7 @@ Support.client.drop_index(@namespace, @set, "index_intval") Support.client.drop_index(@namespace, @set, "index_strval") - wpolicy = { generation: 0, expiration: 24 * 60 * 60 } + wpolicy = { generation: 0 } starbucks = [ [-122.1708441, 37.4241193], @@ -56,7 +56,7 @@ [-122.0303178, 37.3882739], [-122.0464861, 37.3786236], [-122.0582128, 37.3726980], - [-122.0365083, 37.3676930], + [-122.0365083, 37.3676930] ] @record_count.times do |ii| @@ -74,12 +74,12 @@ lat = 37.5 + (0.01 * ii) point = Aerospike::GeoJSON.point(lat, lng) - if ii < starbucks.length - region = Aerospike::GeoJSON.circle(starbucks[ii][0], starbucks[ii][1], 3000.0) - else + region = if ii < starbucks.length + Aerospike::GeoJSON.circle(starbucks[ii][0], starbucks[ii][1], 3000.0) + else # Somewhere off Africa ... - region = Aerospike::GeoJSON.circle(0.0, 0.0, 3000.0) - end + Aerospike::GeoJSON.circle(0.0, 0.0, 3000.0) + end # Accumulate prime factors of the index into a list and map. listval = [] @@ -87,7 +87,7 @@ [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31].each do |ff| if ii >= ff && ii % ff == 0 listval << ff - mapval[ff] = sprintf("0x%04x", ff) + mapval[ff] = format("0x%04x", ff) end end @@ -95,13 +95,13 @@ bins = { "intval" => ii, - "strval" => sprintf("0x%04x", ii), + "strval" => format("0x%04x", ii), "modval" => ii % 10, # "locval" => point, # "rgnval" => region, "lstval" => listval, "mapval" => mapval, - "ballast" => ballast, + "ballast" => ballast } Support.client.put(key, bins, wpolicy) @@ -119,10 +119,12 @@ stmt = Aerospike::Statement.new(@namespace, @set) stmt.filters << Aerospike::Filter.Range("intval", 0, 400) opts = { filter_exp: Aerospike::Exp.int_val(100) } - expect { + expect do rs = client.query(stmt, opts) - rs.each do end - }.to raise_error (Aerospike::Exceptions::Aerospike) { |error| + rs.each do + + end + end.to raise_error(Aerospike::Exceptions::Aerospike) { |error| error.result_code == Aerospike::ResultCode::PARAMETER_ERROR } end @@ -134,7 +136,7 @@ stmt.filters << Aerospike::Filter.Range("intval", 0, 400) opts = { filter_exp: Aerospike::Exp.ge(Aerospike::Exp.int_bin("modval"), Aerospike::Exp.int_val(8)) } - # The query clause selects [0, 1, ... 400, 401] The predexp + # The query clause selects [0, 1, ... 400, 401] The filter_exp # only takes mod 8 and 9, should be 2 pre decade or 80 total. rs = client.query(stmt, opts) @@ -162,11 +164,11 @@ opts = { filter_exp: Aerospike::Exp.or( Aerospike::Exp.and( Aerospike::Exp.not(Aerospike::Exp.eq(Aerospike::Exp.str_bin("strval"), Aerospike::Exp.str_val("0x0001"))), - Aerospike::Exp.ge(Aerospike::Exp.int_bin("modval"), Aerospike::Exp.int_val(8)), + Aerospike::Exp.ge(Aerospike::Exp.int_bin("modval"), Aerospike::Exp.int_val(8)) ), Aerospike::Exp.eq(Aerospike::Exp.str_bin("strval"), Aerospike::Exp.str_val("0x0104")), Aerospike::Exp.eq(Aerospike::Exp.str_bin("strval"), Aerospike::Exp.str_val("0x0105")), - Aerospike::Exp.eq(Aerospike::Exp.str_bin("strval"), Aerospike::Exp.str_val("0x0106")), + Aerospike::Exp.eq(Aerospike::Exp.str_bin("strval"), Aerospike::Exp.str_val("0x0106")) ) } rs = client.query(stmt, opts) @@ -219,7 +221,7 @@ def query_method(exp, ops = {}) "bin3" => ii.to_f / 3, "bin4" => BytesValue.new("blob#{ii}"), "bin5" => ["a", "b", ii], - "bin6" => { "a": "test", "b": ii }, + "bin6" => { a: "test", b: ii } } Support.client.put(key, bins) end @@ -255,7 +257,7 @@ def query_method(exp, ops = {}) ["key must work", 0, Exp.eq(Exp.key(Exp::Type::INT), Exp.int_val(50))], ["key_exists must work", 0, Exp.key_exists], ["nil must work", 100, Exp.eq(Exp.nil_val, Exp.nil_val)], - ["regex_compare must work", 75, Exp.regex_compare("[1-5]", Exp::RegexFlags::ICASE, Exp.str_bin("bin2"))], + ["regex_compare must work", 75, Exp.regex_compare("[1-5]", Exp::RegexFlags::ICASE, Exp.str_bin("bin2"))] ] matrix.each do |title, result, exp| @@ -281,19 +283,19 @@ def query_method(exp, ops = {}) fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(16), - ), + Exp.int_val(16) + ) } - expect { + expect do client.delete(key, opts) - }.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) + end.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) opts = { fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(15), - ), + Exp.int_val(15) + ) } client.delete(key, opts) end @@ -304,19 +306,19 @@ def query_method(exp, ops = {}) fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(15), - ), + Exp.int_val(15) + ) } - expect { + expect do client.put(key, { "bin" => 26 }, opts) - }.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) + end.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) opts = { fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(25), - ), + Exp.int_val(25) + ) } client.put(key, { "bin" => 26 }, opts) end @@ -327,20 +329,20 @@ def query_method(exp, ops = {}) fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(15), - ), + Exp.int_val(15) + ) } - expect { + expect do client.get(key, nil, opts) - }.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) + end.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) opts = { fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(35), - ), + Exp.int_val(35) + ) } client.get(key, ["bin"], opts) end @@ -351,19 +353,19 @@ def query_method(exp, ops = {}) fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(15), - ), + Exp.int_val(15) + ) } - expect { + expect do client.exists(key, opts) - }.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) + end.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) opts = { fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(45), - ), + Exp.int_val(45) + ) } client.exists(key, opts) end @@ -374,19 +376,19 @@ def query_method(exp, ops = {}) fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(15), - ), + Exp.int_val(15) + ) } - expect { + expect do client.add(key, { "test55" => "test" }, opts) - }.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) + end.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) opts = { fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(55), - ), + Exp.int_val(55) + ) } client.add(key, { "test55" => "test" }, opts) end @@ -397,19 +399,19 @@ def query_method(exp, ops = {}) fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(15), - ), + Exp.int_val(15) + ) } - expect { + expect do client.prepend(key, { "test55" => "test" }, opts) - }.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) + end.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) opts = { fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(55), - ), + Exp.int_val(55) + ) } client.prepend(key, { "test55" => "test" }, opts) end @@ -420,19 +422,19 @@ def query_method(exp, ops = {}) fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(15), - ), + Exp.int_val(15) + ) } - expect { + expect do client.touch(key, opts) - }.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) + end.to raise_aerospike_error(Aerospike::ResultCode::FILTERED_OUT) opts = { fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(65), - ), + Exp.int_val(65) + ) } client.touch(key, opts) end @@ -442,8 +444,8 @@ def query_method(exp, ops = {}) fail_on_filtered_out: true, filter_exp: Exp.eq( Exp.int_bin("bin"), - Exp.int_val(75), - ), + Exp.int_val(75) + ) } rs = client.scan_all(@namespace, @set, nil, opts) @@ -498,7 +500,7 @@ def query_method(exp, ops = {}) ), Exp.eq( Exp.int_and(Exp.int_bin(bin_a), Exp.int_val(0xFFFF)), - Exp.int_val(1), + Exp.int_val(1) ) ) ), key_a, key_a, bin_a, 1, true], @@ -510,7 +512,7 @@ def query_method(exp, ops = {}) ), Exp.eq( Exp.int_or(Exp.int_bin(bin_a), Exp.int_val(0xFF)), - Exp.int_val(0xFF), + Exp.int_val(0xFF) ) ) ), key_a, key_a, bin_a, 1, true], @@ -522,7 +524,7 @@ def query_method(exp, ops = {}) ), Exp.eq( Exp.int_xor(Exp.int_bin(bin_a), Exp.int_val(0xFF)), - Exp.int_val(0xFE), + Exp.int_val(0xFE) ) ) ), key_a, key_a, bin_a, 1, true], @@ -596,7 +598,7 @@ def query_method(exp, ops = {}) Exp.def("val", Exp.add(Exp.float_bin(bin_b), Exp.float_val(1.1))), Exp.and( Exp.ge(Exp.var("val"), Exp.float_val(3.2999)), - Exp.le(Exp.var("val"), Exp.float_val(3.3001)), + Exp.le(Exp.var("val"), Exp.float_val(3.3001)) ) ), key_a, key_b, bin_a, 2, false], @@ -613,26 +615,28 @@ def query_method(exp, ops = {}) Exp.ge(Exp.var("val"), Exp.float_val(4.8399)), Exp.le(Exp.var("val"), Exp.float_val(4.8401)) ) - ), key_a, key_b, bin_a, 2, false], + ), key_a, key_b, bin_a, 2, false] ] matrix.each do |title, exp, key, exp_key, bin, expected, reverse_exp| it "#{title} should work" do opts = { fail_on_filtered_out: true, - filter_exp: exp, + filter_exp: exp } - expect { + expect do client.get(key, nil, opts) - }.to raise_error (Aerospike::Exceptions::Aerospike) { |error| + end.to raise_error(Aerospike::Exceptions::Aerospike) { |error| error.result_code == Aerospike::ResultCode::FILTERED_OUT } - opts = { - fail_on_filtered_out: true, - filter_exp: Exp.not(exp), - } if reverse_exp + if reverse_exp + opts = { + fail_on_filtered_out: true, + filter_exp: Exp.not(exp) + } + end r = client.get(exp_key, nil, opts) client.get(key) expect(r.bins[bin]).to eq expected diff --git a/spec/aerospike/pred_exp_spec.rb b/spec/aerospike/pred_exp_spec.rb deleted file mode 100644 index 0849e245..00000000 --- a/spec/aerospike/pred_exp_spec.rb +++ /dev/null @@ -1,536 +0,0 @@ -# frozen_string_literal: true - -describe Aerospike::PredExp, skip: Support.min_version?("6") do - let(:client) { Support.client } - - before :all do - @namespace = "test" - @set = "predexp" - @record_count = 5 - @record_count.times do |i| - key = Aerospike::Key.new(@namespace, @set, i) - bin_map = { - 'bin1' => "value#{i}", - 'bin2' => i, - 'bin3' => [ i, i + 1_000, i + 1_000_000 ], - 'bin4' => { "key#{i}" => i } - } - Support.client.put(key, bin_map, ttl: i * 10) - end - - tasks = [] - tasks << Support.client.create_index(@namespace, @set, "predexp_index_str_bin1", "bin1", :string) - tasks << Support.client.create_index(@namespace, @set, "predexp_index_int_bin2", "bin2", :numeric) - tasks << Support.client.create_index(@namespace, @set, "predexp_index_lst_bin3", "bin3", :numeric, :list) - tasks << Support.client.create_index(@namespace, @set, "predexp_index_mapkey_bin4", "bin4", :string, :mapkeys) - tasks << Support.client.create_index(@namespace, @set, "predexp_index_mapval_bin4", "bin4", :numeric, :mapvalues) - tasks.each(&:wait_till_completed) - expect(tasks.all?(&:completed?)).to be true - end - - let(:statement) { Aerospike::Statement.new(@namespace, @set) } - let(:string_bin) { 'bin1' } - let(:integer_bin) { 'bin2' } - let(:list_bin) { 'bin3' } - let(:map_bin) { 'bin4' } - - context 'void time' do - let(:seconds_from_now) { 30 } - let(:time) { Time.now + seconds_from_now } - let(:value) { Support.time_in_nanoseconds(time) } - let(:predexp) do - [ - Aerospike::PredExp.integer_value(value), - Aerospike::PredExp.void_time, - Aerospike::PredExp.integer_less - ] - end - - it 'returns records expiring later than set time' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - expect(r.ttl).to be > seconds_from_now - count += 1 - end - - expect(count).to be > 0 - end - end - - describe 'expressions for integer bins' do - let(:value) { 3 } - let(:predexp) do - [ - Aerospike::PredExp.integer_bin(integer_bin), - Aerospike::PredExp.integer_value(value) - ] - end - - it 'returns records with bin equal to value' do - predexp << Aerospike::PredExp.integer_equal - statement.predexp = predexp - rs = client.query(statement) - rs.each do |r| - expect(r.bins[integer_bin]).to eq(value) - end - end - - it 'returns records not equal to value' do - predexp << Aerospike::PredExp.integer_unequal - statement.predexp = predexp - rs = client.query(statement) - rs.each do |r| - expect(r.bins[integer_bin]).not_to eq(value) - end - end - - it 'returns records with bin less than value' do - predexp << Aerospike::PredExp.integer_less - statement.predexp = predexp - rs = client.query(statement) - rs.each do |r| - expect(r.bins[integer_bin]).to be < value - end - end - - it 'returns records with bin less or equal than value' do - predexp << Aerospike::PredExp.integer_less_eq - statement.predexp = predexp - rs = client.query(statement) - rs.each do |r| - expect(r.bins[integer_bin]).to be <= value - end - end - - it 'returns records with bin greater than value' do - predexp << Aerospike::PredExp.integer_greater - statement.predexp = predexp - rs = client.query(statement) - rs.each do |r| - expect(r.bins[integer_bin]).to be > value - end - end - - it 'returns records with bin greater or equal to value' do - predexp << Aerospike::PredExp.integer_greater_eq - statement.predexp = predexp - rs = client.query(statement) - rs.each do |r| - expect(r.bins[integer_bin]).to be >= value - end - end - end - - describe 'expressions for string bins' do - let(:value) { 'value3' } - let(:predexp) do - [ - Aerospike::PredExp.string_bin(string_bin), - Aerospike::PredExp.string_value(value) - ] - end - - it 'returns records equal to value' do - predexp << Aerospike::PredExp.string_equal - statement.predexp = predexp - rs = client.query(statement) - rs.each do |r| - expect(r.bins[string_bin]).to eq(value) - end - end - - it 'returns records not equal to value' do - predexp << Aerospike::PredExp.string_unequal - statement.predexp = predexp - rs = client.query(statement) - rs.each do |r| - expect(r.bins[string_bin]).not_to eq(value) - end - end - - context 'regex' do - let(:value) { 'lue3' } - context 'default flag' do - it 'returns records matching regex' do - predexp << Aerospike::PredExp.string_regex(Aerospike::PredExp::RegexFlags::NONE) - statement.predexp = predexp - rs = client.query(statement) - count = 0 - rs.each do |r| - count += 1 - end - - expect(count).to eq(1) - end - end - end - end - - context 'expressions for GeoJSON bins' do - before :all do - @lat = 1.3083 - @lng = 103.9114 - - @point = Aerospike::GeoJSON.new(type: "Point", coordinates: [@lng, @lat]) - @record_count.times do |i| - key = Aerospike::Key.new(@namespace, @set, i) - sub_point = Support::Geo.destination_point(@lng, @lat, i * 200, rand(0..359)) - - sub_point_cords = sub_point.coordinates - - polygon_points = [315, 45, 135, 225].map { |x| - Support::Geo.destination_point(sub_point_cords.first, sub_point_cords.last, 100, x).coordinates - } - polygon_points << polygon_points.first - polygon = Aerospike::GeoJSON.new(type: 'Polygon', coordinates: [polygon_points]) - bin_map = { - 'geo_point' => sub_point, - 'geo_polygon' => polygon - } - Support.client.put(key, bin_map, ttl: 10) - end - - - tasks = [] - tasks << Support.client.create_index(@namespace, @set, "index_geo_bin5", "geo_point", :geo2dsphere) - tasks << Support.client.create_index(@namespace, @set, "index_geo_bin5", "geo_polygon", :geo2dsphere) - tasks.each(&:wait_till_completed) - expect(tasks.all?(&:completed?)).to be true - end - - let(:point_bin) { 'geo_point' } - let(:polygon_bin) { 'geo_polygon' } - - context 'contains' do - let(:geo_json_area_circle) { Aerospike::GeoJSON.new(type: 'AeroCircle', coordinates: [[@lng, @lat], 500]) } - - let(:predexp) do - [ - Aerospike::PredExp.geojson_bin(point_bin), - Aerospike::PredExp.geojson_value(geo_json_area_circle), - Aerospike::PredExp.geojson_contains - ] - end - - it 'returns records with points within a circle' do - statement.predexp = predexp - rs = client.query(statement) - count = 0 - rs.each do |r| - count += 1 - end - expect(count).to eq(3) - end - end - - context 'within' do - let(:predexp) do - [ - Aerospike::PredExp.geojson_bin(polygon_bin), - Aerospike::PredExp.geojson_value(@point), - Aerospike::PredExp.geojson_within - ] - end - - it 'returns records with polygons which contain point' do - statement.predexp = predexp - rs = client.query(statement) - count = 0 - rs.each do |r| - count += 1 - end - expect(count).to eq(1) - end - end - end - - context 'expressions for bins with lists' do - let(:value) { 3 } - - context 'list_or' do - let(:predexp) do - [ - Aerospike::PredExp.integer_value(value), - Aerospike::PredExp.integer_var('x'), - Aerospike::PredExp.integer_equal, - Aerospike::PredExp.list_bin(list_bin), - Aerospike::PredExp.list_iterate_or('x') - ] - end - - it 'returns items which has any item equal to value' do - statement.predexp = predexp - rs = client.query(statement) - count = 0 - rs.each do |r| - expect(r.bins[list_bin]).to include(value) - count += 1 - end - - expect(count).to eq(1) - end - end - - context 'list_and' do - let(:predexp) do - [ - Aerospike::PredExp.integer_value(value), - Aerospike::PredExp.integer_var('x'), - Aerospike::PredExp.integer_unequal, - Aerospike::PredExp.list_bin(list_bin), - Aerospike::PredExp.list_iterate_and('x') - ] - end - - it 'returns items which do NOT contain an item equal to value' do - statement.predexp = predexp - rs = client.query(statement) - count = 0 - rs.each do |r| - expect(r.bins[list_bin]).not_to include(value) - count += 1 - end - - expect(count).to eq(4) - end - end - end - - context 'expressions for bins with mapkeys' do - let(:bin) { 'bin4' } - let(:value) { 3 } - let(:key) { 'key3' } - - context 'keys' do - context 'iterate_or' do - let(:predexp) do - [ - Aerospike::PredExp.string_value(key), - Aerospike::PredExp.string_var('k'), - Aerospike::PredExp.string_equal, - Aerospike::PredExp.map_bin(map_bin), - Aerospike::PredExp.mapkey_iterate_or('k') - ] - end - - it 'returns records with map bins containing chosen key' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - expect(r.bins[map_bin].keys).to include(key) - count += 1 - end - - expect(count).to eq(1) - end - end - - context 'iterate_and' do - let(:predexp) do - [ - Aerospike::PredExp.string_value(key), - Aerospike::PredExp.string_var('k'), - Aerospike::PredExp.string_unequal, - Aerospike::PredExp.map_bin(map_bin), - Aerospike::PredExp.mapkey_iterate_and('k') - ] - end - - it 'returns records with map bins NOT containing chosen key' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - expect(r.bins[map_bin].keys).not_to include(key) - count += 1 - end - - expect(count).to eq(4) - end - end - end - - context 'values' do - context 'iterate_or' do - let(:predexp) do - [ - Aerospike::PredExp.integer_value(value), - Aerospike::PredExp.integer_var('v'), - Aerospike::PredExp.integer_equal, - Aerospike::PredExp.map_bin(map_bin), - Aerospike::PredExp.mapval_iterate_or('v') - ] - end - - it 'returns records with map bins containing chosen value' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - expect(r.bins[map_bin].values).to include(value) - count += 1 - end - - expect(count).to eq(1) - end - end - - context 'iterate_and' do - let(:predexp) do - [ - Aerospike::PredExp.integer_value(value), - Aerospike::PredExp.integer_var('v'), - Aerospike::PredExp.integer_unequal, - Aerospike::PredExp.map_bin(map_bin), - Aerospike::PredExp.mapval_iterate_and('v') - ] - end - - it 'returns records with map bins NOT containing chosen value' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - expect(r.bins[map_bin].values).not_to include(value) - count += 1 - end - - expect(count).to eq(4) - end - end - end - end - - context '.and' do - let(:min_value) { 2 } - - # return records with bin2 > 2 AND bin1 equal to 'value4' - let(:predexp) do - [ - Aerospike::PredExp.integer_bin(integer_bin), - Aerospike::PredExp.integer_value(min_value), - Aerospike::PredExp.integer_greater, - Aerospike::PredExp.string_bin(string_bin), - Aerospike::PredExp.string_value('value4'), - Aerospike::PredExp.string_equal, - Aerospike::PredExp.and(2) - ] - end - - it 'returns records fulfilling multiple predicates' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - bins = r.bins - expect(bins[integer_bin]).to be > min_value - expect(bins[string_bin]).to eq('value4') - count += 1 - end - - expect(count).to eq(1) - end - end - - context '.or' do - let(:max_value) { 2 } - - # return all records with bin2 <=2 OR bin1 equal to 'value4' - let(:predexp) do - [ - Aerospike::PredExp.integer_bin(integer_bin), - Aerospike::PredExp.integer_value(max_value), - Aerospike::PredExp.integer_less_eq, - Aerospike::PredExp.string_bin(string_bin), - Aerospike::PredExp.string_value('value4'), - Aerospike::PredExp.string_equal, - Aerospike::PredExp.or(2) - ] - end - - it 'returns records fulfilling one of the predicates' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - bins = r.bins - if bins[integer_bin] > max_value - expect(bins[string_bin]).to eq('value4') - else - expect(bins[integer_bin]).to be <= max_value - end - - count += 1 - end - - expect(count).to eq(4) - end - end - - context '.not' do - let(:value) { 3 } - # return all records with bin2 not equal 3 - let(:predexp) do - [ - Aerospike::PredExp.integer_bin(integer_bin), - Aerospike::PredExp.integer_value(value), - Aerospike::PredExp.integer_equal, - Aerospike::PredExp.not - ] - end - - it 'returns records NOT fulfilling the predicate' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - expect(r.bins[integer_bin]).not_to eq(value) - count += 1 - end - - expect(count).to eq(4) - end - end - - context 'last update' do - before :all do - @current_time = Support.time_in_nanoseconds(Time.now) - sleep 0.1 - key = Aerospike::Key.new(@namespace, @set, "new_rec") - bin_map = { - 'bin1' => 'val' - } - Support.client.put(key, bin_map, ttl: 10) - end - - let(:predexp) do - [ - Aerospike::PredExp.integer_value(@current_time), - Aerospike::PredExp.last_update, - Aerospike::PredExp.integer_less - ] - end - - it 'returns records updated at chosen time' do - statement.predexp = predexp - rs = client.query(statement) - - count = 0 - rs.each do |r| - count += 1 - end - expect(count).to eq(1) - end - end -end diff --git a/spec/aerospike/predexp_ops_spec.rb b/spec/aerospike/predexp_ops_spec.rb deleted file mode 100644 index 2c40d05c..00000000 --- a/spec/aerospike/predexp_ops_spec.rb +++ /dev/null @@ -1,329 +0,0 @@ -# Copyright 2014-2020 Aerospike, Inc. -# -# Portions may be licensed to Aerospike, Inc. under one or more contributor -# license agreements. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -require 'aerospike' -require 'benchmark' - -describe Aerospike::Client do - - describe "Predicates", skip: Support.min_version?("6") do - - let(:key) { Aerospike::Key.new(Support.namespace, 'predexp_ops_spec', 0) } - let(:client) { Support.client } - let(:valid_predicate) { - [ - Aerospike::PredExp.integer_bin('bin2'), - Aerospike::PredExp.integer_value(9), - Aerospike::PredExp.integer_less_eq - ] - } - - let(:invalid_predicate) { - [ - Aerospike::PredExp.string_bin('bin1'), - Aerospike::PredExp.string_value('value'), - Aerospike::PredExp.string_unequal - ] - } - - before :each do - client.delete(key) - client.put(key, {'bin1' => 'value', 'bin2' => 9}) - end - - describe "#put" do - - it "should put the key if the predicate is valid" do - client.put(key, {'bin3' => 1.1}, predexp: valid_predicate) - rec = client.get(key) - expect(rec.bins['bin1']).to eq 'value' - expect(rec.bins['bin2']).to eq 9 - expect(rec.bins['bin3']).to eq 1.1 - end - - it "should NOT put the key if the predicate is invalid" do - client.put(key, {'bin3' => 1.1}, predexp: invalid_predicate) - rec = client.get(key) - expect(rec.bins['bin1']).to eq 'value' - expect(rec.bins['bin2']).to eq 9 - expect(rec.bins['bin3']).to be_nil - end - - it "should raise exception if the predicate is invalid" do - expect { - client.put(key, {'bin3' => 1.1}, predexp: invalid_predicate, fail_on_filtered_out: true) - }.to raise_error (Aerospike::Exceptions::Aerospike){ |error| - error.result_code == Aerospike::ResultCode::FILTERED_OUT - } - end - - end - - describe "#get" do - - it "should get the key if the predicate is valid" do - rec = client.get(key, [], predexp: valid_predicate) - expect(rec.bins['bin1']).to eq 'value' - expect(rec.bins['bin2']).to eq 9 - end - - it "should NOT get the key if the predicate is invalid" do - rec = client.get(key, [], predexp: invalid_predicate) - expect(rec).to be_nil - end - - it "should raise exception if the predicate is invalid" do - expect { - client.get(key, [], predexp: invalid_predicate, fail_on_filtered_out: true) - }.to raise_error (Aerospike::Exceptions::Aerospike){ |error| - error.result_code == Aerospike::ResultCode::FILTERED_OUT - } - end - - end - - describe "#get_header" do - - it "should get the key if the predicate is valid" do - rec = client.get_header(key, predexp: valid_predicate) - expect(rec).not_to be_nil - end - - it "should NOT get the key if the predicate is invalid" do - rec = client.get_header(key, predexp: invalid_predicate) - expect(rec).to be_nil - end - - it "should raise exception if the predicate is invalid" do - expect { - client.get_header(key, predexp: invalid_predicate, fail_on_filtered_out: true) - }.to raise_error (Aerospike::Exceptions::Aerospike){ |error| - error.result_code == Aerospike::ResultCode::FILTERED_OUT - } - end - - end - - describe "#delete" do - - it "should delete a key if the predicate is valid" do - existed = client.delete(key, predexp: valid_predicate) - expect(existed).to eq true - end - - it "should NOT delete a key if the predicate is invalid" do - existed = client.delete(key, predexp: invalid_predicate) - expect(existed).to eq true - rec = client.get(key, []) - expect(rec.bins['bin1']).to eq 'value' - expect(rec.bins['bin2']).to eq 9 - end - - it "should raise exception if the predicate is invalid" do - expect { - client.delete(key, predexp: invalid_predicate, fail_on_filtered_out: true) - }.to raise_error (Aerospike::Exceptions::Aerospike){ |error| - error.result_code == Aerospike::ResultCode::FILTERED_OUT - } - end - - end - - describe "#touch" do - - it "should touch the record to bump its generation if the predicate is valid" do - client.touch(key, predexp: valid_predicate) - record = client.get_header(key) - expect(record.generation).to eq 2 - end - - it "should NOT touch the record to bump its generation if the predicate is invalid" do - client.touch(key, predexp: invalid_predicate) - record = client.get_header(key) - expect(record.generation).to eq 1 - end - - it "should raise exception if the predicate is invalid" do - expect { - client.touch(key, predexp: invalid_predicate, fail_on_filtered_out: true) - }.to raise_error (Aerospike::Exceptions::Aerospike){ |error| - error.result_code == Aerospike::ResultCode::FILTERED_OUT - } - end - - end - - describe "#exists" do - - it "should check existence of the record if the predicate is valid" do - existed = client.exists(key, predexp: valid_predicate) - expect(existed).to eq true - end - - it "should NOT check existence of the record if the predicate is invalid" do - existed = client.exists(key, predexp: invalid_predicate) - expect(existed).to eq true - end - - it "should raise exception if the predicate is invalid" do - expect { - client.exists(key, predexp: invalid_predicate, fail_on_filtered_out: true) - }.to raise_error (Aerospike::Exceptions::Aerospike){ |error| - error.result_code == Aerospike::ResultCode::FILTERED_OUT - } - end - - end - - describe "#operate" do - - let(:bin_int) do - Aerospike::Bin.new('bin2', 5) - end - - it "should #add, #get if the predicate is valid" do - client.operate(key, [ - Aerospike::Operation.add(bin_int), - ], predexp: valid_predicate) - rec = client.get(key) - expect(rec.bins[bin_int.name]).to eq bin_int.value + 9 - expect(rec.generation).to eq 2 - end - - it "should NOT #add, #get if the predicate is invalid" do - client.operate(key, [ - Aerospike::Operation.add(bin_int), - ], predexp: invalid_predicate) - rec = client.get(key) - expect(rec.bins[bin_int.name]).to eq 9 - expect(rec.generation).to eq 1 - end - - it "should raise exception if the predicate is invalid" do - expect { - client.operate(key, [ - Aerospike::Operation.add(bin_int), - ], predexp: invalid_predicate, fail_on_filtered_out: true) - }.to raise_error (Aerospike::Exceptions::Aerospike){ |error| - error.result_code == Aerospike::ResultCode::FILTERED_OUT - } - end - - end - - describe "#batch" do - - it "should batch_get if the predicate is valid" do - result = client.batch_get([key], [], predexp: valid_predicate) - expect(result[0].bins['bin1']).to eq 'value' - end - - it "should NOT batch_get if the predicate is invalid" do - result = client.batch_get([key], [], predexp: invalid_predicate) - expect(result[0]).to be_nil - end - - end - - describe "#scan" do - - it "should scan and return records if the predicate is valid" do - rs = client.scan_all(key.namespace, key.set_name, nil, predexp: valid_predicate) - count = 0 - rs.each do |rs| - count += 1 - end - - expect(count).to eq 1 - end - - it "should NOT scan and return records if the predicate is invalid" do - rs = client.scan_all(key.namespace, key.set_name, nil, predexp: invalid_predicate) - count = 0 - rs.each do |rs| - count += 1 - end - - expect(count).to eq 0 - end - - end - - describe "#query" do - - let(:stmt) { stmt = Aerospike::Statement.new(key.namespace, key.set_name) } - - it "should query and return records if the predicate is valid" do - stmt.predexp = valid_predicate - rs = client.query(stmt) - count = 0 - rs.each do |rs| - count += 1 - end - - expect(count).to eq 1 - end - - it "should query and return records if the predicate is invalid - predexp on policy" do - rs = client.query(stmt, predexp: valid_predicate) - count = 0 - rs.each do |rs| - count += 1 - end - - expect(count).to eq 1 - end - - it "should query and return records if the predicate is valid - predexp on policy" do - # policy value takes precedence - stmt.predexp = invalid_predicate - rs = client.query(stmt, predexp: valid_predicate) - count = 0 - rs.each do |rs| - count += 1 - end - - expect(count).to eq 1 - end - - it "should NOT query and return records if the predicate is valid - predexp on policy" do - # policy value takes precedence - stmt.predexp = invalid_predicate - rs = client.query(stmt) - count = 0 - rs.each do |rs| - count += 1 - end - - expect(count).to eq 0 - end - - it "should NOT query and return records if the predicate is valid - predexp on policy" do - rs = client.query(stmt, predexp: invalid_predicate) - count = 0 - rs.each do |rs| - count += 1 - end - - expect(count).to eq 0 - end - - end - - end - -end diff --git a/spec/aerospike/udf_spec.rb b/spec/aerospike/udf_spec.rb index 41e3c16c..ca6dfff4 100644 --- a/spec/aerospike/udf_spec.rb +++ b/spec/aerospike/udf_spec.rb @@ -151,8 +151,8 @@ number_of_records = 100 number_of_records.times do |i| - key = Support.gen_random_key(50, {:set => set}) - bin1 = Aerospike::Bin.new('bin1', i * div) + key = Support.gen_random_key(50, { :set => set }) + bin1 = Aerospike::Bin.new('bin1', (i + 1) * div) bin2 = Aerospike::Bin.new('bin2', -1) client.put(key, [bin1, bin2]) end @@ -170,7 +170,7 @@ recordset = client.scan_all(ns, set) cnt = 0 recordset.each do |rec| - expect(rec.bins['bin2']).to eq (rec.bins['bin1'] / div) + expect(rec.bins['bin2']).to eq(rec.bins['bin1'] / div) cnt += 1 end expect(cnt).to eq number_of_records @@ -184,7 +184,7 @@ number_of_records = 100 number_of_records.times do |i| - key = Support.gen_random_key(50, {:set => set}) + key = Support.gen_random_key(50, { :set => set }) bin1 = Aerospike::Bin.new('bin1', i * div) bin2 = Aerospike::Bin.new('bin2', -1) client.put(key, [bin1, bin2]) @@ -212,7 +212,7 @@ cnt = 0 recordset.each do |rec| if rec.bins['bin1'] <= number_of_records / 2 - expect(rec.bins['bin2']).to eq (rec.bins['bin1'] / div) + expect(rec.bins['bin2']).to eq(rec.bins['bin1'] / div) else expect(rec.bins['bin2']).to eq(-1) end diff --git a/spec/support/utils.rb b/spec/support/utils.rb index 9a975f15..7366e2a0 100644 --- a/spec/support/utils.rb +++ b/spec/support/utils.rb @@ -20,10 +20,10 @@ module Support RAND_CHARS = ('a'..'z').to_a.concat(('A'..'Z').to_a).concat(('0'..'9').to_a) - VERSION_REGEX = /\d+(?:.\d+)+(:?-\d+)?(?:-[a-z0-9]{8})?/.freeze + VERSION_REGEX = /\d+(?:.\d+)+(:?-\d+)?(?:-[a-z0-9]{8})?/ def self.rand_string(len) - RAND_CHARS.shuffle[0,len].join + RAND_CHARS.shuffle[0, len].join end def self.namespace @@ -37,7 +37,7 @@ def self.set_name def self.gen_random_key(len=50, opts = {}) key_val = opts[:key_val] || rand_string(len) set_name = opts[:set] || self.set_name - ns_name = opts[:ns] || self.namespace + ns_name = opts[:ns] || namespace Aerospike::Key.new(ns_name, set_name, key_val) end @@ -47,52 +47,69 @@ def self.delete_set(client, namespace, set_name) end package = "test_utils_delete_record.lua" - function = < Date: Wed, 24 Jul 2024 19:26:18 +0200 Subject: [PATCH 02/13] [CLIENT-3056] Fix Github Actions Workflow --- .github/workflows/development.yml | 50 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 6b842918..60acc9df 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -6,40 +6,38 @@ jobs: test: runs-on: ${{matrix.os}}-latest continue-on-error: ${{matrix.experimental}} - + strategy: matrix: os: - - ubuntu - + - ubuntu-latest + - macos-latest + ruby: - - 2.6 - - 2.7 - + - "2.6" + - "2.7" + - "3.0" + - "3.1" + - "3.2" + - "3.3" + experimental: [false] env: [""] - + include: - os: ubuntu ruby: head experimental: true - steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{matrix.ruby}} - bundler-cache: true - - - name: Start server - timeout-minutes: 5 - env: - TERM: dumb - run: - .github/workflows/start_cluster.sh 2 - - - name: Run tests - timeout-minutes: 30 - env: - AEROSPIKE_HOSTS: "127.0.0.1:3000,127.0.0.1:3100" - run: ${{matrix.env}} bundle exec rspec + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + bundler-cache: true + - name: Set up Aerospike Database + uses: reugn/github-action-aerospike@v1 + - name: Run tests + timeout-minutes: 30 + env: + AEROSPIKE_HOSTS: "127.0.0.1:3000" + run: ${{matrix.env}} bundle exec rspec From fd936d53330d686bf20e50823f55c956e287eead Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Fri, 9 Aug 2024 17:31:40 +0200 Subject: [PATCH 03/13] [CLIENT_1731] Support Batch Operations --- lib/aerospike.rb | 13 + lib/aerospike/batch_attr.rb | 292 ++++++++++++++++++ lib/aerospike/batch_delete.rb | 48 +++ lib/aerospike/batch_read.rb | 97 ++++++ lib/aerospike/batch_record.rb | 83 +++++ lib/aerospike/batch_results.rb | 38 +++ lib/aerospike/batch_udf.rb | 76 +++++ lib/aerospike/batch_write.rb | 79 +++++ lib/aerospike/client.rb | 33 ++ lib/aerospike/cluster.rb | 96 +++--- lib/aerospike/command/batch_index_node.rb | 7 +- .../command/batch_operate_command.rb | 151 +++++++++ lib/aerospike/command/batch_operate_node.rb | 51 +++ lib/aerospike/command/command.rb | 93 +++++- lib/aerospike/node.rb | 10 +- lib/aerospike/operation.rb | 37 +++ lib/aerospike/policy/batch_delete_policy.rb | 71 +++++ lib/aerospike/policy/batch_policy.rb | 54 +++- lib/aerospike/policy/batch_read_policy.rb | 38 +++ lib/aerospike/policy/batch_udf_policy.rb | 75 +++++ lib/aerospike/policy/batch_write_policy.rb | 105 +++++++ lib/aerospike/utils/buffer.rb | 30 +- spec/aerospike/batch_operate_spec.rb | 185 +++++++++++ 23 files changed, 1689 insertions(+), 73 deletions(-) create mode 100644 lib/aerospike/batch_attr.rb create mode 100644 lib/aerospike/batch_delete.rb create mode 100644 lib/aerospike/batch_read.rb create mode 100644 lib/aerospike/batch_record.rb create mode 100644 lib/aerospike/batch_results.rb create mode 100644 lib/aerospike/batch_udf.rb create mode 100644 lib/aerospike/batch_write.rb create mode 100644 lib/aerospike/command/batch_operate_command.rb create mode 100644 lib/aerospike/command/batch_operate_node.rb create mode 100644 lib/aerospike/policy/batch_delete_policy.rb create mode 100644 lib/aerospike/policy/batch_read_policy.rb create mode 100644 lib/aerospike/policy/batch_udf_policy.rb create mode 100644 lib/aerospike/policy/batch_write_policy.rb create mode 100644 spec/aerospike/batch_operate_spec.rb diff --git a/lib/aerospike.rb b/lib/aerospike.rb index b1323ce4..b6cc426c 100644 --- a/lib/aerospike.rb +++ b/lib/aerospike.rb @@ -44,6 +44,7 @@ require "aerospike/value/value" require "aerospike/command/single_command" require "aerospike/command/batch_index_node" +require "aerospike/command/batch_operate_node" require "aerospike/command/field_type" require "aerospike/command/command" require "aerospike/command/execute_command" @@ -53,6 +54,7 @@ require "aerospike/command/exists_command" require "aerospike/command/multi_command" require "aerospike/command/batch_index_command" +require "aerospike/command/batch_operate_command" require "aerospike/command/batch_index_exists_command" require "aerospike/command/read_header_command" require "aerospike/command/touch_command" @@ -94,6 +96,10 @@ require "aerospike/policy/generation_policy" require "aerospike/policy/policy" require "aerospike/policy/batch_policy" +require "aerospike/policy/batch_delete_policy" +require "aerospike/policy/batch_read_policy" +require "aerospike/policy/batch_udf_policy" +require "aerospike/policy/batch_write_policy" require "aerospike/policy/write_policy" require "aerospike/policy/scan_policy" require "aerospike/policy/query_policy" @@ -102,6 +108,13 @@ require "aerospike/policy/admin_policy" require "aerospike/policy/auth_mode" +require "aerospike/batch_record" +require "aerospike/batch_attr" +require "aerospike/batch_read" +require "aerospike/batch_write" +require "aerospike/batch_delete" +require "aerospike/batch_udf" + require "aerospike/socket/base" require "aerospike/socket/ssl" require "aerospike/socket/tcp" diff --git a/lib/aerospike/batch_attr.rb b/lib/aerospike/batch_attr.rb new file mode 100644 index 00000000..c6c8f449 --- /dev/null +++ b/lib/aerospike/batch_attr.rb @@ -0,0 +1,292 @@ +# frozen_string_literal: true + +# Copyright 2014-2020 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +module Aerospike + + class BatchAttr + + attr_reader :filter_exp, :read_attr, :write_attr, :info_attr, :expiration, :generation, :has_write, :send_key + + def initialize(ops = nil, opt = {}) + rp = create_policy(opt, BatchPolicy, nil) + wp = create_policy(opt, BatchWritePolicy, nil) + + read_all_bins = false + read_header = false + has_read = false + has_write_op = false + + ops&.each do |op| + case op.op_type + when Operation::BIT_READ, Operation::EXP_READ, Operation::HLL_READ, Operation::CDT_READ, Operation::READ # Read all bins if no bin is specified. + read_all_bins = op.bin_name.nil? + has_read = true + + when Operation::READ_HEADER + read_header = true + has_read = true + + else + has_write_op = true + end + end + + if has_write_op + set_batch_write(wp) + + if has_read + @read_attr |= Aerospike::INFO1_READ + + if read_all_bins + @read_attr |= Aerospike::INFO1_GET_ALL + elsif read_header + @read_attr |= Aerospike::INFO1_NOBINDATA + end + end + else + set_batch_read(rp) + + if read_all_bins + @read_attr |= Aerospike::INFO1_GET_ALL + elsif read_header + @read_attr |= Aerospike::INFO1_NOBINDATA + end + end + end + + def set_read(rp) + @filter_exp = nil + @read_attr = Aerospike::INFO1_READ + + @write_attr = 0 + @info_attr = 0 + + @expiration = 0 + @generation = 0 + @has_write = false + @send_key = false + end + + def set_batch_read(rp) + @filter_exp = rp.filter_exp + @read_attr = Aerospike::INFO1_READ + + @write_attr = 0 + @info_attr = 0 + + @expiration = 0 + @generation = 0 + @has_write = false + @send_key = false + end + + def adjust_read(ops) + read_all_bins = false + read_header = false + + ops.each do |op| + case op.op_type + when Operation::BIT_READ, Operation::EXP_READ, Operation::HLL_READ, Operation::CDT_READ, Operation::READ # Read all bins if no bin is specified. + read_all_bins = op.bin_name.nil? + when Operation::READ_HEADER + read_header = true + end + end + + if read_all_bins + @read_attr |= Aerospike::INFO1_GET_ALL + elsif read_header + @read_attr |= Aerospike::INFO1_NOBINDATA + end + end + + def adjust_read_all_bins(read_all_bins) + @read_attr |= read_all_bins ? Aerospike::INFO1_GET_ALL : Aerospike::INFO1_NOBINDATA + end + + def set_write(wp) + @filter_exp = nil + @read_attr = 0 + @write_attr = Aerospike::INFO2_WRITE | Aerospike::INFO2_RESPOND_ALL_OPS + @info_attr = 0 + @expiration = 0 + @generation = 0 + @has_write = true + @send_key = wp.send_key + end + + def set_batch_write(wp) + @filter_exp = wp.filter_exp + @read_attr = 0 + @write_attr = Aerospike::INFO2_WRITE | Aerospike::INFO2_RESPOND_ALL_OPS + @info_attr = 0 + @expiration = wp.expiration + @has_write = true + @send_key = wp.send_key + + case wp.generation_policy + when GenerationPolicy::NONE + @generation = 0 + when GenerationPolicy::EXPECT_GEN_EQUAL + @generation = wp.generation + @write_attr |= Aerospike::INFO2_GENERATION + when GenerationPolicy::EXPECT_GEN_GT + @generation = wp.generation + @write_attr |= Aerospike::INFO2_GENERATION_GT + else + @generation = 0 + end + + case wp.record_exists_action + when RecordExistsAction::UPDATE + # NOOP + when RecordExistsAction::UPDATE_ONLY + @info_attr |= Aerospike::INFO3_UPDATE_ONLY + when RecordExistsAction::REPLACE + @info_attr |= Aerospike::INFO3_CREATE_OR_REPLACE + when RecordExistsAction::REPLACE_ONLY + @info_attr |= Aerospike::INFO3_REPLACE_ONLY + when RecordExistsAction::CREATE_ONLY + @write_attr |= Aerospike::INFO2_CREATE_ONLY + end + + if wp.durable_delete + @write_attr |= Aerospike::INFO2_DURABLE_DELETE + end + + if wp.commit_level == CommitLevel::COMMIT_MASTER + @info_attr |= Aerospike::INFO3_COMMIT_MASTER + end + end + + def adjust_write(ops) + read_all_bins = false + read_header = false + has_read = false + + ops.each do |op| + case op.op_type + when Operation::BIT_READ, Operation::EXP_READ, Operation::HLL_READ, Operation::CDT_READ, Operation::READ # Read all bins if no bin is specified. + read_all_bins = op.bin_name.nil? + has_read = true + + when Operation::READ_HEADER + read_header = true + has_read = true + + end + end + + if has_read + @read_attr |= Aerospike::INFO1_READ + + if read_all_bins + @read_attr |= Aerospike::INFO1_GET_ALL + elsif read_header + @read_attr |= Aerospike::INFO1_NOBINDATA + end + end + end + + def set_udf(up) + @filter_exp = nil + @read_attr = 0 + @write_attr = Aerospike::INFO2_WRITE + @info_attr = 0 + @expiration = 0 + @generation = 0 + @has_write = true + @send_key = up.send_key + end + + def set_batch_udf(up) + @filter_exp = up.filter_exp + @read_attr = 0 + @write_attr = Aerospike::INFO2_WRITE + @info_attr = 0 + @expiration = up.expiration + @generation = 0 + @has_write = true + @send_key = up.send_key + + if up.durable_delete + @write_attr |= Aerospike::INFO2_DURABLE_DELETE + end + + if up.commit_level == CommitLevel::COMMIT_MASTER + @info_attr |= Aerospike::INFO3_COMMIT_MASTER + end + end + + def set_delete(dp) + @filter_exp = nil + @read_attr = 0 + @write_attr = Aerospike::INFO2_WRITE | Aerospike::INFO2_RESPOND_ALL_OPS | Aerospike::INFO2_DELETE + @info_attr = 0 + @expiration = 0 + @generation = 0 + @has_write = true + @send_key = dp.send_key + end + + def set_batch_delete(dp) + @filter_exp = dp.filter_exp + @read_attr = 0 + @write_attr = Aerospike::INFO2_WRITE | Aerospike::INFO2_RESPOND_ALL_OPS | Aerospike::INFO2_DELETE + @info_attr = 0 + @expiration = 0 + @has_write = true + @send_key = dp.send_key + + case dp.generation_policy + when GenerationPolicy::NONE + @generation = 0 + when GenerationPolicy::EXPECT_GEN_EQUAL + @generation = dp.generation + @write_attr |= Aerospike::INFO2_GENERATION + when GenerationPolicy::EXPECT_GEN_GT + @generation = dp.generation + @write_attr |= Aerospike::INFO2_GENERATION_GT + else + @generation = 0 + end + + if dp.durable_delete + @write_attr |= Aerospike::INFO2_DURABLE_DELETE + end + + if dp.commit_level == CommitLevel::COMMIT_MASTER + @info_attr |= Aerospike::INFO3_COMMIT_MASTER + end + end + + private + + def create_policy(policy, policy_klass, default_policy = nil) + case policy + when nil + default_policy || policy_klass.new + when policy_klass + policy + when Hash + policy_klass.new(policy) + else + raise TypeError, "policy should be a #{policy_klass.name} instance or a Hash" + end + end + end +end diff --git a/lib/aerospike/batch_delete.rb b/lib/aerospike/batch_delete.rb new file mode 100644 index 00000000..f67f1824 --- /dev/null +++ b/lib/aerospike/batch_delete.rb @@ -0,0 +1,48 @@ +# encoding: utf-8 +# Copyright 2014-2024 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License") you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +module Aerospike + + # Batch delete operation. + class BatchDelete < BatchRecord + # Optional delete policy. + attr_accessor :policy + + # Initialize policy and key. + def initialize(key, opt = {}) + super(key, has_write: true) + @policy = BatchRecord.create_policy(opt, BatchDeletePolicy, DEFAULT_BATCH_DELETE_POLICY) + end + + def ==(other) + other && other.instance_of?(self.class) && @policy == other.policy + end + + DEFAULT_BATCH_DELETE_POLICY = BatchDeletePolicy.new + + # Return wire protocol size. For internal use only. + def size + size = 6 # gen(2) + exp(4) = 6 + + size += @policy&.filter_exp&.size if @policy&.filter_exp + if @policy&.send_key + size += @key.user_key.estimate_size + Aerospike::FIELD_HEADER_SIZE + 1 + end + + size + end + end +end \ No newline at end of file diff --git a/lib/aerospike/batch_read.rb b/lib/aerospike/batch_read.rb new file mode 100644 index 00000000..9a62cb7a --- /dev/null +++ b/lib/aerospike/batch_read.rb @@ -0,0 +1,97 @@ +# encoding: utf-8 +# Copyright 2014-2024 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License") you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# Batch key and read only operations with default policy. +# Used in batch read commands where different bins are needed for each key. + +module Aerospike + + class BatchRead < BatchRecord + + # Optional read policy. + attr_accessor :policy + + # Bins to retrieve for this key. bin_names are mutually exclusive with + # {@link com.aerospike.client.BatchRead#ops}. + attr_accessor :bin_names + + # Optional operations for this key. ops are mutually exclusive with + # {@link com.aerospike.client.BatchRead#bin_names}. A bin_name can be emulated with + # {@link com.aerospike.client.Operation#get(String)} + attr_accessor :ops + + # If true, ignore bin_names and read all bins. + # If false and bin_names are set, read specified bin_names. + # If false and bin_names are not set, read record header (generation, expiration) only. + attr_accessor :read_all_bins + + # Initialize batch key and bins to retrieve. + def self.read_bins(key, bin_names, opt = {}) + br = BatchRead.new(key) + br.policy = BatchRecord.create_policy(opt, BatchReadPolicy, DEFAULT_BATCH_READ_POLICY) + br.bin_names = bin_names + br.read_all_bins = false + br + end + + # Initialize batch key and read_all_bins indicator. + def self.read_all_bins(key, opt = {}) + br = BatchRead.new(key) + br.policy = create_policy(opt, BatchReadPolicy, DEFAULT_BATCH_READ_POLICY) + br.read_all_bins = true + br + end + + # Initialize batch key and read operations. + def self.ops(key, ops, opt = {}) + br = BatchRead.new(key) + br.policy = create_policy(opt, BatchReadPolicy, DEFAULT_BATCH_READ_POLICY) + br.ops = ops + br.read_all_bins = false + br + end + + # Optimized reference equality check to determine batch wire protocol repeat flag. + # For internal use only. + def ==(other) + other && other.instance_of?(self.class) && + @bin_names.sort == other.bin_names.sort && @ops.sort == other.ops.sort && + @policy == other.policy && @read_all_bins == other.read_all_bins + end + + DEFAULT_BATCH_READ_POLICY = BatchReadPolicy.new + + # Return wire protocol size. For internal use only. + def size + size = 0 + size += @policy&.filter_exp&.size if @policy&.filter_exp + + @bin_names&.each do |bin_name| + size += bin_name.bytesize + Aerospike::OPERATION_HEADER_SIZE + end + + @ops&.each do |op| + if op.is_write? + raise AerospikeException.new(ResultCode::PARAMETER_ERROR, "Write operations not allowed in batch read") + end + size += op.bin_name.bytesize + Aerospike::OPERATION_HEADER_SIZE + size += op.value.estimate_size + end + + size + end + end +end diff --git a/lib/aerospike/batch_record.rb b/lib/aerospike/batch_record.rb new file mode 100644 index 00000000..898aa7ad --- /dev/null +++ b/lib/aerospike/batch_record.rb @@ -0,0 +1,83 @@ +# encoding: utf-8 +# Copyright 2014-2024 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License") you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +module Aerospike + + # Batch key and record result. + class BatchRecord + # Key. + attr_reader :key + + # Record result after batch command has completed. Will be null if record was not found + # or an error occurred. See {@link BatchRecord#result_code}. + attr_reader :record + + # Result code for this returned record. See {@link com.aerospike.client.ResultCode}. + # If not {@link com.aerospike.client.ResultCode#OK}, the record will be null. + attr_accessor :result_code + + # Is it possible that the write transaction may have completed even though an error + # occurred for this record. This may be the case when a client error occurs (like timeout) + # after the command was sent to the server. + attr_accessor :in_doubt + + # Does this command contain a write operation. For internal use only. + attr_reader :has_write + + # Constructor. + def initialize(key, result_code: ResultCode::NO_RESPONSE, in_doubt: false, has_write: false) + @key = key + @record = record + @result_code = result_code + @in_doubt = in_doubt + @has_write = has_write + end + + def self.create_policy(policy, policy_klass, default_policy = nil) + case policy + when nil + default_policy || policy_klass.new + when policy_klass + policy + when Hash + policy_klass.new(policy) + else + raise TypeError, "policy should be a #{policy_klass.name} instance or a Hash" + end + end + + # Prepare for upcoming batch call. Reset result fields because this instance might be + # reused. For internal use only. + def prepare + @record = nil + @result_code = ResultCode::NO_RESPONSE + @in_doubt = false + end + + # Set record result. For internal use only. + def record=(record) + @record = record + @result_code = ResultCode::OK + end + + # Set error result. For internal use only. + def set_error(result_code, in_doubt) + @result_code = result_code + @in_doubt = in_doubt + end + + end +end diff --git a/lib/aerospike/batch_results.rb b/lib/aerospike/batch_results.rb new file mode 100644 index 00000000..ae71041d --- /dev/null +++ b/lib/aerospike/batch_results.rb @@ -0,0 +1,38 @@ +# encoding: utf-8 +# Copyright 2014-2024 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License") you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# Batch key and read only operations with default policy. +# Used in batch read commands where different bins are needed for each key. + +module Aerospike + + # Batch record results. + class BatchResults + + # Record results. + attr_accessor :records + + # Indicates if all records returned success. + attr_accessor :status + + # Constructor. + def intialize(records, status) + @records = records + @status = status + end + + end +end diff --git a/lib/aerospike/batch_udf.rb b/lib/aerospike/batch_udf.rb new file mode 100644 index 00000000..22bdddd0 --- /dev/null +++ b/lib/aerospike/batch_udf.rb @@ -0,0 +1,76 @@ +# encoding: utf-8 +# Copyright 2014-2024 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License") you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# Batch key and read only operations with default policy. +# Used in batch read commands where different bins are needed for each key. + +module Aerospike + + # Batch user defined functions. + class BatchUDF < BatchRecord + + # Optional UDF policy. + attr_accessor :policy + + # Package or lua module name. + attr_accessor :package_name + + # Lua function name. + attr_accessor :function_name + + # Optional arguments to lua function. + attr_accessor :function_args + + # Wire protocol bytes for function args. For internal use only. + attr_reader :arg_bytes + + # Constructor using default policy. + def initialize(key, package_name, function_name, function_args, opt = {}) + super(key, has_write: true) + @policy = BatchRecord.create_policy(opt, BatchUDFPolicy, DEFAULT_BATCH_UDF_POLICY) + @package_name = package_name + @function_name = function_name + @function_args = ListValue.new(function_args) + # Do not set arg_bytes here because may not be necessary if batch repeat flag is used. + end + + # Optimized reference equality check to determine batch wire protocol repeat flag. + # For internal use only. + def equals(other) + other && other.instance_of?(self.class) && + @function_name == other.function_name && @function_args == other.function_args && + @package_name == other.package_name && @policy == other.policy + end + + DEFAULT_BATCH_UDF_POLICY = BatchUDFPolicy.new + + # Return wire protocol size. For internal use only. + def size + size = 6 # gen(2) + exp(4) = 6 + + size += @policy&.filter_exp&.size if @policy&.filter_exp + + if @policy&.send_key + size += @key.user_key.estimate_size + Aerospike::FIELD_HEADER_SIZE + 1 + end + size += @package_name.bytesize + Aerospike::FIELD_HEADER_SIZE + size += @function_name.bytesize + Aerospike::FIELD_HEADER_SIZE + @arg_bytes = @function_args.to_bytes + size += @arg_bytes.bytesize + Aerospike::FIELD_HEADER_SIZE + size + end + end +end \ No newline at end of file diff --git a/lib/aerospike/batch_write.rb b/lib/aerospike/batch_write.rb new file mode 100644 index 00000000..74b5520b --- /dev/null +++ b/lib/aerospike/batch_write.rb @@ -0,0 +1,79 @@ +# encoding: utf-8 +# Copyright 2014-2024 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License") you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# Batch key and read only operations with default policy. +# Used in batch read commands where different bins are needed for each key. + +module Aerospike + + private + + # Batch key and read/write operations with write policy. + class BatchWrite < BatchRecord + # Optional write policy. + attr_accessor :policy + + # Required operations for this key. + attr_accessor :ops + + # Initialize batch key and read/write operations. + #

+ # {@link Operation#get()} is not allowed because it returns a variable number of bins and + # makes it difficult (sometimes impossible) to lineup operations with results. Instead, + # use {@link Operation#get(String)} for each bin name. + def initialize(key, ops, opt = {}) + super(key, has_write: true) + @policy = BatchRecord.create_policy(opt, BatchWritePolicy, DEFAULT_BATCH_WRITE_POLICY) + @ops = ops + end + + # Optimized reference equality check to determine batch wire protocol repeat flag. + # For internal use only. + def ==(other) + other && other.instance_of?(self.class) && + @ops == other.ops && @policy == other.policy && (@policy.nil? || !@policy.send_key) + end + + DEFAULT_BATCH_WRITE_POLICY = BatchWritePolicy.new + + # Return wire protocol size. For internal use only. + def size + size = 6 # gen(2) + exp(4) = 6 + + size += @policy&.filter_exp&.size if @policy&.filter_exp + + if @policy&.send_key + size += @key.user_key.estimate_size + Aerospike::FIELD_HEADER_SIZE + 1 + end + + has_write = false + @ops&.each do |op| + if op.is_write? + has_write = true + end + + size += op.bin_name.bytesize + Aerospike::OPERATION_HEADER_SIZE if op.bin_name + size += op.bin_value.estimate_size if op.bin_value + end + + unless has_write + raise AerospikeException.new(ResultCode::PARAMETER_ERROR, "Batch write operations do not contain a write") + end + + size + end + end +end \ No newline at end of file diff --git a/lib/aerospike/client.rb b/lib/aerospike/client.rb index d3a8fa1c..b1a58c8a 100644 --- a/lib/aerospike/client.rb +++ b/lib/aerospike/client.rb @@ -336,6 +336,21 @@ def batch_get_header(keys, options = nil) batch_get(keys, :none, options) end + # Operate on multiple records for specified batch keys in one batch call. + # This method allows different namespaces/bins for each key in the batch. + # The returned records are located in the same list. + # + # records can be BatchRead, BatchWrite, BatchDelete or BatchUDF. + # + # Requires server version 6.0+ + def batch_operate(records, options = nil) + policy = create_policy(options, BatchPolicy, default_batch_policy) + + execute_batch_operate_commands(policy, records) do |node, batch| + BatchOperateCommand.new(node, batch, policy, records) + end + end + # Check if multiple record keys exist in one batch call. # The returned boolean array is in positional order with the original key array order. # The policy can be used to specify timeouts and protocol type. @@ -975,5 +990,23 @@ def execute_batch_index_commands(policy, keys) threads.each(&:join) end + + def execute_batch_operate_commands(policy, records) + if @cluster.nodes.empty? + raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Executing Batch Index command failed because cluster is empty.") + end + + batch_nodes = BatchOperateNode.generate_list(@cluster, policy.replica, records) + threads = [] + + batch_nodes.each do |batch| + threads << Thread.new do + command = yield batch.node, batch + execute_command(command) + end + end + + threads.each(&:join) + end end # class end # module diff --git a/lib/aerospike/cluster.rb b/lib/aerospike/cluster.rb index 643d3d04..f5f5b4c4 100644 --- a/lib/aerospike/cluster.rb +++ b/lib/aerospike/cluster.rb @@ -120,15 +120,15 @@ def connected? # Returns a node on the cluster for read operations def batch_read_node(partition, replica_policy) case replica_policy - when Aerospike::Replica::MASTER, Aerospike::Replica::SEQUENCE - return master_node(partition) - when Aerospike::Replica::MASTER_PROLES - return master_proles_node(partition) - when Aerospike::Replica::PREFER_RACK - return rack_node(partition, seq) - when Aerospike::Replica::RANDOM - return random_node - else + when Aerospike::Replica::MASTER, Aerospike::Replica::SEQUENCE + master_node(partition) + when Aerospike::Replica::MASTER_PROLES + master_proles_node(partition) + when Aerospike::Replica::PREFER_RACK + rack_node(partition, seq) + when Aerospike::Replica::RANDOM + random_node + else raise Aerospike::Exceptions::InvalidNode("invalid policy.replica value") end end @@ -136,17 +136,17 @@ def batch_read_node(partition, replica_policy) # Returns a node on the cluster for read operations def read_node(partition, replica_policy, seq) case replica_policy - when Aerospike::Replica::MASTER - return master_node(partition) - when Aerospike::Replica::MASTER_PROLES - return master_proles_node(partition) - when Aerospike::Replica::PREFER_RACK - return rack_node(partition, seq) - when Aerospike::Replica::SEQUENCE - return sequence_node(partition, seq) - when Aerospike::Replica::RANDOM - return random_node - else + when Aerospike::Replica::MASTER + master_node(partition) + when Aerospike::Replica::MASTER_PROLES + master_proles_node(partition) + when Aerospike::Replica::PREFER_RACK + rack_node(partition, seq) + when Aerospike::Replica::SEQUENCE + sequence_node(partition, seq) + when Aerospike::Replica::RANDOM + random_node + else raise Aerospike::Exceptions::InvalidNode("invalid policy.replica value") end end @@ -155,12 +155,12 @@ def read_node(partition, replica_policy, seq) def master_node(partition) partition_map = partitions replica_array = partition_map[partition.namespace] - raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array + raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array - node_array = (replica_array.get)[0] - raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !node_array + node_array = replica_array.get[0] + raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless node_array - node = (node_array.get)[partition.partition_id] + node = node_array.get[partition.partition_id] raise Aerospike::Exceptions::InvalidNode if !node || !node.active? node @@ -170,7 +170,7 @@ def master_node(partition) def rack_node(partition, seq) partition_map = partitions replica_array = partition_map[partition.namespace] - raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array + raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array replica_array = replica_array.get @@ -179,10 +179,10 @@ def rack_node(partition, seq) node = nil fallback = nil for i in 1..replica_array.length - idx = (seq.update{|v| v.succ} % replica_array.size).abs - node = (replica_array[idx].get)[partition.partition_id] + idx = (seq.update { |v| v.succ } % replica_array.size).abs + node = replica_array[idx].get[partition.partition_id] - next if !node + next unless node fallback = node @@ -202,14 +202,14 @@ def rack_node(partition, seq) def master_proles_node(partition) partition_map = partitions replica_array = partition_map[partition.namespace] - raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array + raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array replica_array = replica_array.get node = nil for replica in replica_array - idx = (@replica_index.update{|v| v.succ} % replica_array.size).abs - node = (replica_array[idx].get)[partition.partition_id] + idx = (@replica_index.update { |v| v.succ } % replica_array.size).abs + node = replica_array[idx].get[partition.partition_id] return node if node && node.active? end @@ -221,14 +221,14 @@ def master_proles_node(partition) def sequence_node(partition, seq) partition_map = partitions replica_array = partition_map[partition.namespace] - raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array + raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array replica_array = replica_array.get node = nil for replica in replica_array - idx = (seq.update{|v| v.succ} % replica_array.size).abs - node = (replica_array[idx].get)[partition.partition_id] + idx = (seq.update { |v| v.succ } % replica_array.size).abs + node = replica_array[idx].get[partition.partition_id] return node if node && node.active? end @@ -236,9 +236,13 @@ def sequence_node(partition, seq) raise Aerospike::Exceptions::InvalidNode end - def get_node_for_key(replica_policy, key) + def get_node_for_key(replica_policy, key, is_write: false) partition = Partition.new_by_key(key) - batch_read_node(partition, replica_policy) + if is_write + master_node(partition) + else + batch_read_node(partition, replica_policy) + end end # Returns partitions pertaining to a node @@ -247,10 +251,10 @@ def node_partitions(node, namespace) partition_map = partitions replica_array = partition_map[namespace] - raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array + raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array - node_array = (replica_array.get)[0] - raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !node_array + node_array = replica_array.get[0] + raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless node_array pid = 0 @@ -270,7 +274,7 @@ def random_node i = 0 while i < length # Must handle concurrency with other non-tending threads, so node_index is consistent. - idx = (@node_index.update{ |v| v.succ } % node_array.length).abs + idx = (@node_index.update { |v| v.succ } % node_array.length).abs node = node_array[idx] return node if node.active? @@ -366,13 +370,13 @@ def launch_tend_thread @tend_thread = Thread.new do Thread.current.abort_on_exception = false loop do - begin + tend sleep(@tend_interval / 1000.0) - rescue => e + rescue => e Aerospike.logger.error("Exception occured during tend: #{e}") Aerospike.logger.debug { e.backtrace.join("\n") } - end + end end end @@ -453,7 +457,7 @@ def refresh_nodes def log_tend_stats(nodes) diff = nodes.size - @old_node_count - action = "#{diff.abs} #{diff.abs == 1 ? "node has" : "nodes have"} #{diff > 0 ? "joined" : "left"} the cluster." + action = "#{diff.abs} #{diff.abs == 1 ? 'node has' : 'nodes have'} #{diff > 0 ? 'joined' : 'left'} the cluster." Aerospike.logger.info("Tend finished. #{action} Old node count: #{@old_node_count}, New node count: #{nodes.size}") @old_node_count = nodes.size end @@ -689,11 +693,11 @@ def remove_nodes_copy(nodes_to_remove) end def node_exists(search, node_list) - node_list.any? {|node| node == search } + node_list.any? { |node| node == search } end def find_node_by_name(node_name) - nodes.detect{|node| node.name == node_name } + nodes.detect { |node| node.name == node_name } end end end diff --git a/lib/aerospike/command/batch_index_node.rb b/lib/aerospike/command/batch_index_node.rb index 8cb93d28..33514076 100644 --- a/lib/aerospike/command/batch_index_node.rb +++ b/lib/aerospike/command/batch_index_node.rb @@ -19,13 +19,12 @@ module Aerospike class BatchIndexNode #:nodoc: - attr_accessor :node - attr_accessor :keys_by_idx + attr_accessor :node, :keys_by_idx def self.generate_list(cluster, replica_policy, keys) keys.each_with_index - .group_by { |key, _| cluster.get_node_for_key(replica_policy, key) } - .map { |node, keys_with_idx| BatchIndexNode.new(node, keys_with_idx) } + .group_by { |key, _| cluster.get_node_for_key(replica_policy, key) } + .map { |node, keys_with_idx| BatchIndexNode.new(node, keys_with_idx) } end def initialize(node, keys_with_idx) diff --git a/lib/aerospike/command/batch_operate_command.rb b/lib/aerospike/command/batch_operate_command.rb new file mode 100644 index 00000000..db816e83 --- /dev/null +++ b/lib/aerospike/command/batch_operate_command.rb @@ -0,0 +1,151 @@ +# Copyright 2018 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +require 'aerospike/command/multi_command' + +module Aerospike + + class BatchOperateCommand < MultiCommand #:nodoc: + + attr_accessor :batch, :policy, :attr, :records + + def initialize(node, batch, policy, records) + super(node) + @batch = batch + @policy = policy + @records = records + end + + def batch_flags + flags = 0 + # flags |= 0x1 if @policy.allow_inline + flags |= 0x2 if @policy.allow_inline_ssd + flags |= 0x4 if @policy.respond_all_keys + flags + end + + def write_buffer + field_count = 1 + + exp_size = estimate_expression_size(@policy.filter_exp) + @data_offset += exp_size + field_count += 1 if exp_size > 0 + + @data_buffer.reset + begin_cmd + @data_offset += FIELD_HEADER_SIZE + 4 + 1 # batch.keys.length + flags + + prev = nil + @records.each do |record| + key = record.key + @data_offset += key.digest.length + 4 # 4 byte batch offset + + if !@policy.send_key && !prev.nil? && prev.key.namespace == key.namespace && prev.key.set_name == key.set_name && record == prev + @data_offset += 1 + else + @data_offset += 12 + @data_offset += key.namespace.bytesize + FIELD_HEADER_SIZE + @data_offset += key.set_name.bytesize + FIELD_HEADER_SIZE + @data_offset += record.size + end + + prev = record + end + size_buffer + write_batch_header(policy, field_count) + + write_filter_exp(@policy.filter_exp, exp_size) + + field_size_offset = @data_offset + + write_field_header(0, Aerospike::FieldType::BATCH_INDEX) + @data_offset += @data_buffer.write_int32(batch.records.length, @data_offset) + @data_offset += @data_buffer.write_byte(batch_flags, @data_offset) + + prev = nil + attr = BatchAttr.new + batch.records.each_with_index do |record, index| + @data_offset += @data_buffer.write_int32(index, @data_offset) + key = record.key + @data_offset += @data_buffer.write_binary(key.digest, @data_offset) + + if !@policy.send_key && !prev.nil? && prev.key.namespace == key.namespace && prev.key.set_name == key.set_name && record == prev + @data_offset += @data_buffer.write_byte(BATCH_MSG_REPEAT, @data_offset) + else + case record + when BatchRead + attr.set_batch_read(record.policy) + if record.bin_names&.length&.> 0 + write_batch_bin_names(key, record.bin_names, attr, attr.filter_exp) + elsif record.ops&.length&.> 0 + attr.adjust_read(br.ops) + write_batch_operations(key, record.ops, attr, attr.filter_exp) + else + attr.adjust_read_all_bins(record.read_all_bins) + write_batch_read(key, attr, attr.filter_exp, 0) + end + + when BatchWrite + attr.set_batch_write(record.policy) + attr.adjust_write(record.ops) + write_batch_operations(key, record.ops, attr, attr.filter_exp) + + when BatchUDF + attr.set_batch_udf(record.policy) + write_batch_write(key, attr, attr.filter_exp, 3, 0) + write_field_string(record.package_name, Aerospike::FieldType::UDF_PACKAGE_NAME) + write_field_string(record.function_name, Aerospike::FieldType::UDF_FUNCTION) + write_field_bytes(record.arg_bytes, Aerospike::FieldType::UDF_ARGLIST) + + when BatchDelete + attr.set_batch_delete(record.policy) + write_batch_write(key, attr, attr.filter_exp, 0, 0) + end + + prev = record + end + end + + @data_buffer.write_uint32(@data_offset-MSG_TOTAL_HEADER_SIZE-4, field_size_offset) + + end_cmd + mark_compressed(@policy) + end + + # Parse all results in the batch. Add records to shared list. + # If the record was not found, the bins will be nil. + def parse_row(result_code) + generation = @data_buffer.read_int32(6) + expiration = @data_buffer.read_int32(10) + batch_index = @data_buffer.read_int32(14) + field_count = @data_buffer.read_int16(18) + op_count = @data_buffer.read_int16(20) + + skip_key(field_count) + req_key = records[batch_index].key + + records[batch_index].result_code = result_code + case result_code + when 0, ResultCode::UDF_BAD_RESPONSE + record = parse_record(req_key, op_count, generation, expiration) + records[batch_index].record = record + end + end + + end # class + +end # module diff --git a/lib/aerospike/command/batch_operate_node.rb b/lib/aerospike/command/batch_operate_node.rb new file mode 100644 index 00000000..1905ed6c --- /dev/null +++ b/lib/aerospike/command/batch_operate_node.rb @@ -0,0 +1,51 @@ +# Copyright 2018 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +module Aerospike + + class BatchOperateNode #:nodoc: + + attr_accessor :node, :records_by_idx + + def self.generate_list(cluster, replica_policy, records) + records.each_with_index + .group_by { |record, _| cluster.get_node_for_key(replica_policy, record.key, is_write: record.has_write) } + .map { |node, records_with_idx| BatchOperateNode.new(node, records_with_idx) } + end + + def initialize(node, records_with_idx) + @node = node + @records_by_idx = records_with_idx.map(&:reverse).to_h + end + + def records + records_by_idx.values + end + + def each_record_with_index + records_by_idx.each do |idx, rec| + yield rec, idx + end + end + + def record_for_index(idx) + @records_by_idx[idx] + end + + end + +end diff --git a/lib/aerospike/command/command.rb b/lib/aerospike/command/command.rb index 05807a3d..03da4bcb 100644 --- a/lib/aerospike/command/command.rb +++ b/lib/aerospike/command/command.rb @@ -76,6 +76,12 @@ module Aerospike # Completely replace existing record only. INFO3_REPLACE_ONLY = Integer(1 << 5) + BATCH_MSG_READ = 0x0 + BATCH_MSG_REPEAT = 0x1 + BATCH_MSG_INFO = 0x2 + BATCH_MSG_GEN = 0x4 + BATCH_MSG_TTL = 0x8 + MSG_TOTAL_HEADER_SIZE = 30 FIELD_HEADER_SIZE = 5 OPERATION_HEADER_SIZE = 8 @@ -958,7 +964,7 @@ def write_header_read(policy, read_attr, info_attr, field_count, operation_count @data_buffer.write_byte(0, 10) @data_buffer.write_byte(info_attr, 11) - (12...22).each { |i| @data_buffer.write_byte(i, 0) } + (12...22).each { |i| @data_buffer.write_byte(0, i) } # Initialize timeout. It will be written later. @data_buffer.write_byte(0, 22) @@ -981,7 +987,7 @@ def write_header_read_header(policy, read_attr, field_count, operation_count) @data_buffer.write_byte(0, 10) @data_buffer.write_byte(info_attr, 11) - (12...22).each { |i| @data_buffer.write_byte(i, 0) } + (12...22).each { |i| @data_buffer.write_byte(0, i) } # Initialize timeout. It will be written later. @data_buffer.write_byte(0, 22) @@ -995,6 +1001,89 @@ def write_header_read_header(policy, read_attr, field_count, operation_count) @data_offset = MSG_TOTAL_HEADER_SIZE end + def write_batch_operations(key, ops, attr, filter_exp) + if attr.has_write + write_batch_write(key, attr, filter_exp, 0, ops.length) + else + write_batch_read(key, attr, filter_exp, ops.length) + end + + ops.each do |op| + write_operation_for_operation(op) + end + end + + def write_batch_fields(key, field_count, op_count) + field_count+=2 + @data_offset += @data_buffer.write_uint16(field_count, @data_offset) + @data_offset += @data_buffer.write_uint16(op_count, @data_offset) + write_field_string(key.namespace, Aerospike::FieldType::NAMESPACE) + write_field_string(key.set_name, Aerospike::FieldType::TABLE) + end + + def write_batch_fields_with_filter(key, filter_exp, field_count, op_count) + if filter_exp + field_count+=1 + write_batch_fields(key, field_count, op_count) + write_filter_exp(filter_exp, filter_exp.size) + else + write_batch_fields(key, field_count, op_count) + end + end + + def write_batch_read(key, attr, filter_exp, op_count) + @data_offset += @data_buffer.write_byte(BATCH_MSG_INFO | BATCH_MSG_TTL, @data_offset) + @data_offset += @data_buffer.write_byte(attr.read_attr, @data_offset) + @data_offset += @data_buffer.write_byte(attr.write_attr, @data_offset) + @data_offset += @data_buffer.write_byte(attr.info_attr, @data_offset) + @data_offset += @data_buffer.write_uint32(attr.expiration, @data_offset) + write_batch_fields_with_filter(key, filter_exp, 0, op_count) + end + + def write_batch_write(key, attr, filter_exp, field_count, op_count) + @data_offset += @data_buffer.write_byte(BATCH_MSG_INFO | BATCH_MSG_GEN | BATCH_MSG_TTL, @data_offset) + @data_offset += @data_buffer.write_byte(attr.read_attr, @data_offset) + @data_offset += @data_buffer.write_byte(attr.write_attr, @data_offset) + @data_offset += @data_buffer.write_byte(attr.info_attr, @data_offset) + @data_offset += @data_buffer.write_uint16(attr.generation, @data_offset) + @data_offset += @data_buffer.write_uint32(attr.expiration, @data_offset) + if attr.send_key + field_count+=1 + write_batch_fields_with_filter(key, filter_exp, field_count, op_count) + write_field_value(key.user_key, KEY) + else + write_batch_fields_with_filter(key, filter_exp, field_count, op_count) + end + end + + def write_batch_bin_names(key, bin_names, attr, filter_exp) + write_batch_read(key, attr, filter_exp, bin_names.length) + bin_names.each do |bin_name| + write_operation_for_bin_name(bin_name, Aerospike::Operation::READ) + end + end + + def write_batch_header(policy, field_count) + read_attr = INFO1_BATCH + read_attr |= INFO1_COMPRESS_RESPONSE if policy.use_compression + #TODO: Add SC Mode + + @data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message header.length, @data_offset. + @data_buffer.write_byte(read_attr, 9) + @data_buffer.write_byte(0, 10) + @data_buffer.write_byte(0, 11) + + (12...22).each { |i| @data_buffer.write_byte(0, i) } + + # Initialize timeout. It will be written later. + @data_buffer.write_uint32(0, 22) + + @data_buffer.write_uint16(field_count, 26) + @data_buffer.write_uint16(0, 28) + + @data_offset = MSG_TOTAL_HEADER_SIZE + end + def write_key(key, policy = nil) # Write key into buffer. if key.namespace diff --git a/lib/aerospike/node.rb b/lib/aerospike/node.rb index f95b9bb1..b2799e7b 100644 --- a/lib/aerospike/node.rb +++ b/lib/aerospike/node.rb @@ -71,6 +71,10 @@ def query_show? (@features & HAS_QUERY_SHOW) != 0 end + def batch_any? + (@features & HAS_BATCH_ANY) != 0 + end + def update_racks(parser) new_racks = parser.update_racks @racks.value = new_racks if new_racks @@ -78,7 +82,7 @@ def update_racks(parser) def has_rack(ns, rack_id) racks = @racks.value - return false if !racks + return false unless racks racks[ns] == rack_id end @@ -108,7 +112,7 @@ def get_connection(timeout) # Put back a connection to the cache. If cache is full, the connection will be # closed and discarded def put_connection(conn) - conn.close if !active? + conn.close unless active? @connections.offer(conn) end @@ -236,7 +240,7 @@ def refresh_partitions(peers) Node::Refresh::Partitions.(self, peers) end - def refresh_racks() + def refresh_racks Node::Refresh::Racks.(self) end diff --git a/lib/aerospike/operation.rb b/lib/aerospike/operation.rb index 974110c6..b72fe388 100644 --- a/lib/aerospike/operation.rb +++ b/lib/aerospike/operation.rb @@ -80,5 +80,42 @@ def self.touch def self.delete Operation.new(DELETE) end + + def is_write? + case @op_type + when READ + false + when READ_HEADER + false + when WRITE + true + when CDT_READ + false + when CDT_MODIFY + true + when ADD + true + when EXP_READ + false + when EXP_MODIFY + true + when APPEND + true + when PREPEND + true + when TOUCH + true + when BIT_READ + false + when BIT_MODIFY + true + when DELETE + true + when HLL_READ + false + when HLL_MODIFY + true + end + end end end # module diff --git a/lib/aerospike/policy/batch_delete_policy.rb b/lib/aerospike/policy/batch_delete_policy.rb new file mode 100644 index 00000000..8372cdee --- /dev/null +++ b/lib/aerospike/policy/batch_delete_policy.rb @@ -0,0 +1,71 @@ +# encoding: utf-8 +# Copyright 2014-2024 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License") you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +module Aerospike + + # Policy attributes used in batch delete commands. + class BatchDeletePolicy + attr_accessor :filter_exp, :commit_level, :generation_policy, :generation, :durable_delete, :send_key + + def initialize(opt = {}) + # Optional expression filter. If filterExp exists and evaluates to false, the specific batch key + # request is not performed and {@link com.aerospike.client.BatchRecord#result_code} is set to + # {@link com.aerospike.client.ResultCode#FILTERED_OUT}. + # + # If exists, this filter overrides the batch parent filter {@link com.aerospike.client.policy.Policy#filter_exp} + # for the specific key in batch commands that allow a different policy per key. + # Otherwise, this filter is ignored. + # + # Default: nil + @filter_exp = opt[:filter_exp] + + # Desired consistency guarantee when committing a transaction on the server. The default + # (COMMIT_ALL) indicates that the server should wait for master and all replica commits to + # be successful before returning success to the client. + # + # Default: CommitLevel.COMMIT_ALL + @commit_level = opt[:commit_level] || CommitLevel::COMMIT_ALL + + # Qualify how to handle record deletes based on record generation. The default (NONE) + # indicates that the generation is not used to restrict deletes. + # + # Default: GenerationPolicy.NONE + @generation_policy = opt[:generation_policy] || GenerationPolicy::NONE + + # Expected generation. Generation is the number of times a record has been modified + # (including creation) on the server. This field is only relevant when generationPolicy + # is not NONE. + # + # Default: 0 + @generation = opt[:generation] || 0 + + # If the transaction results in a record deletion, leave a tombstone for the record. + # This prevents deleted records from reappearing after node failures. + # Valid for Aerospike Server Enterprise Edition only. + # + # Default: false (do not tombstone deleted records). + @durable_delete = opt[:durable_delete] || false + + # Send user defined key in addition to hash digest. + # If true, the key will be stored with the tombstone record on the server. + # + # Default: false (do not send the user defined key) + @send_key = opt[:send_key] || false + + self + end + end +end \ No newline at end of file diff --git a/lib/aerospike/policy/batch_policy.rb b/lib/aerospike/policy/batch_policy.rb index c54247ab..b9233413 100644 --- a/lib/aerospike/policy/batch_policy.rb +++ b/lib/aerospike/policy/batch_policy.rb @@ -21,9 +21,10 @@ module Aerospike # Container object for batch policy command. class BatchPolicy < Policy + attr_accessor :allow_inline_ssd, :respond_all_keys, :send_key def initialize(opt={}) - super(opt) + super # [:nodoc:] # DEPRECATED @@ -39,11 +40,58 @@ def initialize(opt={}) # index protocol will perform this record proxy when necessary. # # Default: false (use new batch index protocol if server supports it) - @use_batch_direct = opt.fetch(:use_batch_direct) { false } - + @use_batch_direct = opt.fetch(:use_batch_direct, false) + + + # Allow batch to be processed immediately in the server's receiving thread for SSD + # namespaces. If false, the batch will always be processed in separate service threads. + # Server versions < 6.0 ignore this field. + # + # Inline processing can introduce the possibility of unfairness because the server + # can process the entire batch before moving onto the next command. + # + # Default: false + @allow_inline_ssd = opt.fetch(:allow_inline_ssd, false) + + + # Should all batch keys be attempted regardless of errors. This field is used on both + # the client and server. The client handles node specific errors and the server handles + # key specific errors. + # + # If true, every batch key is attempted regardless of previous key specific errors. + # Node specific errors such as timeouts stop keys to that node, but keys directed at + # other nodes will continue to be processed. + # + # If false, the server will stop the batch to its node on most key specific errors. + # The exceptions are {@link com.aerospike.client.ResultCode#KEY_NOT_FOUND_ERROR} and + # {@link com.aerospike.client.ResultCode#FILTERED_OUT} which never stop the batch. + # The client will stop the entire batch on node specific errors. The client will + # not stop the entire batch commands run in parallel. + # + # Server versions < 6.0 do not support this field and treat this value as false + # for key specific errors. + # + # Default: true + @respond_all_keys = opt.fetch(:respond_all_keys, true) + + + # Send user defined key in addition to hash digest on a record put. + # The default is to _not_ send the user defined key. + @send_key = opt.fetch(:send_key, false) + self end + def self.read_default + BatchPolicy.new + end + + def self.write_default + bp = BatchPolicy.new + bp.max_retries = 0 + bp + end + end # class end # module diff --git a/lib/aerospike/policy/batch_read_policy.rb b/lib/aerospike/policy/batch_read_policy.rb new file mode 100644 index 00000000..309534b3 --- /dev/null +++ b/lib/aerospike/policy/batch_read_policy.rb @@ -0,0 +1,38 @@ +# Copyright 2014-2020 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +module Aerospike + + # Policy attributes used in batch read commands. + class BatchReadPolicy + + attr_accessor :filter_exp + + def initialize(opt={}) + # Optional expression filter. If filterExp exists and evaluates to false, the specific batch key + # request is not performed and {@link com.aerospike.client.BatchRecord#result_code} is set to + # {@link com.aerospike.client.ResultCode#FILTERED_OUT}. + # + # If exists, this filter overrides the batch parent filter {@link com.aerospike.client.policy.Policy#filter_exp} + # for the specific key in batch commands that allow a different policy per key. + # Otherwise, this filter is ignored. + # + # Default: nil + @filter_exp = opt[:filter_exp] + end + end +end \ No newline at end of file diff --git a/lib/aerospike/policy/batch_udf_policy.rb b/lib/aerospike/policy/batch_udf_policy.rb new file mode 100644 index 00000000..f1f014b2 --- /dev/null +++ b/lib/aerospike/policy/batch_udf_policy.rb @@ -0,0 +1,75 @@ +# Copyright 2014-2023 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +module Aerospike + + # Policy attributes used in batch UDF execute commands. + class BatchUDFPolicy + + attr_accessor :filter_exp, :commit_level, :ttl, :durable_delete, :send_key + + alias expiration ttl + alias expiration= ttl= + + def initialize(opt={}) + # Optional expression filter. If filterExp exists and evaluates to false, the specific batch key + # request is not performed and {@link com.aerospike.client.BatchRecord#resultCode} is set to + # {@link com.aerospike.client.ResultCode#FILTERED_OUT}. + # + # If exists, this filter overrides the batch parent filter {@link com.aerospike.client.policy.Policy#filterExp} + # for the specific key in batch commands that allow a different policy per key. + # Otherwise, this filter is ignored. + # + # Default: nil + @filter_exp = opt[:filter_exp] + + # Desired consistency guarantee when committing a transaction on the server. The default + # (COMMIT_ALL) indicates that the server should wait for master and all replica commits to + # be successful before returning success to the client. + # + # Default: CommitLevel::COMMIT_ALL + @commit_level = opt.fetch(:commit_level, CommitLevel::COMMIT_ALL) + + # Record expiration; also known as time-to-live (TTL). + # Seconds record will live before being removed by the server. + # + # Supported values: + # - `Aerospike::TTL::NEVER_EXPIRE`: Never expire record; requires Aerospike 2 + # server versions >= 2.7.2 or Aerospike 3 server versions >= 3.1.4. Do + # not use for older servers. + # - `Aerospike::TTL::NAMESPACE_DEFAULT`: Default to namespace configuration + # variable "default-ttl" on the server. + # - `Aerospike::TTL::DONT_UPDATE`: Do not change a record's expiration date + # when updating the record. Requires Aerospike server v3.10.1 or later. + # - Any value > 0: Actual time-to-live in seconds. + @ttl = opt[:ttl] || opt[:expiration] || 0 + + # If the transaction results in a record deletion, leave a tombstone for the record. + # This prevents deleted records from reappearing after node failures. + # Valid for Aerospike Server Enterprise Edition only. + # + # Default: false (do not tombstone deleted records). + @durable_delete = opt.fetch(:durable_delete, false) + + # Send user defined key in addition to hash digest. + # If true, the key will be stored with the record on the server. + # + # Default: false (do not send the user defined key) + @send_key = opt.fetch(:send_key, false) + end + end +end \ No newline at end of file diff --git a/lib/aerospike/policy/batch_write_policy.rb b/lib/aerospike/policy/batch_write_policy.rb new file mode 100644 index 00000000..c17e56c5 --- /dev/null +++ b/lib/aerospike/policy/batch_write_policy.rb @@ -0,0 +1,105 @@ +# Copyright 2014-2023 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +module Aerospike + + + # Policy attributes used in batch write commands. + class BatchWritePolicy + + attr_accessor :filter_exp, :record_exists_action, :commit_level, + :generation_policy, :generation, :ttl, :durable_delete, + :send_key + + alias expiration ttl + alias expiration= ttl= + + def initialize(opt={}) + # Optional expression filter. If filterExp exists and evaluates to false, the specific batch key + # request is not performed and {@link com.aerospike.client.BatchRecord#resultCode} is set to + # {@link com.aerospike.client.ResultCode#FILTERED_OUT}. + # + # If exists, this filter overrides the batch parent filter {@link com.aerospike.client.policy.Policy#filter_exp} + # for the specific key in batch commands that allow a different policy per key. + # Otherwise, this filter is ignored. + # + # Default: nil + @filter_exp = opt[:filter_exp] + + # Qualify how to handle writes where the record already exists. + # + # Default: RecordExistsAction::UPDATE + @record_exists_action = opt.fetch(:record_exists_action, RecordExistsAction::UPDATE) + + # Desired consistency guarantee when committing a transaction on the server. The default + # (COMMIT_ALL) indicates that the server should wait for master and all replica commits to + # be successful before returning success to the client. + # + # Default: CommitLevel::COMMIT_ALL + @commit_level = opt.fetch(:commit_level, CommitLevel::COMMIT_ALL) + + # Qualify how to handle record writes based on record generation. The default (NONE) + # indicates that the generation is not used to restrict writes. + # + # The server does not support this field for UDF execute() calls. The read-modify-write + # usage model can still be enforced inside the UDF code itself. + # + # Default: GenerationPolicy::NONE + @generation_policy = opt.fetch(:generation_policy, GenerationPolicy::NONE) + + # Expected generation. Generation is the number of times a record has been modified + # (including creation) on the server. If a write operation is creating a record, + # the expected generation would be 0. This field is only relevant when + # generationPolicy is not NONE. + # + # The server does not support this field for UDF execute() calls. The read-modify-write + # usage model can still be enforced inside the UDF code itself. + # + # Default: 0 + @generation = opt.fetch(:generation, 0) + + # Record expiration; also known as time-to-live (TTL). + # Seconds record will live before being removed by the server. + # + # Supported values: + # - `Aerospike::TTL::NEVER_EXPIRE`: Never expire record; requires Aerospike 2 + # server versions >= 2.7.2 or Aerospike 3 server versions >= 3.1.4. Do + # not use for older servers. + # - `Aerospike::TTL::NAMESPACE_DEFAULT`: Default to namespace configuration + # variable "default-ttl" on the server. + # - `Aerospike::TTL::DONT_UPDATE`: Do not change a record's expiration date + # when updating the record. Requires Aerospike server v3.10.1 or later. + # - Any value > 0: Actual time-to-live in seconds. + @ttl = opt[:ttl] || opt[:expiration] || 0 + + # If the transaction results in a record deletion, leave a tombstone for the record. + # This prevents deleted records from reappearing after node failures. + # Valid for Aerospike Server Enterprise Edition only. + # + # Default: false (do not tombstone deleted records). + @durable_delete = opt.fetch(:durable_delete, false) + + # Send user defined key in addition to hash digest. + # If true, the key will be stored with the record on the server. + # + # Default: false (do not send the user defined key) + @send_key = opt.fetch(:send_key, false) + + self + end + end +end \ No newline at end of file diff --git a/lib/aerospike/utils/buffer.rb b/lib/aerospike/utils/buffer.rb index d14c757b..0ed7d62f 100644 --- a/lib/aerospike/utils/buffer.rb +++ b/lib/aerospike/utils/buffer.rb @@ -25,7 +25,7 @@ module Aerospike # Buffer class to ease the work around class Buffer #:nodoc: @@buf_pool = Pool.new - @@buf_pool.create_proc = Proc.new { Buffer.new } + @@buf_pool.create_proc = proc { Buffer.new } attr_accessor :buf @@ -43,7 +43,7 @@ class Buffer #:nodoc: MAX_BUFFER_SIZE = 10 * 1024 * 1024 def initialize(size = DEFAULT_BUFFER_SIZE, buf = nil) - @buf = (buf ? buf : ("%0#{size}d" % 0)) + @buf = buf || format("%0#{size}d", 0) @buf.force_encoding("binary") @slice_end = @buf.bytesize end @@ -60,7 +60,7 @@ def size @buf.bytesize end - alias_method :length, :size + alias length size def eat!(n) @buf.replace(@buf[n..-1]) @@ -74,7 +74,7 @@ def resize(length) end if @buf.bytesize < length - @buf.concat("%0#{length - @buf.bytesize}d" % 0) + @buf.concat(format("%0#{length - @buf.bytesize}d", 0)) end @slice_end = length end @@ -144,37 +144,37 @@ def read(offset, len = nil) def read_int16(offset) vals = @buf[offset..offset + 1] - vals.unpack(INT16)[0] + vals.unpack1(INT16) end def read_uint16(offset) vals = @buf[offset..offset + 1] - vals.unpack(UINT16)[0] + vals.unpack1(UINT16) end def read_int32(offset) vals = @buf[offset..offset + 3] - vals.unpack(INT32)[0] + vals.unpack1(INT32) end def read_uint32(offset) vals = @buf[offset..offset + 3] - vals.unpack(UINT32)[0] + vals.unpack1(UINT32) end def read_int64(offset) vals = @buf[offset..offset + 7] - vals.unpack(INT64)[0] + vals.unpack1(INT64) end def read_uint64_little_endian(offset) vals = @buf[offset..offset + 7] - vals.unpack(UINT64LE)[0] + vals.unpack1(UINT64LE) end def read_uint64(offset) vals = @buf[offset..offset + 7] - vals.unpack(UINT64)[0] + vals.unpack1(UINT64) end def read_var_int64(offset, len) @@ -190,7 +190,7 @@ def read_var_int64(offset, len) def read_double(offset) vals = @buf[offset..offset + 7] - vals.unpack(DOUBLE)[0] + vals.unpack1(DOUBLE) end def read_bool(offset, length) @@ -219,13 +219,13 @@ def dump(start = 0, finish = nil) @buf.bytes[start...finish].each do |c| if counter >= start print "%02x " % c - ascii << (c.between?(32, 126) ? c : ?.) - print " " if ascii.length == (width / 2 + 1) + ascii << (c.between?(32, 126) ? c : '.') + print " " if ascii.length == ((width / 2) + 1) if ascii.length > width ascii << "|" puts ascii ascii = "|" - print "%08x " % (counter + 1) + print format("%08x ", (counter + 1)) end end counter += 1 diff --git a/spec/aerospike/batch_operate_spec.rb b/spec/aerospike/batch_operate_spec.rb new file mode 100644 index 00000000..a165f8e0 --- /dev/null +++ b/spec/aerospike/batch_operate_spec.rb @@ -0,0 +1,185 @@ +# encoding: utf-8 +# Copyright 2014 Aerospike, Inc. +# +# Portions may be licensed to Aerospike, Inc. under one or more contributor +# license agreements. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +require 'aerospike' +require 'aerospike/batch_read' + +describe Aerospike::Client do + + let(:client) { Support.client } + + describe "#batch_operate" do + let(:batch_policy) do + Aerospike::BatchPolicy.new + end + + let(:existing_keys) { Array.new(3) { Support.gen_random_key } } + # let(:existing_keys) { [Aerospike::Key.new("test", "test", 1)] } + let(:keys) { existing_keys } + let(:no_such_key) { Support.gen_random_key } + let(:keys) { existing_keys } + let(:opts) { { filter_exp: Aerospike::Exp.eq(Aerospike::Exp.int_bin("strval"), Aerospike::Exp.int_val(0)) } } + + before do + existing_keys.each_with_index do |key, idx| + client.put(key, { + 'idx' => idx, + 'key' => key.user_key, + 'rnd' => 99 #rand + }, {}) + end + end + + context '#BatchRead' do + it 'returns specified bins' do + bin_names = %w[idx rnd] + records = [Aerospike::BatchRead.read_bins(keys.first, bin_names)] + client.batch_operate(records, batch_policy) + + expect(records[0].result_code).to eql 0 + expect(records[0].record.bins.length).to eql 2 + end + + it 'returns all records' do + records = [Aerospike::BatchRead.read_all_bins(keys.first)] + client.batch_operate(records, batch_policy) + + expect(records[0].result_code).to eql 0 + expect(records[0].record.bins.length).to eql 3 + end + + it 'filter out' do + records = [Aerospike::BatchRead.read_all_bins(keys.first)] + client.batch_operate(records, opts) + + expect(records[0].result_code).to eql Aerospike::ResultCode::FILTERED_OUT + expect(records[0].record).to eql nil + end + end + + context '#BatchWrite' do + it 'updates specified bins' do + ops = [ + Aerospike::Operation.put(Aerospike::Bin.new("new_bin_str", "value")), + Aerospike::Operation.put(Aerospike::Bin.new("new_bin_int", 999)), + Aerospike::Operation.add(Aerospike::Bin.new("new_bin_int", 1)) + ] + records = [Aerospike::BatchWrite.new(keys.first, ops)] + client.batch_operate(records, batch_policy) + + expect(records[0].result_code).to eql 0 + expect(records[0].record.bins).to eql({ "new_bin_int"=>nil, "new_bin_str"=>nil }) + + records = [Aerospike::BatchRead.read_all_bins(keys.first)] + client.batch_operate(records, batch_policy) + + expect(records[0].record.bins).to eql({ + 'idx' => 0, + 'key' => keys.first.user_key, + 'rnd' => 99, #rand + "new_bin_str" => "value", + "new_bin_int" => 1000 + }) + end + + it 'filter out' do + ops = [ + Aerospike::Operation.put(Aerospike::Bin.new("new_bin_str", "value")), + Aerospike::Operation.put(Aerospike::Bin.new("new_bin_int", 999)), + Aerospike::Operation.add(Aerospike::Bin.new("new_bin_int", 1)) + ] + records = [Aerospike::BatchWrite.new(keys.first, ops)] + client.batch_operate(records, opts) + + expect(records[0].result_code).to eql Aerospike::ResultCode::FILTERED_OUT + expect(records[0].record).to eql nil + end + + it 'removes specific records' do + ops = [ + Aerospike::Operation.delete + ] + records = [Aerospike::BatchWrite.new(keys.first, ops)] + client.batch_operate(records, batch_policy) + + exists = client.exists(keys.first) + expect(exists).to eql false + + end + end + + context '#BatchDelete' do + it 'removes specific records' do + ops = [ + Aerospike::Operation.delete + ] + records = [Aerospike::BatchDelete.new(keys.first)] + client.batch_operate(records, batch_policy) + + expect(records[0].result_code).to eql 0 + expect(records[0].record.bins).to eql nil + + exists = client.exists(keys.first) + expect(exists).to eql false + end + + it 'filter out' do + ops = [ + Aerospike::Operation.delete + ] + records = [Aerospike::BatchDelete.new(keys.first)] + client.batch_operate(records, opts) + + expect(records[0].result_code).to eql Aerospike::ResultCode::FILTERED_OUT + expect(records[0].record).to eql nil + end + + end # context + + context '#BatchUDF' do + let(:udf_body_string) do + "function testStr(rec, str) + return str -- Return the Return value and/or status + end" + end + + before do + register_task = client.register_udf(udf_body_string, "test-udf-batch.lua", Aerospike::Language::LUA) + expect(register_task.wait_till_completed).to be true + expect(register_task.completed?).to be true + end + + it 'calls specific UDF' do + records = [Aerospike::BatchUDF.new(keys.first, "test-udf-batch", "testStr", ["ping_str"])] + client.batch_operate(records, batch_policy) + + expect(records[0].result_code).to eql 0 + expect(records[0].record.bins).to eql({ "SUCCESS"=>"ping_str" }) + end + + it 'filter out' do + records = [Aerospike::BatchUDF.new(keys.first, "test-udf-batch", "testStr", ["ping_str"])] + client.batch_operate(records, opts) + + expect(records[0].result_code).to eql Aerospike::ResultCode::FILTERED_OUT + expect(records[0].record).to eql nil + end + + end # context + + end # describe + +end # describe From e54c151fa7cce3e819be0b37d035e1eb422dc84d Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Fri, 9 Aug 2024 17:50:34 +0200 Subject: [PATCH 04/13] Cleanup documentation --- .github/workflows/development.yml | 8 +- lib/aerospike/batch_delete.rb | 4 +- lib/aerospike/batch_read.rb | 10 +- lib/aerospike/batch_record.rb | 14 +- lib/aerospike/batch_udf.rb | 4 +- lib/aerospike/batch_write.rb | 10 +- lib/aerospike/cdt/bit_operation.rb | 9 +- lib/aerospike/exp/exp.rb | 44 ++--- lib/aerospike/exp/exp_bit.rb | 48 ++--- lib/aerospike/exp/exp_hll.rb | 24 +-- lib/aerospike/exp/exp_list.rb | 164 ++++++++-------- lib/aerospike/exp/exp_map.rb | 204 ++++++++++---------- lib/aerospike/exp/operation.rb | 4 +- lib/aerospike/operation.rb | 1 + lib/aerospike/policy/batch_delete_policy.rb | 8 +- lib/aerospike/policy/batch_policy.rb | 4 +- lib/aerospike/policy/batch_read_policy.rb | 8 +- lib/aerospike/policy/batch_udf_policy.rb | 8 +- lib/aerospike/policy/batch_write_policy.rb | 8 +- lib/aerospike/policy/policy.rb | 2 +- 20 files changed, 285 insertions(+), 301 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 60acc9df..122c34c3 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -15,10 +15,10 @@ jobs: ruby: - "2.6" - - "2.7" - - "3.0" - - "3.1" - - "3.2" + #- "2.7" + #- "3.0" + #- "3.1" + #- "3.2" - "3.3" experimental: [false] diff --git a/lib/aerospike/batch_delete.rb b/lib/aerospike/batch_delete.rb index f67f1824..89af0403 100644 --- a/lib/aerospike/batch_delete.rb +++ b/lib/aerospike/batch_delete.rb @@ -27,14 +27,14 @@ def initialize(key, opt = {}) @policy = BatchRecord.create_policy(opt, BatchDeletePolicy, DEFAULT_BATCH_DELETE_POLICY) end - def ==(other) + def ==(other) # :nodoc: other && other.instance_of?(self.class) && @policy == other.policy end DEFAULT_BATCH_DELETE_POLICY = BatchDeletePolicy.new # Return wire protocol size. For internal use only. - def size + def size # :nodoc: size = 6 # gen(2) + exp(4) = 6 size += @policy&.filter_exp&.size if @policy&.filter_exp diff --git a/lib/aerospike/batch_read.rb b/lib/aerospike/batch_read.rb index 9a62cb7a..ef0f0934 100644 --- a/lib/aerospike/batch_read.rb +++ b/lib/aerospike/batch_read.rb @@ -25,12 +25,12 @@ class BatchRead < BatchRecord attr_accessor :policy # Bins to retrieve for this key. bin_names are mutually exclusive with - # {@link com.aerospike.client.BatchRead#ops}. + # {BatchRead#ops}. attr_accessor :bin_names # Optional operations for this key. ops are mutually exclusive with - # {@link com.aerospike.client.BatchRead#bin_names}. A bin_name can be emulated with - # {@link com.aerospike.client.Operation#get(String)} + # {BatchRead#bin_names}. A bin_name can be emulated with + # {Operation#get(bin_name)} attr_accessor :ops # If true, ignore bin_names and read all bins. @@ -66,7 +66,7 @@ def self.ops(key, ops, opt = {}) # Optimized reference equality check to determine batch wire protocol repeat flag. # For internal use only. - def ==(other) + def ==(other) # :nodoc: other && other.instance_of?(self.class) && @bin_names.sort == other.bin_names.sort && @ops.sort == other.ops.sort && @policy == other.policy && @read_all_bins == other.read_all_bins @@ -75,7 +75,7 @@ def ==(other) DEFAULT_BATCH_READ_POLICY = BatchReadPolicy.new # Return wire protocol size. For internal use only. - def size + def size # :nodoc: size = 0 size += @policy&.filter_exp&.size if @policy&.filter_exp diff --git a/lib/aerospike/batch_record.rb b/lib/aerospike/batch_record.rb index 898aa7ad..273af83e 100644 --- a/lib/aerospike/batch_record.rb +++ b/lib/aerospike/batch_record.rb @@ -22,11 +22,11 @@ class BatchRecord attr_reader :key # Record result after batch command has completed. Will be null if record was not found - # or an error occurred. See {@link BatchRecord#result_code}. + # or an error occurred. See {BatchRecord#result_code}. attr_reader :record - # Result code for this returned record. See {@link com.aerospike.client.ResultCode}. - # If not {@link com.aerospike.client.ResultCode#OK}, the record will be null. + # Result code for this returned record. See {ResultCode}. + # If not {ResultCode#OK}, the record will be null. attr_accessor :result_code # Is it possible that the write transaction may have completed even though an error @@ -46,7 +46,7 @@ def initialize(key, result_code: ResultCode::NO_RESPONSE, in_doubt: false, has_w @has_write = has_write end - def self.create_policy(policy, policy_klass, default_policy = nil) + def self.create_policy(policy, policy_klass, default_policy = nil) # :nodoc: case policy when nil default_policy || policy_klass.new @@ -61,20 +61,20 @@ def self.create_policy(policy, policy_klass, default_policy = nil) # Prepare for upcoming batch call. Reset result fields because this instance might be # reused. For internal use only. - def prepare + def prepare # :nodoc: @record = nil @result_code = ResultCode::NO_RESPONSE @in_doubt = false end # Set record result. For internal use only. - def record=(record) + def record=(record) # :nodoc: @record = record @result_code = ResultCode::OK end # Set error result. For internal use only. - def set_error(result_code, in_doubt) + def set_error(result_code, in_doubt) # :nodoc: @result_code = result_code @in_doubt = in_doubt end diff --git a/lib/aerospike/batch_udf.rb b/lib/aerospike/batch_udf.rb index 22bdddd0..40c1c6e2 100644 --- a/lib/aerospike/batch_udf.rb +++ b/lib/aerospike/batch_udf.rb @@ -49,7 +49,7 @@ def initialize(key, package_name, function_name, function_args, opt = {}) # Optimized reference equality check to determine batch wire protocol repeat flag. # For internal use only. - def equals(other) + def ==(other) # :nodoc: other && other.instance_of?(self.class) && @function_name == other.function_name && @function_args == other.function_args && @package_name == other.package_name && @policy == other.policy @@ -58,7 +58,7 @@ def equals(other) DEFAULT_BATCH_UDF_POLICY = BatchUDFPolicy.new # Return wire protocol size. For internal use only. - def size + def size # :nodoc: size = 6 # gen(2) + exp(4) = 6 size += @policy&.filter_exp&.size if @policy&.filter_exp diff --git a/lib/aerospike/batch_write.rb b/lib/aerospike/batch_write.rb index 74b5520b..5fa177a1 100644 --- a/lib/aerospike/batch_write.rb +++ b/lib/aerospike/batch_write.rb @@ -30,10 +30,10 @@ class BatchWrite < BatchRecord attr_accessor :ops # Initialize batch key and read/write operations. - #

- # {@link Operation#get()} is not allowed because it returns a variable number of bins and + # + # {Operation#get()} is not allowed because it returns a variable number of bins and # makes it difficult (sometimes impossible) to lineup operations with results. Instead, - # use {@link Operation#get(String)} for each bin name. + # use {Operation#get(bin_name)} for each bin name. def initialize(key, ops, opt = {}) super(key, has_write: true) @policy = BatchRecord.create_policy(opt, BatchWritePolicy, DEFAULT_BATCH_WRITE_POLICY) @@ -42,7 +42,7 @@ def initialize(key, ops, opt = {}) # Optimized reference equality check to determine batch wire protocol repeat flag. # For internal use only. - def ==(other) + def ==(other) # :nodoc: other && other.instance_of?(self.class) && @ops == other.ops && @policy == other.policy && (@policy.nil? || !@policy.send_key) end @@ -50,7 +50,7 @@ def ==(other) DEFAULT_BATCH_WRITE_POLICY = BatchWritePolicy.new # Return wire protocol size. For internal use only. - def size + def size # :nodoc: size = 6 # gen(2) + exp(4) = 6 size += @policy&.filter_exp&.size if @policy&.filter_exp diff --git a/lib/aerospike/cdt/bit_operation.rb b/lib/aerospike/cdt/bit_operation.rb index d0a70857..cb64685a 100644 --- a/lib/aerospike/cdt/bit_operation.rb +++ b/lib/aerospike/cdt/bit_operation.rb @@ -65,9 +65,8 @@ def initialize(op_type, bit_op, bin_name, *arguments, ctx: nil, policy: nil) @arguments = arguments end - # BitResizeOp creates byte "resize" operation. - # Server resizes byte[] to byte_size according to resize_flags (See {@link BitResizeFlags}). + # Server resizes byte[] to byte_size according to resize_flags (See {BitResizeFlags}). # Server does not return a value. # Example: # bin = [0b00000001, 0b01000010] @@ -195,7 +194,7 @@ def self.rshift(bin_name, bit_offset, bit_size, shift, ctx: nil, policy: BitPoli # BitAddOp creates bit "add" operation. # Server adds value to byte[] bin starting at bit_offset for bit_size. Bit_size must be <= 64. # Signed indicates if bits should be treated as a signed number. - # If add overflows/underflows, {@link BitOverflowAction} is used. + # If add overflows/underflows, {BitOverflowAction} is used. # Server does not return a value. # Example: # bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101] @@ -223,7 +222,7 @@ def self.add( # BitSubtractOp creates bit "subtract" operation. # Server subtracts value from byte[] bin starting at bit_offset for bit_size. Bit_size must be <= 64. # Signed indicates if bits should be treated as a signed number. - # If add overflows/underflows, {@link BitOverflowAction} is used. + # If add overflows/underflows, {BitOverflowAction} is used. # Server does not return a value. # Example: # bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101] @@ -351,7 +350,7 @@ def pack_bin_value bytes = nil args = arguments.dup Packer.use do |packer| - if @ctx != nil && @ctx.length > 0 + if !@ctx.nil? && @ctx.length > 0 packer.write_array_header(3) Value.of(0xff).pack(packer) diff --git a/lib/aerospike/exp/exp.rb b/lib/aerospike/exp/exp.rb index b3f3a099..4908c362 100644 --- a/lib/aerospike/exp/exp.rb +++ b/lib/aerospike/exp/exp.rb @@ -96,7 +96,7 @@ def self.key(type) end # Create expression that returns if the primary key is stored in the record meta data - # as a boolean expression. This would occur when {@link Policy#send_key} + # as a boolean expression. This would occur when {Policy#send_key} # is true on record write. This expression usually evaluates quickly because record # meta data is cached in memory. # @@ -214,7 +214,7 @@ def self.bin_exists(name) end # Create expression that returns bin's integer particle type:: - # See {@link ParticleType}. + # See {ParticleType}. # # ==== Examples # # Bin "a" particle type is a list @@ -225,8 +225,8 @@ def self.bin_type(name) # Create expression that returns the record size. This expression usually evaluates # quickly because record meta data is cached in memory. - # Requires server version 7.0+. This expression replaces {@link #deviceSize()} and - # {@link #memorySize()} since those older expressions are equivalent on server version 7.0+. + # Requires server version 7.0+. This expression replaces {#deviceSize()} and + # {#memorySize()} since those older expressions are equivalent on server version 7.0+. # # {@code # // Record size >= 100 KB @@ -347,7 +347,7 @@ def self.digest_modulo(mod) # Exp.regex_compare("prefix.*suffix", RegexFlags.ICASE | RegexFlags.NEWLINE, Exp.str_bin("a")) # # @param regex regular expression string - # @param flags regular expression bit flags. See {@link Exp::RegexFlags} + # @param flags regular expression bit flags. See {Exp::RegexFlags} # @param bin string bin or string value expression def self.regex_compare(regex, flags, bin) Regex.new(bin, regex, flags) @@ -873,7 +873,7 @@ def self.cond(*exps) # # ==== Examples # Args Format: , , ..., - # def: {@link Exp#def(String, Exp)} + # def: {Exp#def(String, Exp)} # exp: Scoped expression # # ==== Examples @@ -887,7 +887,7 @@ def self.let(*exps) Let.new(exps) end - # Assign variable to a {@link Exp#let(Exp...)} expression that can be accessed later. + # Assign variable to a {Exp#let(Exp...)} expression that can be accessed later. # Requires server version 5.6.0+. # # ==== Examples @@ -920,8 +920,8 @@ def self.var(name) #-------------------------------------------------- # Create unknown value. Used to intentionally fail an expression. - # The failure can be ignored with {@link Exp::WriteFlags#EVAL_NO_FAIL} - # or {@link Exp::ReadFlags#EVAL_NO_FAIL}. + # The failure can be ignored with {Exp::WriteFlags#EVAL_NO_FAIL} + # or {Exp::ReadFlags#EVAL_NO_FAIL}. # Requires server version 5.6.0+. # # ==== Examples @@ -1066,10 +1066,7 @@ def self.pack_ctx(packer, ctx) # For internal use only. class Module < Exp - attr_reader :bin - attr_reader :bytes - attr_reader :ret_type - attr_reader :module + attr_reader :bin, :bytes, :ret_type, :module def initialize(bin, bytes, ret_type, modul) @bin = bin @@ -1090,8 +1087,7 @@ def pack(packer) end class Bin < Exp - attr_reader :name - attr_reader :type + attr_reader :name, :type def initialize(name, type) @name = name @@ -1107,9 +1103,7 @@ def pack(packer) end class Regex < Exp - attr_reader :bin - attr_reader :regex - attr_reader :flags + attr_reader :bin, :regex, :flags def initialize(bin, regex, flags) @bin = bin @@ -1135,7 +1129,7 @@ def initialize(exps) def pack(packer) # Let wire format: LET , , , , ..., - count = (@exps.length - 1) * 2 + 2 + count = ((@exps.length - 1) * 2) + 2 packer.write_array_header(count) packer.write(LET) @@ -1146,8 +1140,7 @@ def pack(packer) end class Def < Exp - attr_reader :name - attr_reader :exp + attr_reader :name, :exp def initialize(name, exp) @name = name @@ -1161,8 +1154,7 @@ def pack(packer) end class CmdExp < Exp - attr_reader :exps - attr_reader :cmd + attr_reader :exps, :cmd def initialize(cmd, *exps) @exps = exps @@ -1179,8 +1171,7 @@ def pack(packer) end class CmdInt < Exp - attr_reader :cmd - attr_reader :val + attr_reader :cmd, :val def initialize(cmd, val) @cmd = cmd @@ -1195,8 +1186,7 @@ def pack(packer) end class CmdStr < Exp - attr_reader :str - attr_reader :cmd + attr_reader :str, :cmd def initialize(cmd, str) @str = str diff --git a/lib/aerospike/exp/exp_bit.rb b/lib/aerospike/exp/exp_bit.rb index 2824d313..30a702a7 100644 --- a/lib/aerospike/exp/exp_bit.rb +++ b/lib/aerospike/exp/exp_bit.rb @@ -15,7 +15,7 @@ # the License. module Aerospike - # Bit expression generator. See {@link Exp}. + # Bit expression generator. See {Exp}. # # The bin expression argument in these methods can be a reference to a bin or the # result of another expression. Expressions that modify bin values are only used @@ -42,7 +42,7 @@ class Exp::Bit # Exp.val(2)) def self.resize(byte_size, resize_flags, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, RESIZE, byte_size, policy.flags, resize_flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that inserts value bytes into byte[] bin at byte_offset and returns byte[]. @@ -60,7 +60,7 @@ def self.resize(byte_size, resize_flags, bin, policy: CDT::BitPolicy::DEFAULT) # Exp.val(2)) def self.insert(byte_offset, value, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, INSERT, byte_offset, value, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that removes bytes from byte[] bin at byte_offset for byte_size and returns byte[]. @@ -78,7 +78,7 @@ def self.insert(byte_offset, value, bin, policy: CDT::BitPolicy::DEFAULT) # Exp.val(2)) def self.remove(byte_offset, byte_size, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, REMOVE, byte_offset, byte_size, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that sets value on byte[] bin at bit_offset for bit_size and returns byte[]. @@ -97,7 +97,7 @@ def self.remove(byte_offset, byte_size, bin, policy: CDT::BitPolicy::DEFAULT) # Exp.val(2)) def self.set(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, SET, bit_offset, bit_size, value, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that performs bitwise "or" on value and byte[] bin at bit_offset for bit_size @@ -111,7 +111,7 @@ def self.set(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) # def self.or(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, OR, bit_offset, bit_size, value, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that performs bitwise "xor" on value and byte[] bin at bit_offset for bit_size @@ -125,7 +125,7 @@ def self.or(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) # def self.xor(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, XOR, bit_offset, bit_size, value, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that performs bitwise "and" on value and byte[] bin at bit_offset for bit_size @@ -139,7 +139,7 @@ def self.xor(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) # def self.and(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, AND, bit_offset, bit_size, value, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that negates byte[] bin starting at bit_offset for bit_size and returns byte[]. @@ -151,7 +151,7 @@ def self.and(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) # def self.not(bit_offset, bit_size, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, NOT, bit_offset, bit_size, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that shifts left byte[] bin starting at bit_offset for bit_size and returns byte[]. @@ -164,7 +164,7 @@ def self.not(bit_offset, bit_size, bin, policy: CDT::BitPolicy::DEFAULT) # def self.lshift(bit_offset, bit_size, shift, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, LSHIFT, bit_offset, bit_size, shift, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that shifts right byte[] bin starting at bit_offset for bit_size and returns byte[]. @@ -177,12 +177,12 @@ def self.lshift(bit_offset, bit_size, shift, bin, policy: CDT::BitPolicy::DEFAUL # def self.rshift(bit_offset, bit_size, shift, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, RSHIFT, bit_offset, bit_size, shift, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that adds value to byte[] bin starting at bit_offset for bit_size and returns byte[]. # BitSize must be <= 64. Signed indicates if bits should be treated as a signed number. - # If add overflows/underflows, {@link BitOverflowAction} is used. + # If add overflows/underflows, {BitOverflowAction} is used. # # bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101] # bit_offset = 24 @@ -192,13 +192,13 @@ def self.rshift(bit_offset, bit_size, shift, bin, policy: CDT::BitPolicy::DEFAUL # bin result = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b10000101] # def self.add(bit_offset, bit_size, value, signed, bit_overflow_action, bin, policy: CDT::BitPolicy::DEFAULT) - bytes = self.pack_math(ADD, policy, bit_offset, bit_size, value, signed, bit_overflow_action) - self.add_write(bin, bytes) + bytes = pack_math(ADD, policy, bit_offset, bit_size, value, signed, bit_overflow_action) + add_write(bin, bytes) end # Create expression that subtracts value from byte[] bin starting at bit_offset for bit_size and returns byte[]. # BitSize must be <= 64. Signed indicates if bits should be treated as a signed number. - # If add overflows/underflows, {@link BitOverflowAction} is used. + # If add overflows/underflows, {BitOverflowAction} is used. # # bin = [0b00000001, 0b01000010, 0b00000011, 0b00000100, 0b00000101] # bit_offset = 24 @@ -208,8 +208,8 @@ def self.add(bit_offset, bit_size, value, signed, bit_overflow_action, bin, poli # bin result = [0b00000001, 0b01000010, 0b00000011, 0b0000011, 0b10000101] # def self.subtract(bit_offset, bit_size, value, signed, bit_overflow_action, bin, policy: CDT::BitPolicy::DEFAULT) - bytes = self.pack_math(SUBTRACT, policy, bit_offset, bit_size, value, signed, bit_overflow_action) - self.add_write(bin, bytes) + bytes = pack_math(SUBTRACT, policy, bit_offset, bit_size, value, signed, bit_overflow_action) + add_write(bin, bytes) end # Create expression that sets value to byte[] bin starting at bit_offset for bit_size and returns byte[]. @@ -223,7 +223,7 @@ def self.subtract(bit_offset, bit_size, value, signed, bit_overflow_action, bin, # def self.set_int(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAULT) bytes = Exp.pack(nil, SET_INT, bit_offset, bit_size, value, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that returns bits from byte[] bin starting at bit_offset for bit_size. @@ -240,7 +240,7 @@ def self.set_int(bit_offset, bit_size, value, bin, policy: CDT::BitPolicy::DEFAU # Exp.val(new byte[] {(byte)0b10000000})) def self.get(bit_offset, bit_size, bin) bytes = Exp.pack(nil, GET, bit_offset, bit_size) - self.add_read(bin, bytes, Exp::Type::BLOB) + add_read(bin, bytes, Exp::Type::BLOB) end # Create expression that returns integer count of set bits from byte[] bin starting at @@ -256,7 +256,7 @@ def self.get(bit_offset, bit_size, bin) # Exp.le(BitExp.count(Exp.val(0), Exp.val(5), Exp.blobBin("a")), Exp.val(2)) def self.count(bit_offset, bit_size, bin) bytes = Exp.pack(nil, COUNT, bit_offset, bit_size) - self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end # Create expression that returns integer bit offset of the first specified value bit in byte[] bin @@ -278,7 +278,7 @@ def self.count(bit_offset, bit_size, bin) # @param bin bin or blob value expression def self.lscan(bit_offset, bit_size, value, bin) bytes = Exp.pack(nil, LSCAN, bit_offset, bit_size, value) - self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end # Create expression that returns integer bit offset of the last specified value bit in byte[] bin @@ -301,7 +301,7 @@ def self.lscan(bit_offset, bit_size, value, bin) # @param bin bin or blob value expression def self.rscan(bit_offset, bit_size, value, bin) bytes = Exp.pack(nil, RSCAN, bit_offset, bit_size, value) - self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end # Create expression that returns integer from byte[] bin starting at bit_offset for bit_size. @@ -317,8 +317,8 @@ def self.rscan(bit_offset, bit_size, value, bin) # # getInt(a) == 16899 # Exp.eq(BitExp.getInt(Exp.val(8), Exp.val(16), false, Exp.blobBin("a")), Exp.val(16899)) def self.get_int(bit_offset, bit_size, signed, bin) - bytes = self.pack_get_int(bit_offset, bit_size, signed) - self.add_read(bin, bytes, Exp::Type::INT) + bytes = pack_get_int(bit_offset, bit_size, signed) + add_read(bin, bytes, Exp::Type::INT) end private diff --git a/lib/aerospike/exp/exp_hll.rb b/lib/aerospike/exp/exp_hll.rb index 16b4c53e..156c4414 100644 --- a/lib/aerospike/exp/exp_hll.rb +++ b/lib/aerospike/exp/exp_hll.rb @@ -15,7 +15,7 @@ # the License. module Aerospike - # HyperLogLog (HLL) expression generator. See {@link Exp}. + # HyperLogLog (HLL) expression generator. See {Exp}. # # The bin expression argument in these methods can be a reference to a bin or the # result of another expression. Expressions that modify bin values are only used @@ -25,14 +25,14 @@ class Exp::HLL # Create expression that creates a new HLL or resets an existing HLL with minhash bits. # - # @param policy write policy, use {@link HLLPolicy#Default} for default + # @param policy write policy, use {HLLPolicy#Default} for default # @param index_bit_count number of index bits. Must be between 4 and 16 inclusive. # @param min_hash_bit_count number of min hash bits. Must be between 4 and 51 inclusive. # Also, index_bit_count + min_hash_bit_count must be <= 64. Optional. # @param bin HLL bin or value expression def self.init(index_bit_count, bin, min_hash_bit_count: Exp.int_val(-1), policy: CDT::HLLPolicy::DEFAULT) bytes = Exp.pack(nil, INIT, index_bit_count, min_hash_bit_count, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that adds values to a HLL set and returns HLL set. If HLL bin does not @@ -45,7 +45,7 @@ def self.init(index_bit_count, bin, min_hash_bit_count: Exp.int_val(-1), policy: # HLLExp.add(HLLPolicy.Default, Exp.val(list), Exp.val(10), Exp.val(20), Exp.hllBin("a"))), # Exp.val(7)) # - # @param policy write policy, use {@link HLLPolicy#Default} for default + # @param policy write policy, use {HLLPolicy#Default} for default # @param list list bin or value expression of values to be added # @param index_bit_count number of index bits expression. Must be between 4 and 16 inclusive. # @param min_hash_bit_count number of min hash bits expression. Must be between 4 and 51 inclusive. @@ -53,7 +53,7 @@ def self.init(index_bit_count, bin, min_hash_bit_count: Exp.int_val(-1), policy: # @param bin HLL bin or value expression def self.add(list, bin, policy: CDT::HLLPolicy::DEFAULT, index_bit_count: Exp.val(-1), min_hash_bit_count: Exp.val(-1)) bytes = Exp.pack(nil, ADD, list, index_bit_count, min_hash_bit_count, policy.flags) - self.add_write(bin, bytes) + add_write(bin, bytes) end # Create expression that returns estimated number of elements in the HLL bin. @@ -63,7 +63,7 @@ def self.add(list, bin, policy: CDT::HLLPolicy::DEFAULT, index_bit_count: Exp.va # Exp.gt(HLLExp.getCount(Exp.hllBin("a")), Exp.val(7)) def self.get_count(bin) bytes = Exp.pack(nil, COUNT) - self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end # Create expression that returns a HLL object that is the union of all specified HLL objects @@ -77,7 +77,7 @@ def self.get_count(bin) # HLLExp.getUnion(Exp.val(list), Exp.hllBin("b")) def self.get_union(list, bin) bytes = Exp.pack(nil, UNION, list) - self.add_read(bin, bytes, Exp::Type::HLL) + add_read(bin, bytes, Exp::Type::HLL) end # Create expression that returns estimated number of elements that would be contained by @@ -91,7 +91,7 @@ def self.get_union(list, bin) # HLLExp.getUnionCount(Exp.val(list), Exp.hllBin("b")) def self.get_union_count(list, bin) bytes = Exp.pack(nil, UNION_COUNT, list) - self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end # Create expression that returns estimated number of elements that would be contained by @@ -105,7 +105,7 @@ def self.get_union_count(list, bin) # HLLExp.getIntersectCount(Exp.val(list), Exp.hllBin("b")) def self.get_intersect_count(list, bin) bytes = Exp.pack(nil, INTERSECT_COUNT, list) - self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end # Create expression that returns estimated similarity of these HLL objects as a @@ -116,7 +116,7 @@ def self.get_intersect_count(list, bin) # Exp.ge(HLLExp.getSimilarity(Exp.hllBin("a"), Exp.hllBin("b")), Exp.val(0.75)) def self.get_similarity(list, bin) bytes = Exp.pack(nil, SIMILARITY, list) - self.add_read(bin, bytes, Exp::Type::FLOAT) + add_read(bin, bytes, Exp::Type::FLOAT) end # Create expression that returns index_bit_count and min_hash_bit_count used to create HLL bin @@ -130,7 +130,7 @@ def self.get_similarity(list, bin) # Exp.val(10)) def self.describe(bin) bytes = Exp.pack(nil, DESCRIBE) - self.add_read(bin, bytes, Exp::Type::LIST) + add_read(bin, bytes, Exp::Type::LIST) end # Create expression that returns one if HLL bin may contain all items in the list. @@ -142,7 +142,7 @@ def self.describe(bin) # Exp.eq(HLLExp.mayContain(Exp.val(list), Exp.hllBin("a")), Exp.val(1)) def self.may_contain(list, bin) bytes = Exp.pack(nil, MAY_CONTAIN, list) - self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end private diff --git a/lib/aerospike/exp/exp_list.rb b/lib/aerospike/exp/exp_list.rb index 70a03007..4985735d 100644 --- a/lib/aerospike/exp/exp_list.rb +++ b/lib/aerospike/exp/exp_list.rb @@ -16,7 +16,7 @@ module Aerospike - # List expression generator. See {@link Exp}. + # List expression generator. See {Exp}. # # The bin expression argument in these methods can be a reference to a bin or the # result of another expression. Expressions that modify bin values are only used @@ -50,74 +50,74 @@ class Exp::List # Create expression that appends value to end of list. def self.append(value, bin, ctx: nil, policy: CDT::ListPolicy::DEFAULT) bytes = Exp.pack(ctx, APPEND, value, policy.order, policy.flags) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that appends list items to end of list. def self.append_items(list, bin, ctx: nil, policy: CDT::ListPolicy::DEFAULT) bytes = Exp.pack(ctx, APPEND_ITEMS, list, policy.order, policy.flags) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that inserts value to specified index of list. def self.insert(index, value, bin, ctx: nil, policy: CDT::ListPolicy::DEFAULT) bytes = Exp.pack(ctx, INSERT, index, value, policy.flags) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that inserts each input list item starting at specified index of list. def self.insert_items(index, list, bin, ctx: nil, policy: CDT::ListPolicy::DEFAULT) bytes = Exp.pack(ctx, INSERT_ITEMS, index, list, policy.flags) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that increments list[index] by value. # Value expression should resolve to a number. def self.increment(index, value, bin, ctx: nil, policy: CDT::ListPolicy::DEFAULT) bytes = Exp.pack(ctx, INCREMENT, index, value, policy.order, policy.flags) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that sets item value at specified index in list. def self.set(index, value, bin, ctx: nil, policy: CDT::ListPolicy::DEFAULT) bytes = Exp.pack(ctx, SET, index, value, policy.flags) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes all items in list. def self.clear(bin, ctx: nil) bytes = Exp.pack(ctx, CLEAR) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that sorts list according to sort_flags. # - # @param sort_flags sort flags. See {@link ListSortFlagsend. + # @param sort_flags sort flags. See {ListSortFlagsend. # @param bin bin or list value expression # @param ctx optional context path for nested CDT def self.sort(sort_flags, bin, ctx: nil) bytes = Exp.pack(ctx, SORT, sort_flags) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes list items identified by value. def self.remove_by_value(value, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_VALUE, CDT::ListReturnType::NONE, value) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes list items identified by values. def self.remove_by_value_list(values, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_VALUE_LIST, CDT::ListReturnType::NONE, values) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes list items identified by value range (value_begin inclusive, value_end exclusive). # If value_begin is nil, the range is less than value_end. If value_end is nil, the range is # greater than equal to value_begin. def self.remove_by_value_range(value_begin, value_end, bin, ctx: nil) - bytes = self.pack_range_operation(REMOVE_BY_VALUE_INTERVAL, CDT::ListReturnType::NONE, value_begin, value_end, ctx) - self.add_write(bin, bytes, ctx) + bytes = pack_range_operation(REMOVE_BY_VALUE_INTERVAL, CDT::ListReturnType::NONE, value_begin, value_end, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes list items nearest to value and greater by relative rank with a count limit if provided. @@ -132,44 +132,44 @@ def self.remove_by_value_range(value_begin, value_end, bin, ctx: nil) # (3,3,7) = [11,15] # (3,-3,2) = [] def self.remove_by_value_relative_rank_range(value, rank, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, REMOVE_BY_VALUE_REL_RANK_RANGE, CDT::ListReturnType::NONE, value, rank, count) - else - bytes = Exp.pack(ctx, REMOVE_BY_VALUE_REL_RANK_RANGE, CDT::ListReturnType::NONE, value, rank) - end - self.add_write(bin, bytes, ctx) + bytes = if count.nil? + Exp.pack(ctx, REMOVE_BY_VALUE_REL_RANK_RANGE, CDT::ListReturnType::NONE, value, rank) + else + Exp.pack(ctx, REMOVE_BY_VALUE_REL_RANK_RANGE, CDT::ListReturnType::NONE, value, rank, count) + end + add_write(bin, bytes, ctx) end # Create expression that removes list item identified by index. def self.remove_by_index(index, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_INDEX, CDT::ListReturnType::NONE, index) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes "count" list items starting at specified index. def self.remove_by_index_range(index, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, REMOVE_BY_INDEX_RANGE, CDT::ListReturnType::NONE, index, count) - else - bytes = Exp.pack(ctx, REMOVE_BY_INDEX_RANGE, CDT::ListReturnType::NONE, index) - end - self.add_write(bin, bytes, ctx) + bytes = if count.nil? + Exp.pack(ctx, REMOVE_BY_INDEX_RANGE, CDT::ListReturnType::NONE, index) + else + Exp.pack(ctx, REMOVE_BY_INDEX_RANGE, CDT::ListReturnType::NONE, index, count) + end + add_write(bin, bytes, ctx) end # Create expression that removes list item identified by rank. def self.remove_by_rank(rank, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_RANK, CDT::ListReturnType::NONE, rank) - self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes "count" list items starting at specified rank. def self.remove_by_rank_range(rank, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, REMOVE_BY_RANK_RANGE, CDT::ListReturnType::NONE, rank, count) - else - bytes = Exp.pack(ctx, REMOVE_BY_RANK_RANGE, CDT::ListReturnType::NONE, rank) - end - self.add_write(bin, bytes, ctx) + bytes = if count.nil? + Exp.pack(ctx, REMOVE_BY_RANK_RANGE, CDT::ListReturnType::NONE, rank) + else + Exp.pack(ctx, REMOVE_BY_RANK_RANGE, CDT::ListReturnType::NONE, rank, count) + end + add_write(bin, bytes, ctx) end # Create expression that returns list size. @@ -180,7 +180,7 @@ def self.remove_by_rank_range(rank, bin, ctx: nil, count: nil) # end def self.size(bin, ctx: nil) bytes = Exp.pack(ctx, SIZE) - self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end # Create expression that selects list items identified by value and returns selected @@ -193,13 +193,13 @@ def self.size(bin, ctx: nil) # Exp.val(0)) # end # - # @param return_type metadata attributes to return. See {@link CDT::ListReturnTypeend + # @param return_type metadata attributes to return. See {CDT::ListReturnType} # @param value search expression # @param bin list bin or list value expression # @param ctx optional context path for nested CDT def self.get_by_value(return_type, value, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_VALUE, return_type, value) - self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects list items identified by value range and returns selected data @@ -210,25 +210,25 @@ def self.get_by_value(return_type, value, bin, ctx: nil) # ListExp.getByValueRange(CDT::ListReturnType::VALUE, Exp.val(10), Exp.val(20), Exp.listBin("a")) # end # - # @param return_type metadata attributes to return. See {@link CDT::ListReturnTypeend + # @param return_type metadata attributes to return. See {CDT::ListReturnType} # @param value_begin begin expression inclusive. If nil, range is less than value_end. # @param value_end end expression exclusive. If nil, range is greater than equal to value_begin. # @param bin bin or list value expression # @param ctx optional context path for nested CDT def self.get_by_value_range(return_type, value_begin, value_end, bin, ctx: nil) - bytes = self.pack_range_operation(GET_BY_VALUE_INTERVAL, return_type, value_begin, value_end, ctx) - self.add_read(bin, bytes, get_value_type(return_type)) + bytes = pack_range_operation(GET_BY_VALUE_INTERVAL, return_type, value_begin, value_end, ctx) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects list items identified by values and returns selected data # specified by return_type. def self.get_by_value_list(return_type, values, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_VALUE_LIST, return_type, values) - self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects list items nearest to value and greater by relative rank with a count limit - # and returns selected data specified by return_type (See {@link CDT::ListReturnTypeend). + # and returns selected data specified by return_type (See {CDT::ListReturnType}). # # Examples for ordered list [0,4,5,9,11,15]: # @@ -240,12 +240,12 @@ def self.get_by_value_list(return_type, values, bin, ctx: nil) # (3,3,7) = [11,15] # (3,-3,2) = [] def self.get_by_value_relative_rank_range(return_type, value, rank, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, GET_BY_VALUE_REL_RANK_RANGE, return_type, value, rank, count) - else - bytes = Exp.pack(ctx, GET_BY_VALUE_REL_RANK_RANGE, return_type, value, rank) - end - self.add_read(bin, bytes, get_value_type(return_type)) + bytes = if count.nil? + Exp.pack(ctx, GET_BY_VALUE_REL_RANK_RANGE, return_type, value, rank) + else + Exp.pack(ctx, GET_BY_VALUE_REL_RANK_RANGE, return_type, value, rank, count) + end + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects list item identified by index and returns @@ -258,32 +258,32 @@ def self.get_by_value_relative_rank_range(return_type, value, rank, bin, ctx: ni # Exp.val(5)) # end # - # @param return_type metadata attributes to return. See {@link CDT::ListReturnTypeend + # @param return_type metadata attributes to return. See {CDT::ListReturnType} # @param value_type expected type of value # @param index list index expression # @param bin list bin or list value expression # @param ctx optional context path for nested CDT def self.get_by_index(return_type, value_type, index, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_INDEX, return_type, index) - self.add_read(bin, bytes, value_type) + add_read(bin, bytes, value_type) end # Create expression that selects list items starting at specified index to the end of list - # and returns selected data specified by return_type (See {@link CDT::ListReturnTypeend). + # and returns selected data specified by return_type (See {CDT::ListReturnType}). def self.get_by_index_range(return_type, index, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index) - self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects "count" list items starting at specified index - # and returns selected data specified by return_type (See {@link CDT::ListReturnTypeend). + # and returns selected data specified by return_type (See {CDT::ListReturnType}). def self.get_by_index_range(return_type, index, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index, count) - else - bytes = Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index) - end - self.add_read(bin, bytes, get_value_type(return_type)) + bytes = if count.nil? + Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index) + else + Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index, count) + end + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects list item identified by rank and returns selected @@ -294,32 +294,32 @@ def self.get_by_index_range(return_type, index, bin, ctx: nil, count: nil) # ListExp.getByRank(CDT::ListReturnType::VALUE, Type.STRING, Exp.val(0), Exp.listBin("a")) # end # - # @param return_type metadata attributes to return. See {@link CDT::ListReturnTypeend + # @param return_type metadata attributes to return. See {CDT::ListReturnType} # @param value_type expected type of value # @param rank rank expression # @param bin list bin or list value expression # @param ctx optional context path for nested CDT def self.get_by_rank(return_type, value_type, rank, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_RANK, return_type, rank) - self.add_read(bin, bytes, value_type) + add_read(bin, bytes, value_type) end # Create expression that selects list items starting at specified rank to the last ranked item - # and returns selected data specified by return_type (See {@link CDT::ListReturnTypeend). + # and returns selected data specified by return_type (See {CDT::ListReturnType}). def self.get_by_rank_range(return_type, rank, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank) - self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects "count" list items starting at specified rank and returns - # selected data specified by return_type (See {@link CDT::ListReturnTypeend). + # selected data specified by return_type (See {CDT::ListReturnType}). def self.get_by_rank_range(return_type, rank, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank, count) - else - bytes = Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank) - end - self.add_read(bin, bytes, get_value_type(return_type)) + bytes = if count.nil? + Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank) + else + Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank, count) + end + add_read(bin, bytes, get_value_type(return_type)) end private @@ -336,7 +336,7 @@ def self.get_by_rank_range(return_type, rank, bin, ctx: nil, count: nil) SIZE = 16 GET_BY_INDEX = 19 GET_BY_RANK = 21 - GET_BY_VALUE = 22 # GET_ALL_BY_VALUE on server + GET_BY_VALUE = 22 # GET_ALL_BY_VALUE on server GET_BY_VALUE_LIST = 23 GET_BY_INDEX_RANGE = 24 GET_BY_VALUE_INTERVAL = 25 @@ -352,11 +352,11 @@ def self.get_by_rank_range(return_type, rank, bin, ctx: nil, count: nil) REMOVE_BY_VALUE_REL_RANK_RANGE = 40 def self.add_write(bin, bytes, ctx) - if ctx.to_a.empty? - ret_type = Exp::Type::LIST - else - ret_type = ((ctx[0].id & 0x10) == 0) ? Exp::Type::MAP : Exp::Type::LIST - end + ret_type = if ctx.to_a.empty? + Exp::Type::LIST + else + (ctx[0].id & 0x10) == 0 ? Exp::Type::MAP : Exp::Type::LIST + end Exp::Module.new(bin, bytes, ret_type, MODULE | Exp::MODIFY) end @@ -379,14 +379,12 @@ def self.pack_range_operation(command, return_type, value_begin, value_end, ctx) packer.write(command) packer.write(return_type) - unless value_begin.nil? - if value_begin.is_a?(Exp) - value_begin.pack(packer) - else - Value.of(value_begin).pack(packer) - end - else + if value_begin.nil? packer.write(nil) + elsif value_begin.is_a?(Exp) + value_begin.pack(packer) + else + Value.of(value_begin).pack(packer) end unless value_end.nil? diff --git a/lib/aerospike/exp/exp_map.rb b/lib/aerospike/exp/exp_map.rb index 0cae9958..473cfade 100644 --- a/lib/aerospike/exp/exp_map.rb +++ b/lib/aerospike/exp/exp_map.rb @@ -15,7 +15,7 @@ # the License. module Aerospike - # Map expression generator. See {@link Exp}. + # Map expression generator. See {Exp}. # # The bin expression argument in these methods can be a reference to a bin or the # result of another expression. Expressions that modify bin values are only used @@ -80,24 +80,22 @@ def self.put(key, value, bin, ctx: nil, policy: CDT::MapPolicy::DEFAULT) value.pack(packer) packer.write(policy.attributes) packer.write(policy.flags) + elsif policy.item_command == REPLACE + Exp.pack_ctx(packer, ctx) + packer.write_array_header(3) + packer.write(policy.item_command) + key.pack(packer) + value.pack(packer) + # Replace doesn't allow map attributes because it does not create on non-existing key. else - if policy.item_command == REPLACE - # Replace doesn't allow map attributes because it does not create on non-existing key. - Exp.pack_ctx(packer, ctx) - packer.write_array_header(3) - packer.write(policy.item_command) - key.pack(packer) - value.pack(packer) - else Exp.pack_ctx(packer, ctx) packer.write_array_header(4) packer.write(policy.item_command) key.pack(packer) value.pack(packer) packer.write(policy.attributes) - end end - self.add_write(bin, packer.bytes, ctx) + add_write(bin, packer.bytes, ctx) end end @@ -111,22 +109,20 @@ def self.put_items(map, bin, ctx: nil, policy: CDT::MapPolicy::DEFAULT) map.pack(packer) packer.write(policy.attributes) packer.write(policy.flags) + elsif policy.items_command == REPLACE_ITEMS + Exp.pack_ctx(packer, ctx) + packer.write_array_header(2) + packer.write(policy.items_command) + map.pack(packer) + # Replace doesn't allow map attributes because it does not create on non-existing key. else - if policy.items_command == REPLACE_ITEMS - # Replace doesn't allow map attributes because it does not create on non-existing key. - Exp.pack_ctx(packer, ctx) - packer.write_array_header(2) - packer.write(policy.items_command) - map.pack(packer) - else Exp.pack_ctx(packer, ctx) packer.write_array_header(3) packer.write(policy.items_command) map.pack(packer) packer.write(policy.attributes) - end end - self.add_write(bin, packer.bytes, ctx) + add_write(bin, packer.bytes, ctx) end end @@ -134,25 +130,25 @@ def self.put_items(map, bin, ctx: nil, policy: CDT::MapPolicy::DEFAULT) # Valid only for numbers. def self.increment(key, incr, bin, ctx: nil, policy: CDT::MapPolicy::DEFAULT) bytes = Exp.pack(ctx, INCREMENT, key, incr, policy.attributes) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes all items in map. def self.clear(bin, ctx: nil) bytes = Exp.pack(ctx, CLEAR) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map item identified by key. def self.remove_by_key(key, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_KEY, CDT::MapReturnType::NONE, key) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map items identified by keys. def self.remove_by_key_list(keys, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_KEY_LIST, CDT::MapReturnType::NONE, keys) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map items identified by key range (key_begin inclusive, key_end exclusive). @@ -160,7 +156,7 @@ def self.remove_by_key_list(keys, bin, ctx: nil) # If key_end is nil, the range is greater than equal to key_begin. def self.remove_by_key_range(key_begin, key_end, bin, ctx: nil) bytes = Exp::List.pack_range_operation(REMOVE_BY_KEY_INTERVAL, CDT::MapReturnType::NONE, key_begin, key_end, ctx) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map items nearest to key and greater by index with a count limit if provided. @@ -174,24 +170,24 @@ def self.remove_by_key_range(key_begin, key_end, bin, ctx: nil) # (3,2,1) = [{9=10}] # (3,-2,2) = [{0=17}] def self.remove_by_key_relative_index_range(key, index, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, REMOVE_BY_KEY_REL_INDEX_RANGE, CDT::MapReturnType::NONE, key, index, count) - else - bytes = Exp.pack(ctx, REMOVE_BY_KEY_REL_INDEX_RANGE, CDT::MapReturnType::NONE, key, index) - end - return self.add_write(bin, bytes, ctx) + bytes = if count.nil? + Exp.pack(ctx, REMOVE_BY_KEY_REL_INDEX_RANGE, CDT::MapReturnType::NONE, key, index) + else + Exp.pack(ctx, REMOVE_BY_KEY_REL_INDEX_RANGE, CDT::MapReturnType::NONE, key, index, count) + end + add_write(bin, bytes, ctx) end # Create expression that removes map items identified by value. def self.remove_by_value(value, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_VALUE, CDT::MapReturnType::NONE, value) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map items identified by values. def self.remove_by_value_list(values, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_VALUE_LIST, CDT::MapReturnType::NONE, values) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map items identified by value range (valueBegin inclusive, valueEnd exclusive). @@ -199,7 +195,7 @@ def self.remove_by_value_list(values, bin, ctx: nil) # If valueEnd is nil, the range is greater than equal to valueBegin. def self.remove_by_value_range(valueBegin, valueEnd, bin, ctx: nil) bytes = Exp::List.pack_range_operation(REMOVE_BY_VALUE_INTERVAL, CDT::MapReturnType::NONE, valueBegin, valueEnd, ctx) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map items nearest to value and greater by relative rank. @@ -211,7 +207,7 @@ def self.remove_by_value_range(valueBegin, valueEnd, bin, ctx: nil) # (11,-1) = [{9=10},{5=15},{0=17}] def self.remove_by_value_relative_rank_range(value, rank, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_VALUE_REL_RANK_RANGE, CDT::MapReturnType::NONE, value, rank) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map items nearest to value and greater by relative rank with a count limit. @@ -223,40 +219,40 @@ def self.remove_by_value_relative_rank_range(value, rank, bin, ctx: nil) # (11,-1,1) = [{9=10}] def self.remove_by_value_relative_rank_range(value, rank, count, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_VALUE_REL_RANK_RANGE, CDT::MapReturnType::NONE, value, rank, count) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes map item identified by index. def self.remove_by_index(index, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_INDEX, CDT::MapReturnType::NONE, index) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes "count" map items starting at specified index limited by count if provided. def self.remove_by_index_range(index, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, REMOVE_BY_INDEX_RANGE, CDT::MapReturnType::NONE, index, count) - else - bytes = Exp.pack(ctx, REMOVE_BY_INDEX_RANGE, CDT::MapReturnType::NONE, index) - end - return self.add_write(bin, bytes, ctx) + bytes = if count.nil? + Exp.pack(ctx, REMOVE_BY_INDEX_RANGE, CDT::MapReturnType::NONE, index) + else + Exp.pack(ctx, REMOVE_BY_INDEX_RANGE, CDT::MapReturnType::NONE, index, count) + end + add_write(bin, bytes, ctx) end # Create expression that removes map item identified by rank. def self.remove_by_rank(rank, bin, ctx: nil) bytes = Exp.pack(ctx, REMOVE_BY_RANK, CDT::MapReturnType::NONE, rank) - return self.add_write(bin, bytes, ctx) + add_write(bin, bytes, ctx) end # Create expression that removes "count" map items starting at specified rank. If count is not provided, # all items until the last ranked item will be removed def self.remove_by_rank_range(rank, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, REMOVE_BY_RANK_RANGE, CDT::MapReturnType::NONE, rank, count) - else - bytes = Exp.pack(ctx, REMOVE_BY_RANK_RANGE, CDT::MapReturnType::NONE, rank) - end - return self.add_write(bin, bytes, ctx) + bytes = if count.nil? + Exp.pack(ctx, REMOVE_BY_RANK_RANGE, CDT::MapReturnType::NONE, rank) + else + Exp.pack(ctx, REMOVE_BY_RANK_RANGE, CDT::MapReturnType::NONE, rank, count) + end + add_write(bin, bytes, ctx) end # Create expression that returns list size. @@ -266,7 +262,7 @@ def self.remove_by_rank_range(rank, bin, ctx: nil, count: nil) # Exp.gt(MapExp.size(mapBin("a")), Exp.val(7)) def self.size(bin, ctx: nil) bytes = Exp.pack(ctx, SIZE) - return self.add_read(bin, bytes, Exp::Type::INT) + add_read(bin, bytes, Exp::Type::INT) end # Create expression that selects map item identified by key and returns selected data @@ -278,35 +274,35 @@ def self.size(bin, ctx: nil) # MapExp.getByKey(CDT::MapReturnType::COUNT, Exp::Type::INT, Exp.val("B"), Exp.mapBin("a")), # Exp.val(0)) # - # @param return_type metadata attributes to return. See {@link MapReturnType} + # @param return_type metadata attributes to return. See {MapReturnType} # @param value_type expected type of return value # @param key map key expression # @param bin bin or map value expression # @param ctx optional context path for nested CDT def self.get_by_key(return_type, value_type, key, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_KEY, return_type, key) - return self.add_read(bin, bytes, value_type) + add_read(bin, bytes, value_type) end # Create expression that selects map items identified by key range (key_begin inclusive, key_end exclusive). # If key_begin is nil, the range is less than key_end. # If key_end is nil, the range is greater than equal to key_begin. # - # Expression returns selected data specified by return_type (See {@link MapReturnType}). + # Expression returns selected data specified by return_type (See {MapReturnType}). def self.get_by_key_range(return_type, key_begin, key_end, bin, ctx: nil) bytes = Exp::List.pack_range_operation(GET_BY_KEY_INTERVAL, return_type, key_begin, key_end, ctx) - return self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map items identified by keys and returns selected data specified by - # return_type (See {@link MapReturnType}). + # return_type (See {MapReturnType}). def self.get_by_key_list(return_type, keys, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_KEY_LIST, return_type, keys) - return self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map items nearest to key and greater by index with a coun. - # Expression returns selected data specified by return_type (See {@link MapReturnType}). + # Expression returns selected data specified by return_type (See {MapReturnType}). # # Examples for ordered map [{0=17},{4=2},{5=15},{9=10}]: # @@ -318,11 +314,11 @@ def self.get_by_key_list(return_type, keys, bin, ctx: nil) # (3,-2) = [{0=17},{4=2},{5=15},{9=10}] def self.get_by_key_relative_index_range(return_type, key, index, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_KEY_REL_INDEX_RANGE, return_type, key, index) - return self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map items nearest to key and greater by index with a count limit if provided. - # Expression returns selected data specified by return_type (See {@link MapReturnType}). + # Expression returns selected data specified by return_type (See {MapReturnType}). # # Examples for ordered map [{0=17},{4=2},{5=15},{9=10}]: # @@ -333,12 +329,12 @@ def self.get_by_key_relative_index_range(return_type, key, index, bin, ctx: nil) # (3,2,1) = [{9=10}] # (3,-2,2) = [{0=17}] def self.get_by_key_relative_index_range(return_type, key, index, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, GET_BY_KEY_REL_INDEX_RANGE, return_type, key, index, count) - else - bytes = Exp.pack(ctx, GET_BY_KEY_REL_INDEX_RANGE, return_type, key, index) - end - return self.add_read(bin, bytes, get_value_type(return_type)) + bytes = if count.nil? + Exp.pack(ctx, GET_BY_KEY_REL_INDEX_RANGE, return_type, key, index) + else + Exp.pack(ctx, GET_BY_KEY_REL_INDEX_RANGE, return_type, key, index, count) + end + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map items identified by value and returns selected data @@ -350,34 +346,34 @@ def self.get_by_key_relative_index_range(return_type, key, index, bin, ctx: nil, # MapExp.getByValue(CDT::MapReturnType::COUNT, Exp.val("BBB"), Exp.mapBin("a")), # Exp.val(0)) # - # @param return_type metadata attributes to return. See {@link MapReturnType} + # @param return_type metadata attributes to return. See {MapReturnType} # @param value value expression # @param bin bin or map value expression # @param ctx optional context path for nested CDT def self.get_by_value(return_type, value, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_VALUE, return_type, value) - return self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map items identified by value range (valueBegin inclusive, valueEnd exclusive) # If valueBegin is nil, the range is less than valueEnd. # If valueEnd is nil, the range is greater than equal to valueBegin. # - # Expression returns selected data specified by return_type (See {@link MapReturnType}). + # Expression returns selected data specified by return_type (See {MapReturnType}). def self.get_by_value_range(return_type, valueBegin, valueEnd, bin, ctx: nil) bytes = Exp::List.pack_range_operation(GET_BY_VALUE_INTERVAL, return_type, valueBegin, valueEnd, ctx) - return self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map items identified by values and returns selected data specified by - # return_type (See {@link MapReturnType}). + # return_type (See {MapReturnType}). def self.get_by_value_list(return_type, values, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_VALUE_LIST, return_type, values) - return self.add_read(bin, bytes, get_value_type(return_type)) + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map items nearest to value and greater by relative rank (with a count limit if passed). - # Expression returns selected data specified by return_type (See {@link MapReturnType}). + # Expression returns selected data specified by return_type (See {MapReturnType}). # # Examples for map [{4=2},{9=10},{5=15},{0=17}]: # @@ -385,48 +381,48 @@ def self.get_by_value_list(return_type, values, bin, ctx: nil) # (11,1) = [{0=17}] # (11,-1) = [{9=10},{5=15},{0=17}] def self.get_by_value_relative_rank_range(return_type, value, rank, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, GET_BY_VALUE_REL_RANK_RANGE, return_type, value, rank, count) - else - bytes = Exp.pack(ctx, GET_BY_VALUE_REL_RANK_RANGE, return_type, value, rank) - end - return self.add_read(bin, bytes, get_value_type(return_type)) + bytes = if count.nil? + Exp.pack(ctx, GET_BY_VALUE_REL_RANK_RANGE, return_type, value, rank) + else + Exp.pack(ctx, GET_BY_VALUE_REL_RANK_RANGE, return_type, value, rank, count) + end + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map item identified by index and returns selected data specified by - # return_type (See {@link MapReturnType}). + # return_type (See {MapReturnType}). def self.get_by_index(return_type, value_type, index, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_INDEX, return_type, index) - return self.add_read(bin, bytes, value_type) + add_read(bin, bytes, value_type) end # Create expression that selects map items starting at specified index to the end of map and returns selected - # data specified by return_type (See {@link MapReturnType}) limited by count if provided. + # data specified by return_type (See {MapReturnType}) limited by count if provided. def self.get_by_index_range(return_type, index, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index, count) - else - bytes = Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index) - end - return self.add_read(bin, bytes, get_value_type(return_type)) + bytes = if count.nil? + Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index) + else + Exp.pack(ctx, GET_BY_INDEX_RANGE, return_type, index, count) + end + add_read(bin, bytes, get_value_type(return_type)) end # Create expression that selects map item identified by rank and returns selected data specified by - # return_type (See {@link MapReturnType}). + # return_type (See {MapReturnType}). def self.get_by_rank(return_type, value_type, rank, bin, ctx: nil) bytes = Exp.pack(ctx, GET_BY_RANK, return_type, rank) - return self.add_read(bin, bytes, value_type) + add_read(bin, bytes, value_type) end # Create expression that selects map items starting at specified rank to the last ranked item and - # returns selected data specified by return_type (See {@link MapReturnType}). + # returns selected data specified by return_type (See {MapReturnType}). def self.get_by_rank_range(return_type, rank, bin, ctx: nil, count: nil) - unless count.nil? - bytes = Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank, count) - else - bytes = Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank) - end - return self.add_read(bin, bytes, get_value_type(return_type)) + bytes = if count.nil? + Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank) + else + Exp.pack(ctx, GET_BY_RANK_RANGE, return_type, rank, count) + end + add_read(bin, bytes, get_value_type(return_type)) end private @@ -454,7 +450,7 @@ def self.get_by_rank_range(return_type, rank, bin, ctx: nil, count: nil) GET_BY_KEY = 97 GET_BY_INDEX = 98 GET_BY_RANK = 100 - GET_BY_VALUE = 102; # GET_ALL_BY_VALUE on server + GET_BY_VALUE = 102; # GET_ALL_BY_VALUE on server GET_BY_KEY_INTERVAL = 103 GET_BY_INDEX_RANGE = 104 GET_BY_VALUE_INTERVAL = 105 @@ -465,11 +461,11 @@ def self.get_by_rank_range(return_type, rank, bin, ctx: nil, count: nil) GET_BY_VALUE_REL_RANK_RANGE = 110 def self.add_write(bin, bytes, ctx) - if ctx.to_a.empty? - ret_type = Exp::Type::MAP - else - ret_type = ((ctx[0].id & 0x10) == 0) ? Exp::Type::MAP : Exp::Type::LIST - end + ret_type = if ctx.to_a.empty? + Exp::Type::MAP + else + (ctx[0].id & 0x10) == 0 ? Exp::Type::MAP : Exp::Type::LIST + end Exp::Module.new(bin, bytes, ret_type, MODULE | Exp::MODIFY) end @@ -487,7 +483,7 @@ def self.get_value_type(return_type) if t == CDT::MapReturnType::KEY_VALUE return Exp::Type::MAP end - return Exp::Type::LIST + Exp::Type::LIST end end # class MapExp end # module Aerospike diff --git a/lib/aerospike/exp/operation.rb b/lib/aerospike/exp/operation.rb index 7cfc6104..3c6e70dd 100644 --- a/lib/aerospike/exp/operation.rb +++ b/lib/aerospike/exp/operation.rb @@ -24,7 +24,7 @@ class Exp::Operation # # @param bin_name name of bin to store expression result # @param exp expression to evaluate - # @param flags expression write flags. See {@link Exp::WriteFlags} + # @param flags expression write flags. See {Exp::WriteFlags} def self.write(bin_name, exp, flags = Aerospike::Exp::WriteFlags::DEFAULT) create_operation(Aerospike::Operation::EXP_MODIFY, bin_name, exp, flags) end @@ -36,7 +36,7 @@ def self.write(bin_name, exp, flags = Aerospike::Exp::WriteFlags::DEFAULT) # @param name variable name of read expression result. This name can be used as the # bin name when retrieving bin results from the record. # @param exp expression to evaluate - # @param flags expression read flags. See {@link Exp::ExpReadFlags} + # @param flags expression read flags. See {Exp::ExpReadFlags} def self.read(name, exp, flags = Aerospike::Exp::ReadFlags::DEFAULT) create_operation(Aerospike::Operation::EXP_READ, name, exp, flags) end diff --git a/lib/aerospike/operation.rb b/lib/aerospike/operation.rb index b72fe388..944bc5c5 100644 --- a/lib/aerospike/operation.rb +++ b/lib/aerospike/operation.rb @@ -81,6 +81,7 @@ def self.delete Operation.new(DELETE) end + # :nodoc: def is_write? case @op_type when READ diff --git a/lib/aerospike/policy/batch_delete_policy.rb b/lib/aerospike/policy/batch_delete_policy.rb index 8372cdee..2978d1e6 100644 --- a/lib/aerospike/policy/batch_delete_policy.rb +++ b/lib/aerospike/policy/batch_delete_policy.rb @@ -21,11 +21,11 @@ class BatchDeletePolicy attr_accessor :filter_exp, :commit_level, :generation_policy, :generation, :durable_delete, :send_key def initialize(opt = {}) - # Optional expression filter. If filterExp exists and evaluates to false, the specific batch key - # request is not performed and {@link com.aerospike.client.BatchRecord#result_code} is set to - # {@link com.aerospike.client.ResultCode#FILTERED_OUT}. + # Optional expression filter. If filter_exp exists and evaluates to false, the specific batch key + # request is not performed and {BatchRecord#result_code} is set to + # {ResultCode#FILTERED_OUT}. # - # If exists, this filter overrides the batch parent filter {@link com.aerospike.client.policy.Policy#filter_exp} + # If exists, this filter overrides the batch parent filter {Policy#filter_exp} # for the specific key in batch commands that allow a different policy per key. # Otherwise, this filter is ignored. # diff --git a/lib/aerospike/policy/batch_policy.rb b/lib/aerospike/policy/batch_policy.rb index b9233413..4c9d9d96 100644 --- a/lib/aerospike/policy/batch_policy.rb +++ b/lib/aerospike/policy/batch_policy.rb @@ -63,8 +63,8 @@ def initialize(opt={}) # other nodes will continue to be processed. # # If false, the server will stop the batch to its node on most key specific errors. - # The exceptions are {@link com.aerospike.client.ResultCode#KEY_NOT_FOUND_ERROR} and - # {@link com.aerospike.client.ResultCode#FILTERED_OUT} which never stop the batch. + # The exceptions are {ResultCode#KEY_NOT_FOUND_ERROR} and + # {ResultCode#FILTERED_OUT} which never stop the batch. # The client will stop the entire batch on node specific errors. The client will # not stop the entire batch commands run in parallel. # diff --git a/lib/aerospike/policy/batch_read_policy.rb b/lib/aerospike/policy/batch_read_policy.rb index 309534b3..c61160e0 100644 --- a/lib/aerospike/policy/batch_read_policy.rb +++ b/lib/aerospike/policy/batch_read_policy.rb @@ -23,11 +23,11 @@ class BatchReadPolicy attr_accessor :filter_exp def initialize(opt={}) - # Optional expression filter. If filterExp exists and evaluates to false, the specific batch key - # request is not performed and {@link com.aerospike.client.BatchRecord#result_code} is set to - # {@link com.aerospike.client.ResultCode#FILTERED_OUT}. + # Optional expression filter. If filter_exp exists and evaluates to false, the specific batch key + # request is not performed and {BatchRecord#result_code} is set to + # {ResultCode#FILTERED_OUT}. # - # If exists, this filter overrides the batch parent filter {@link com.aerospike.client.policy.Policy#filter_exp} + # If exists, this filter overrides the batch parent filter {Policy#filter_exp} # for the specific key in batch commands that allow a different policy per key. # Otherwise, this filter is ignored. # diff --git a/lib/aerospike/policy/batch_udf_policy.rb b/lib/aerospike/policy/batch_udf_policy.rb index f1f014b2..42c54ed7 100644 --- a/lib/aerospike/policy/batch_udf_policy.rb +++ b/lib/aerospike/policy/batch_udf_policy.rb @@ -26,11 +26,11 @@ class BatchUDFPolicy alias expiration= ttl= def initialize(opt={}) - # Optional expression filter. If filterExp exists and evaluates to false, the specific batch key - # request is not performed and {@link com.aerospike.client.BatchRecord#resultCode} is set to - # {@link com.aerospike.client.ResultCode#FILTERED_OUT}. + # Optional expression filter. If filter_exp exists and evaluates to false, the specific batch key + # request is not performed and {BatchRecord#resultCode} is set to + # {ResultCode#FILTERED_OUT}. # - # If exists, this filter overrides the batch parent filter {@link com.aerospike.client.policy.Policy#filterExp} + # If exists, this filter overrides the batch parent filter {Policy#filter_exp} # for the specific key in batch commands that allow a different policy per key. # Otherwise, this filter is ignored. # diff --git a/lib/aerospike/policy/batch_write_policy.rb b/lib/aerospike/policy/batch_write_policy.rb index c17e56c5..dfcbb1b0 100644 --- a/lib/aerospike/policy/batch_write_policy.rb +++ b/lib/aerospike/policy/batch_write_policy.rb @@ -29,11 +29,11 @@ class BatchWritePolicy alias expiration= ttl= def initialize(opt={}) - # Optional expression filter. If filterExp exists and evaluates to false, the specific batch key - # request is not performed and {@link com.aerospike.client.BatchRecord#resultCode} is set to - # {@link com.aerospike.client.ResultCode#FILTERED_OUT}. + # Optional expression filter. If filter_exp exists and evaluates to false, the specific batch key + # request is not performed and {BatchRecord#result_code} is set to + # {ResultCode#FILTERED_OUT}. # - # If exists, this filter overrides the batch parent filter {@link com.aerospike.client.policy.Policy#filter_exp} + # If exists, this filter overrides the batch parent filter {Policy#filter_exp} # for the specific key in batch commands that allow a different policy per key. # Otherwise, this filter is ignored. # diff --git a/lib/aerospike/policy/policy.rb b/lib/aerospike/policy/policy.rb index 69edc810..8e4c9c54 100644 --- a/lib/aerospike/policy/policy.rb +++ b/lib/aerospike/policy/policy.rb @@ -31,7 +31,7 @@ def initialize(opt = {}) # Container object for transaction policy attributes used in all database # operation calls. - # Optional expression filter. If filterExp exists and evaluates to false, the + # Optional expression filter. If filter_exp exists and evaluates to false, the # transaction is ignored. # # Default: nil From 8bc81889d199dbd95e27c9262f8c202e8861ac1d Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Mon, 12 Aug 2024 08:03:50 +0200 Subject: [PATCH 05/13] Reduce testing targets --- .github/workflows/development.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 122c34c3..698d4719 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -11,10 +11,10 @@ jobs: matrix: os: - ubuntu-latest - - macos-latest + #- macos-latest ruby: - - "2.6" + #- "2.6" #- "2.7" #- "3.0" #- "3.1" From 612f59505e909f7df95e9b3dbaaa3a653cb94346 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Mon, 12 Aug 2024 16:16:10 +0200 Subject: [PATCH 06/13] [CLIENT-3072] Fix an issue where statement.return_data is not respected --- lib/aerospike/command/command.rb | 3 ++- spec/aerospike/scan_spec.rb | 18 +++++++++--------- spec/aerospike/udf_spec.rb | 6 +++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/aerospike/command/command.rb b/lib/aerospike/command/command.rb index 03da4bcb..58a43348 100644 --- a/lib/aerospike/command/command.rb +++ b/lib/aerospike/command/command.rb @@ -633,7 +633,8 @@ def set_query(cluster, policy, statement, background, node_partitions) if statement.function_name write_field_header(1, FieldType::UDF_OP) - @data_offset += @data_buffer.write_byte(1, @data_offset) + ret_marker = statement.return_data ? 1 : 2 + @data_offset += @data_buffer.write_byte(ret_marker, @data_offset) write_field_string(statement.package_name, FieldType::UDF_PACKAGE_NAME) write_field_string(statement.function_name, FieldType::UDF_FUNCTION) write_field_string(function_arg_buffer, FieldType::UDF_ARGLIST) diff --git a/spec/aerospike/scan_spec.rb b/spec/aerospike/scan_spec.rb index 3d02b4d4..eb7f5f14 100644 --- a/spec/aerospike/scan_spec.rb +++ b/spec/aerospike/scan_spec.rb @@ -23,7 +23,7 @@ let(:client) { Support.client } before :all do - @namespace = "test" + @namespace = Support.namespace @set = "scan1000" @record_count = 1000 @record_count.times do |i| @@ -31,8 +31,8 @@ bin_map = { 'bin1' => "value#{i}", 'bin2' => i, - 'bin4' => ['value4', {'map1' => 'map val'}], - 'bin5' => {'value5' => [124, "string value"]}, + 'bin4' => ['value4', { 'map1' => 'map val' }], + 'bin5' => { 'value5' => [124, "string value"] } } Support.client.put(key, bin_map, :send_key => true) end @@ -53,7 +53,7 @@ def scan_method(type, compressed, bin_names=[], ops={}) [true, false].each do |compressed| [:single_node, :multiple_nodes].each do |type| - context "#{type.to_s}" do + context "#{type}" do it "should return all records with all bins" do rs_list = scan_method(type, compressed, nil, :record_queue_size => 10) @@ -94,7 +94,7 @@ def scan_method(type, compressed, bin_names=[], ops={}) end # it it "should return only the selected bins" do - rs_list = scan_method(type, compressed, ['bin1', 'bin2'], :record_queue_size => 10) + rs_list = scan_method(type, compressed, %w[bin1 bin2], :record_queue_size => 10) count = 0 rs_list.each do |rs| @@ -127,14 +127,14 @@ def scan_method(type, compressed, bin_names=[], ops={}) rs_list.each do |rs| sleep(1) # fill the queue to make sure deadlock doesn't happen rs.cancel - expect {rs.next_record}.to raise_exception(Aerospike::ResultCode.message(Aerospike::ResultCode::SCAN_TERMINATED)) + expect { rs.next_record }.to raise_exception(Aerospike::ResultCode.message(Aerospike::ResultCode::SCAN_TERMINATED)) end rs_list = scan_method(type, compressed) rs_list.each do |rs| rs = rs_list.first rs.cancel - expect {rs.next_record}.to raise_exception(Aerospike::ResultCode.message(Aerospike::ResultCode::SCAN_TERMINATED)) + expect { rs.next_record }.to raise_exception(Aerospike::ResultCode.message(Aerospike::ResultCode::SCAN_TERMINATED)) end end # it @@ -146,12 +146,12 @@ def scan_method(type, compressed, bin_names=[], ops={}) i = 0 rs.each do |rec| i +=1 - break if (i == 15) + break if i == 15 end expect(i).to eq 15 rs.cancel - expect {rs.next_record}.to raise_exception(Aerospike::ResultCode.message(Aerospike::ResultCode::SCAN_TERMINATED)) + expect { rs.next_record }.to raise_exception(Aerospike::ResultCode.message(Aerospike::ResultCode::SCAN_TERMINATED)) end end # it diff --git a/spec/aerospike/udf_spec.rb b/spec/aerospike/udf_spec.rb index ca6dfff4..90af6e5a 100644 --- a/spec/aerospike/udf_spec.rb +++ b/spec/aerospike/udf_spec.rb @@ -145,14 +145,14 @@ end it "should execute a UDF on all records" do - ns = 'test' + ns = Support.namespace set = Support.rand_string(10) div = 2 number_of_records = 100 - number_of_records.times do |i| + number_of_records.times do key = Support.gen_random_key(50, { :set => set }) - bin1 = Aerospike::Bin.new('bin1', (i + 1) * div) + bin1 = Aerospike::Bin.new('bin1', div) bin2 = Aerospike::Bin.new('bin2', -1) client.put(key, [bin1, bin2]) end From b34c833b8e194a5dd5479a2eb48cb2b5d4523d69 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 14 Aug 2024 10:31:16 +0200 Subject: [PATCH 07/13] Updated the Github Actions --- .github/workflows/development.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 698d4719..f288b32b 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -14,20 +14,16 @@ jobs: #- macos-latest ruby: - #- "2.6" - #- "2.7" - #- "3.0" - #- "3.1" - #- "3.2" + - "2.6" - "3.3" experimental: [false] env: [""] - include: - - os: ubuntu - ruby: head - experimental: true + # include: + # - os: ubuntu + # ruby: head + # experimental: true steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 From f27b6284bf5efcba9e7669e3a79c922b4d1ef356 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 14 Aug 2024 11:12:05 +0200 Subject: [PATCH 08/13] [CLIENT-2308] Add Exp#infinity_val and Exp#wildcard_val --- lib/aerospike/exp/exp.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/aerospike/exp/exp.rb b/lib/aerospike/exp/exp.rb index 4908c362..46fb90e1 100644 --- a/lib/aerospike/exp/exp.rb +++ b/lib/aerospike/exp/exp.rb @@ -424,6 +424,16 @@ def self.nil_val Nil.new end + # Create Infinity value. + def self.infinity_val + Infinity.new + end + + # Create Wildcard value. + def self.wildcard_val + Wildcard.new + end + #-------------------------------------------------- # Boolean Operator #-------------------------------------------------- @@ -1319,6 +1329,18 @@ def pack(packer) end end + class Infinity < Exp + def pack(packer) + InfinityValue.new.pack(packer) + end + end + + class Wildcard < Exp + def pack(packer) + WildcardValue.new.pack(packer) + end + end + class ExpBytes < Exp attr_reader :bytes From 3d8edc5b8172cecb42f63e3e776610c0c809195a Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 14 Aug 2024 11:42:06 +0200 Subject: [PATCH 09/13] [CLIENT-2177] Add support for MapReturnType#MAP_ORDERED and MapReturnType#MAP_UNORDERED --- lib/aerospike/cdt/map_return_type.rb | 8 + spec/aerospike/cdt/cdt_map_spec.rb | 226 +++++++++++++++------------ 2 files changed, 131 insertions(+), 103 deletions(-) diff --git a/lib/aerospike/cdt/map_return_type.rb b/lib/aerospike/cdt/map_return_type.rb index b283a1fb..802d88f4 100644 --- a/lib/aerospike/cdt/map_return_type.rb +++ b/lib/aerospike/cdt/map_return_type.rb @@ -69,6 +69,14 @@ module MapReturnType # Return true if count > 0. EXISTS = 13 + ## + # Return an unordered map. + UNORDERED_MAP = 16 + + ## + # Return an ordered map. + ORDERED_MAP = 17 + ## # :private # diff --git a/spec/aerospike/cdt/cdt_map_spec.rb b/spec/aerospike/cdt/cdt_map_spec.rb index 3e1088e5..2a31a3ac 100644 --- a/spec/aerospike/cdt/cdt_map_spec.rb +++ b/spec/aerospike/cdt/cdt_map_spec.rb @@ -39,7 +39,7 @@ def map_post_op client.delete(key) m = { - "key1" => [7, 9, 5], + "key1" => [7, 9, 5] } client.put(key, Aerospike::Bin.new(map_bin, m)) @@ -48,16 +48,16 @@ def map_post_op ctx = [Context.map_key("key2")] record = client.operate(key, [ - ListOperation.create(map_bin, ListOrder::ORDERED, false, ctx: ctx), - ListOperation.append(map_bin, 2, ctx: ctx), - ListOperation.append(map_bin, 1, ctx: ctx), - Operation.get(map_bin), - ]) + ListOperation.create(map_bin, ListOrder::ORDERED, false, ctx: ctx), + ListOperation.append(map_bin, 2, ctx: ctx), + ListOperation.append(map_bin, 1, ctx: ctx), + Operation.get(map_bin) + ]) expect(record.bins[map_bin]).to eq({ - "key1" => [7, 9, 5], - "key2" => [1, 2], - }) + "key1" => [7, 9, 5], + "key2" => [1, 2] + }) end it "should support Nested Map ops with Lists" do @@ -65,26 +65,26 @@ def map_post_op m = { "key1" => { - "key11" => 9, "key12" => 4, + "key11" => 9, "key12" => 4 }, "key2" => { - "key21" => 3, "key22" => 5, - }, + "key21" => 3, "key22" => 5 + } } client.put(key, Aerospike::Bin.new(map_bin, m)) record = client.operate(key, [Aerospike::Operation.get(map_bin)]) expect(record.bins[map_bin]).to eq(m) - record = client.operate(key, [MapOperation.put(map_bin, "key21", 11, ctx: [Context::map_key("key2")]), Aerospike::Operation.get(map_bin)]) + record = client.operate(key, [MapOperation.put(map_bin, "key21", 11, ctx: [Context.map_key("key2")]), Aerospike::Operation.get(map_bin)]) expect(record.bins[map_bin]).to eq({ - "key1" => { - "key11" => 9, "key12" => 4, - }, - "key2" => { - "key21" => 11, "key22" => 5, - }, - }) + "key1" => { + "key11" => 9, "key12" => 4 + }, + "key2" => { + "key21" => 11, "key22" => 5 + } + }) end it "should support Nested Map ops" do @@ -92,26 +92,26 @@ def map_post_op m = { "key1" => { - "key11" => 9, "key12" => 4, + "key11" => 9, "key12" => 4 }, "key2" => { - "key21" => 3, "key22" => 5, - }, + "key21" => 3, "key22" => 5 + } } client.put(key, Aerospike::Bin.new(map_bin, m)) record = client.operate(key, [Aerospike::Operation.get(map_bin)]) expect(record.bins[map_bin]).to eq(m) - record = client.operate(key, [MapOperation.put(map_bin, "key21", 11, ctx: [Context::map_key("key2")]), Aerospike::Operation.get(map_bin)]) + record = client.operate(key, [MapOperation.put(map_bin, "key21", 11, ctx: [Context.map_key("key2")]), Aerospike::Operation.get(map_bin)]) expect(record.bins[map_bin]).to eq({ - "key1" => { - "key11" => 9, "key12" => 4, - }, - "key2" => { - "key21" => 11, "key22" => 5, - }, - }) + "key1" => { + "key11" => 9, "key12" => 4 + }, + "key2" => { + "key21" => 11, "key22" => 5 + } + }) end it "should support Double Nested Map ops" do @@ -119,11 +119,11 @@ def map_post_op m = { "key1" => { - "key11" => { "key111" => 1 }, "key12" => { "key121" => 5 }, + "key11" => { "key111" => 1 }, "key12" => { "key121" => 5 } }, "key2" => { - "key21" => { "key211" => 7 }, - }, + "key21" => { "key211" => 7 } + } } client.put(key, Aerospike::Bin.new(map_bin, m)) @@ -131,16 +131,16 @@ def map_post_op record = client.operate(key, [Aerospike::Operation.get(map_bin)]) expect(record.bins[map_bin]).to eq(m) - record = client.operate(key, [MapOperation.put(map_bin, "key121", 11, ctx: [Context::map_key("key1"), Context.map_rank(-1)]), Aerospike::Operation.get(map_bin)]) + record = client.operate(key, [MapOperation.put(map_bin, "key121", 11, ctx: [Context.map_key("key1"), Context.map_rank(-1)]), Aerospike::Operation.get(map_bin)]) expect(record.bins[map_bin]).to eq({ - "key1" => { - "key11" => { "key111" => 1 }, "key12" => { "key121" => 11 }, - }, - "key2" => { - "key21" => { "key211" => 7 }, - }, - }) + "key1" => { + "key11" => { "key111" => 1 }, "key12" => { "key121" => 11 } + }, + "key2" => { + "key21" => { "key211" => 7 } + } + }) end end @@ -221,7 +221,7 @@ def map_post_op it "removes a single key from the map" do operation = MapOperation.remove_by_key(map_bin, "b") - .and_return(MapReturnType::VALUE) + .and_return(MapReturnType::VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to be(2) @@ -233,8 +233,8 @@ def map_post_op let(:map_value) { { "a" => 1, "b" => 2, "c" => 3 } } it "removes a list of keys from the map" do - operation = MapOperation.remove_by_key_list(map_bin, ["a", "b"]) - .and_return(MapReturnType::VALUE) + operation = MapOperation.remove_by_key_list(map_bin, %w[a b]) + .and_return(MapReturnType::VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to contain_exactly(1, 2) @@ -278,7 +278,7 @@ def map_post_op it "removes specified number of elements" do operation = MapOperation.remove_by_key_rel_index_range(map_bin, "f", 1, 2) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to contain_exactly("j") @@ -287,7 +287,7 @@ def map_post_op it "removes elements from specified key until the end" do operation = MapOperation.remove_by_key_rel_index_range(map_bin, "f", 1) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to contain_exactly("j") @@ -300,10 +300,10 @@ def map_post_op it "removes the items identified by a single value" do operation = MapOperation.remove_by_value(map_bin, 2) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) - expect(result.bins[map_bin]).to eql(["b", "d"]) + expect(result.bins[map_bin]).to eql(%w[b d]) expect(map_post_op).to eql({ "a" => 1, "c" => 3 }) end end @@ -313,7 +313,7 @@ def map_post_op it "removes the items identified by a list of values" do operation = MapOperation.remove_by_value_list(map_bin, [2, 3]) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to contain_exactly("b", "c", "d") @@ -357,7 +357,7 @@ def map_post_op it "removes specified number of elements" do operation = MapOperation.remove_by_value_rel_rank_range(map_bin, 11, -1, 1) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ 9 => 10 }) @@ -366,7 +366,7 @@ def map_post_op it "removes elements from specified key until the end" do operation = MapOperation.remove_by_value_rel_rank_range(map_bin, 11, -1) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ 9 => 10, 5 => 15, 0 => 17 }) @@ -460,7 +460,7 @@ def map_post_op it "gets a single key from the map" do operation = MapOperation.get_by_key(map_bin, "b") - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2 }) @@ -471,8 +471,8 @@ def map_post_op let(:map_value) { { "a" => 1, "b" => 2, "c" => 3 } } it "gets a list of keys from the map" do - operation = MapOperation.get_by_key_list(map_bin, ["b", "c"]) - .and_return(MapReturnType::VALUE) + operation = MapOperation.get_by_key_list(map_bin, %w[b c]) + .and_return(MapReturnType::VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to contain_exactly(2, 3) @@ -484,7 +484,7 @@ def map_post_op it "gets the specified key range from the map" do operation = MapOperation.get_by_key_range(map_bin, "b", "c") - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2 }) @@ -492,7 +492,7 @@ def map_post_op it "gets all keys from the specified start key until the end" do operation = MapOperation.get_by_key_range(map_bin, "b") - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2, "c" => 3 }) @@ -500,39 +500,59 @@ def map_post_op it "gets all keys from the start to the specified end key" do operation = MapOperation.get_by_key_range(map_bin, nil, "b") - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "a" => 1 }) end end - describe "MapOperation.get_by_key_rel_index_range", skip: !Support.min_version?("4.3") do - let(:map_value) { { "a" => 17, "e" => 2, "f" => 15, "j" => 10 } } + describe "MapOperation.get_by_key_range" do + let(:map_value) { { "b" => 2, "a" => 1, "c" => 3 } } - it "gets specified number of elements" do - operation = MapOperation.get_by_key_rel_index_range(map_bin, "f", 1, 2) - .and_return(MapReturnType::KEY) + it "gets all keys from the start to the specified end key, return ordered map" do + operation = MapOperation.get_by_key_range(map_bin, nil, "c") + .and_return(MapReturnType::ORDERED_MAP) result = client.operate(key, [operation]) - expect(result.bins[map_bin]).to contain_exactly("j") + expect(result.bins[map_bin]).to eql({ "a" => 1, "b" => 2 }) end - it "get elements from specified key until the end" do - operation = MapOperation.get_by_key_rel_index_range(map_bin, "f", 1) - .and_return(MapReturnType::KEY) + it "gets all keys from the start to the specified end key, return unordered map" do + operation = MapOperation.get_by_key_range(map_bin, nil, "c") + .and_return(MapReturnType::UNORDERED_MAP) result = client.operate(key, [operation]) - expect(result.bins[map_bin]).to contain_exactly("j") + expect(result.bins[map_bin]).to eql({ "b" => 2, "a" => 1 }) end end + describe "MapOperation.get_by_key_rel_index_range", skip: !Support.min_version?("4.3") do + let(:map_value) { { "a" => 17, "e" => 2, "f" => 15, "j" => 10 } } + + it "gets specified number of elements" do + operation = MapOperation.get_by_key_rel_index_range(map_bin, "f", 1, 2) + .and_return(MapReturnType::KEY) + result = client.operate(key, [operation]) + + expect(result.bins[map_bin]).to contain_exactly("j") + end + + it "get elements from specified key until the end" do + operation = MapOperation.get_by_key_rel_index_range(map_bin, "f", 1) + .and_return(MapReturnType::KEY) + result = client.operate(key, [operation]) + + expect(result.bins[map_bin]).to contain_exactly("j") + end + end + describe "MapOperation.get_by_value" do let(:map_value) { { "a" => 1, "b" => 2, "c" => 3, "d" => 2 } } it "gets the item identified by a single value" do operation = MapOperation.get_by_value(map_bin, 2) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2, "d" => 2 }) @@ -544,7 +564,7 @@ def map_post_op it "gets the items identified by a list of values" do operation = MapOperation.get_by_value_list(map_bin, [2, 3]) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2, "c" => 3, "d" => 2 }) @@ -556,7 +576,7 @@ def map_post_op it "gets the specified key range from the map" do operation = MapOperation.get_by_value_range(map_bin, 2, 3) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2, "d" => 2 }) @@ -564,7 +584,7 @@ def map_post_op it "gets all values from the specified start value until the end" do operation = MapOperation.get_by_value_range(map_bin, 2) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2, "d" => 2, "c" => 3 }) @@ -572,7 +592,7 @@ def map_post_op it "gets all values from the start of the map until the specified end value" do operation = MapOperation.get_by_value_range(map_bin, nil, 3) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "a" => 1, "b" => 2, "d" => 2 }) @@ -584,7 +604,7 @@ def map_post_op it "gets specified number of elements" do operation = MapOperation.get_by_value_rel_rank_range(map_bin, 11, -1, 1) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ 9 => 10 }) @@ -592,7 +612,7 @@ def map_post_op it "gets elements from specified key until the end" do operation = MapOperation.get_by_value_rel_rank_range(map_bin, 11, -1) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ 9 => 10, 5 => 15, 0 => 17 }) @@ -604,7 +624,7 @@ def map_post_op it "gets a map item identified by index from the map" do operation = MapOperation.get_by_index(map_bin, 1) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2 }) @@ -616,18 +636,18 @@ def map_post_op it "gets 'count' map items starting at the specified index from the map" do operation = MapOperation.get_by_index_range(map_bin, 1, 2) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) - expect(result.bins[map_bin]).to eql(["b", "c"]) + expect(result.bins[map_bin]).to eql(%w[b c]) end it "gets all items starting at the specified index to the end of the map" do operation = MapOperation.get_by_index_range(map_bin, 1) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) - expect(result.bins[map_bin]).to eql(["b", "c"]) + expect(result.bins[map_bin]).to eql(%w[b c]) end end @@ -636,7 +656,7 @@ def map_post_op it "gets a map item identified by rank from the map" do operation = MapOperation.get_by_rank(map_bin, 1) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "b" => 2 }) @@ -648,18 +668,18 @@ def map_post_op it "gets 'count' map items starting at the specified rank from the map" do operation = MapOperation.get_by_rank_range(map_bin, 1, 2) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) - expect(result.bins[map_bin]).to eql(["b", "a"]) + expect(result.bins[map_bin]).to eql(%w[b a]) end it "gets all items starting at the specified rank to the end of the map" do operation = MapOperation.get_by_rank_range(map_bin, 1) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) - expect(result.bins[map_bin]).to eql(["b", "a"]) + expect(result.bins[map_bin]).to eql(%w[b a]) end end @@ -669,7 +689,7 @@ def map_post_op it "removes a single key from the map" do operation = MapOperation.remove_keys(map_bin, "b") - .and_return(MapReturnType::VALUE) + .and_return(MapReturnType::VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to be(2) @@ -678,7 +698,7 @@ def map_post_op it "removes multiple keys from the map" do operation = MapOperation.remove_keys(map_bin, "b", "c") - .and_return(MapReturnType::VALUE) + .and_return(MapReturnType::VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to contain_exactly(2, 3) @@ -691,7 +711,7 @@ def map_post_op it "removes the items identified by a single value" do operation = MapOperation.remove_values(map_bin, 2) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to contain_exactly("b", "d") @@ -700,7 +720,7 @@ def map_post_op it "removes the items identified by multiple values" do operation = MapOperation.remove_values(map_bin, 2, 3) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to contain_exactly("b", "c", "d") @@ -715,7 +735,7 @@ def map_post_op context "NONE" do it "returns nothing" do operation = MapOperation.get_by_key(map_bin, "a") - .and_return(MapReturnType::NONE) + .and_return(MapReturnType::NONE) result = client.operate(key, [operation]) expected = { "map_bin" => nil } @@ -726,7 +746,7 @@ def map_post_op context "INDEX" do it "returns returns the elements index" do operation = MapOperation.get_by_key(map_bin, "a") - .and_return(MapReturnType::INDEX) + .and_return(MapReturnType::INDEX) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql(0) @@ -736,7 +756,7 @@ def map_post_op context "REVERSE_INDEX" do it "returns the elements reverse index" do operation = MapOperation.get_by_key(map_bin, "a") - .and_return(MapReturnType::REVERSE_INDEX) + .and_return(MapReturnType::REVERSE_INDEX) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql(2) @@ -746,7 +766,7 @@ def map_post_op context "RANK" do it "returns the elements rank" do operation = MapOperation.get_by_key(map_bin, "a") - .and_return(MapReturnType::RANK) + .and_return(MapReturnType::RANK) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql(2) @@ -756,7 +776,7 @@ def map_post_op context "REVERSE_RANK" do it "returns the elements reverse rank" do operation = MapOperation.get_by_key(map_bin, "a") - .and_return(MapReturnType::REVERSE_RANK) + .and_return(MapReturnType::REVERSE_RANK) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql(0) @@ -766,7 +786,7 @@ def map_post_op context "COUNT" do it "returns the count of items selected" do operation = MapOperation.get_by_key_range(map_bin, "a", "c") - .and_return(MapReturnType::COUNT) + .and_return(MapReturnType::COUNT) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql(2) @@ -776,7 +796,7 @@ def map_post_op context "KEY" do it "returns the map key" do operation = MapOperation.get_by_index(map_bin, 0) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql("a") @@ -786,7 +806,7 @@ def map_post_op context "VALUE" do it "returns the map value" do operation = MapOperation.get_by_index(map_bin, 0) - .and_return(MapReturnType::VALUE) + .and_return(MapReturnType::VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql(3) @@ -796,7 +816,7 @@ def map_post_op context "KEY_VALUE" do it "returns the map key & value" do operation = MapOperation.get_by_index(map_bin, 0) - .and_return(MapReturnType::KEY_VALUE) + .and_return(MapReturnType::KEY_VALUE) result = client.operate(key, [operation]) expect(result.bins[map_bin]).to eql({ "a" => 3 }) @@ -1065,7 +1085,7 @@ def map_post_op it "returns all keys from 5 to Infinity" do operation = MapOperation.get_by_key_range(map_bin, 5, Aerospike::Value::INFINITY) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) @@ -1074,18 +1094,18 @@ def map_post_op end context "Wildcard value", skip: !Support.min_version?("4.3.1") do - let(:map_value) { + let(:map_value) do { 4 => ["John", 55], 5 => ["Jim", 95], 9 => ["Joe", 80], - 12 => ["Jim", 46], + 12 => ["Jim", 46] } - } + end it "returns all values that match a wildcard" do operation = MapOperation.get_by_value(map_bin, ["Jim", Aerospike::Value::WILDCARD]) - .and_return(MapReturnType::KEY) + .and_return(MapReturnType::KEY) result = client.operate(key, [operation]) From fd514b27b4854c2bac7290bb3d7afbf37b56b68d Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 14 Aug 2024 12:32:40 +0200 Subject: [PATCH 10/13] [CLIENT-3080] Set correct return types in list/map read expressions Set bool return type for list read expressions with ListReturnType.EXISTS. Set bool return type for map read expressions with MapReturnType.EXISTS. Set map return type for map read expressions with MapReturnType.UNORDERED_MAP or MapReturnType.ORDERED_MAP. --- lib/aerospike/exp/exp_list.rb | 23 ++++++++++++++++++++--- lib/aerospike/exp/exp_map.rb | 28 ++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/aerospike/exp/exp_list.rb b/lib/aerospike/exp/exp_list.rb index 4985735d..0235ae21 100644 --- a/lib/aerospike/exp/exp_list.rb +++ b/lib/aerospike/exp/exp_list.rb @@ -365,10 +365,27 @@ def self.add_read(bin, bytes, ret_type) end def self.get_value_type(return_type) - if (return_type & ~CDT::ListReturnType::INVERTED) == CDT::ListReturnType::VALUE - Exp::Type::LIST - else + t = return_type & ~CDT::ListReturnType::INVERTED + case t + when ListReturnType::INDEX, + ListReturnType::REVERSE_INDEX, + ListReturnType::RANK, + ListReturnType::REVERSE_RANK + # This method only called from expressions that can return multiple integers (ie list). + Exp::Type::LIST + + when ListReturnType::COUNT Exp::Type::INT + + when ListReturnType::VALUE + # This method only called from expressions that can return multiple objects (ie list):: + Exp::Type::LIST + + when ListReturnType::EXISTS + Exp::Type::BOOL + + else + raise Exceptions::Aerospike.new(Aerospike::ResultCode::PARAMETER_ERROR, "Invalid ListReturnType: #{return_type}") end end diff --git a/lib/aerospike/exp/exp_map.rb b/lib/aerospike/exp/exp_map.rb index 473cfade..6dcc686d 100644 --- a/lib/aerospike/exp/exp_map.rb +++ b/lib/aerospike/exp/exp_map.rb @@ -4,7 +4,7 @@ # Portions may be licensed to Aerospike, Inc. under one or more contributor # license agreements. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may no +# Licensed under the Apache License, Version 2.0 (the "License") you may no # use this file except in compliance with the License. You may obtain a copy of # the License at http:#www.apache.org/licenses/LICENSE-2.0 # @@ -450,7 +450,7 @@ def self.get_by_rank_range(return_type, rank, bin, ctx: nil, count: nil) GET_BY_KEY = 97 GET_BY_INDEX = 98 GET_BY_RANK = 100 - GET_BY_VALUE = 102; # GET_ALL_BY_VALUE on server + GET_BY_VALUE = 102 # GET_ALL_BY_VALUE on server GET_BY_KEY_INTERVAL = 103 GET_BY_INDEX_RANGE = 104 GET_BY_VALUE_INTERVAL = 105 @@ -475,15 +475,27 @@ def self.add_read(bin, bytes, ret_type) def self.get_value_type(return_type) t = return_type & ~CDT::MapReturnType::INVERTED + case t + when MapReturnType::INDEX, MapReturnType::REVERSE_INDEX, MapReturnType::RANK, MapReturnType::REVERSE_RANK + # This method only called from expressions that can return multiple integers (ie list). + Exp::Type::LIST - if t <= CDT::MapReturnType::COUNT - return Exp::Type::INT - end + when MapReturnType::COUNT + Exp::Type::INT + + when MapReturnType::KEY, MapReturnType::VALUE + # This method only called from expressions that can return multiple objects (ie list). + Exp::Type::LIST + + when MapReturnType::KEY_VALUE, MapReturnType::ORDERED_MAP, MapReturnType::UNORDERED_MAP + Exp::Type::MAP + + when MapReturnType::EXISTS + Exp::Type::BOOL - if t == CDT::MapReturnType::KEY_VALUE - return Exp::Type::MAP + else + raise Exceptions::Aerospike.new(Aerospike::ResultCode::PARAMETER_ERROR, "Invalid MapReturnType: #{return_type}") end - Exp::Type::LIST end end # class MapExp end # module Aerospike From 21429ec761b7f378ccab4a587bdf1a361a9f0639 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 14 Aug 2024 13:11:07 +0200 Subject: [PATCH 11/13] [CLIENT-2682] Code Coverage - obtain current code coverage numbers for automated unit/integration functional tests --- .github/workflows/development.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index f288b32b..1c105bc3 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -10,8 +10,8 @@ jobs: strategy: matrix: os: - - ubuntu-latest - #- macos-latest + - ubuntu + # - macos ruby: - "2.6" @@ -20,20 +20,17 @@ jobs: experimental: [false] env: [""] - # include: - # - os: ubuntu - # ruby: head - # experimental: true steps: + - name: Set up Aerospike Database + uses: reugn/github-action-aerospike@v1 - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{matrix.ruby}} bundler-cache: true - - name: Set up Aerospike Database - uses: reugn/github-action-aerospike@v1 - name: Run tests timeout-minutes: 30 env: AEROSPIKE_HOSTS: "127.0.0.1:3000" + CODECOV_ENABLED: "false" run: ${{matrix.env}} bundle exec rspec From 6a591e6a24df66b854284b09c9923ac42ac258e2 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 14 Aug 2024 15:19:11 +0200 Subject: [PATCH 12/13] Update CHANGELOG --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f841a7b4..19b5c20e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ All notable changes to this project will be documented in this file. +## [3.1.0] 2024-08-14 + +- **New Features** + - [CLIENT-2177] Add support for `MapReturnType#MAP_ORDERED` and `MapReturnType#MAP_UNORDERED`. + - [CLIENT-2308] Add `Exp#infinity_val` and `Exp#wildcard_val`. + - [CLIENT_1731] Support Batch Operations. + +- **Updates** + - [CLIENT-3055] Remove Unsupported Server Features (`Predexp` and `BatchDirect`). + +- **Improvements** + - [CLIENT-3056] Fix Github Actions Workflow. Tests still fail due to runner constraints, but we are now on the right track. + - [CLIENT-2682] Code Coverage - obtain current code coverage numbers for automated unit/integration functional tests. + +- **Fixes** + - [CLIENT-3080] Set correct return types in list/map read expressions. + Set `bool` return type for list read expressions with `ListReturnType.EXISTS`. + Set `bool` return type for map read expressions with `MapReturnType.EXISTS`. + Set `map` return type for map read expressions with `MapReturnType.UNORDERED_MAP` or `MapReturnType.ORDERED_MAP`. + - [CLIENT-3072] Fix an issue where `Statement#return_data` is not respected. + ## [3.0.0] 2023-12-15 Notice: This version of the client only supports Aerospike Server v6.0 and later. Some features will work for the older server versions. - **new_features** From 0f2c3fd10e842d99e8f5054140521d7e39bc8c45 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 14 Aug 2024 19:24:02 +0200 Subject: [PATCH 13/13] Bump version to 4 --- CHANGELOG.md | 2 +- lib/aerospike/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19b5c20e..69198bf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## [3.1.0] 2024-08-14 +## [4.0.0] 2024-08-14 - **New Features** - [CLIENT-2177] Add support for `MapReturnType#MAP_ORDERED` and `MapReturnType#MAP_UNORDERED`. diff --git a/lib/aerospike/version.rb b/lib/aerospike/version.rb index e135e52b..fe400355 100644 --- a/lib/aerospike/version.rb +++ b/lib/aerospike/version.rb @@ -1,4 +1,4 @@ # encoding: utf-8 module Aerospike - VERSION = "3.0.0" + VERSION = "4.0.0" end