module Couchbase::Operations
module Arithmetic
# Increment the value of an existing numeric key
#
# @since 1.0.0
#
# The increment methods allow you to increase a given stored integer
# value. These are the incremental equivalent of the decrement operations
# and work on the same basis; updating the value of a key if it can be
# parsed to an integer. The update operation occurs on the server and is
# provided at the protocol level. This simplifies what would otherwise be a
# two-stage get and set operation.
#
# @note that server values stored and transmitted as unsigned numbers,
# therefore if you try to store negative number and then increment or
# decrement it will cause overflow. (see "Integer overflow" example
# below)
#
# @overload incr(key, delta = 1, options = {})
# @param key [String, Symbol] Key used to reference the value.
# @param delta [Fixnum] Integer (up to 64 bits) value to increment
# @param options [Hash] Options for operation.
# @option options [true, false] :create (false) If set to +true+, it will
# initialize the key with zero value and zero flags (use +:initial+
# option to set another initial value). Note: it won't increment the
# missing value.
# @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
# missing key initialization. This option imply +:create+ option is
# +true+.
# @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
# Values larger than 30*24*60*60 seconds (30 days) are interpreted as
# absolute times (from the epoch). This option ignored for existent
# keys.
# @option options [true, false] :extended (false) If set to +true+, the
# operation will return tuple +[value, cas]+, otherwise (by default) it
# returns just value.
#
# @yieldparam ret [Result] the result of operation in asynchronous mode
# (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
#
# @return [Fixnum] the actual value of the key.
#
# @raise [Couchbase::Error::NotFound] if key is missing and +:create+
# option isn't +true+.
#
# @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
# value
#
# @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
#
# @raise [ArgumentError] when passing the block in synchronous mode
#
# @example Increment key by one
# c.incr("foo")
#
# @example Increment key by 50
# c.incr("foo", 50)
#
# @example Increment key by one OR initialize with zero
# c.incr("foo", :create => true) #=> will return old+1 or 0
#
# @example Increment key by one OR initialize with three
# c.incr("foo", 50, :initial => 3) #=> will return old+50 or 3
#
# @example Increment key and get its CAS value
# val, cas = c.incr("foo", :extended => true)
#
# @example Integer overflow
# c.set("foo", -100)
# c.get("foo") #=> -100
# c.incr("foo") #=> 18446744073709551517
#
# @example Asynchronous invocation
# c.run do
# c.incr("foo") do |ret|
# ret.operation #=> :increment
# ret.success? #=> true
# ret.key #=> "foo"
# ret.value
# ret.cas
# end
# end
#
def incr(*args)
sync_block_error if !async? && block_given?
do_arithmetic(:incr, *args)
end
alias_method :increment, :incr
# Decrement the value of an existing numeric key
#
# @since 1.0.0
#
# The decrement methods reduce the value of a given key if the
# corresponding value can be parsed to an integer value. These operations
# are provided at a protocol level to eliminate the need to get, update,
# and reset a simple integer value in the database. It supports the use of
# an explicit offset value that will be used to reduce the stored value in
# the database.
#
# @note that server values stored and transmitted as unsigned numbers,
# therefore if you try to decrement negative or zero key, you will always
# get zero.
#
# @overload decr(key, delta = 1, options = {})
# @param key [String, Symbol] Key used to reference the value.
# @param delta [Fixnum] Integer (up to 64 bits) value to decrement
# @param options [Hash] Options for operation.
# @option options [true, false] :create (false) If set to +true+, it will
# initialize the key with zero value and zero flags (use +:initial+
# option to set another initial value). Note: it won't decrement the
# missing value.
# @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
# missing key initialization. This option imply +:create+ option is
# +true+.
# @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
# Values larger than 30*24*60*60 seconds (30 days) are interpreted as
# absolute times (from the epoch). This option ignored for existent
# keys.
# @option options [true, false] :extended (false) If set to +true+, the
# operation will return tuple +[value, cas]+, otherwise (by default) it
# returns just value.
#
# @yieldparam ret [Result] the result of operation in asynchronous mode
# (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
#
# @return [Fixnum] the actual value of the key.
#
# @raise [Couchbase::Error::NotFound] if key is missing and +:create+
# option isn't +true+.
#
# @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
# value
#
# @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
#
# @raise [ArgumentError] when passing the block in synchronous mode
#
# @example Decrement key by one
# c.decr("foo")
#
# @example Decrement key by 50
# c.decr("foo", 50)
#
# @example Decrement key by one OR initialize with zero
# c.decr("foo", :create => true) #=> will return old-1 or 0
#
# @example Decrement key by one OR initialize with three
# c.decr("foo", 50, :initial => 3) #=> will return old-50 or 3
#
# @example Decrement key and get its CAS value
# val, cas = c.decr("foo", :extended => true)
#
# @example Decrementing zero
# c.set("foo", 0)
# c.decrement("foo", 100500) #=> 0
#
# @example Decrementing negative value
# c.set("foo", -100)
# c.decrement("foo", 100500) #=> 0
#
# @example Asynchronous invocation
# c.run do
# c.decr("foo") do |ret|
# ret.operation #=> :decrement
# ret.success? #=> true
# ret.key #=> "foo"
# ret.value
# ret.cas
# end
# end
#
def decr(*args)
sync_block_error if !async? && block_given?
do_arithmetic(:decr, *args)
end
alias_method :decrement, :decr
private
def do_arithmetic(op, *args)
key, delta, options = expand_arithmetic_args(args)
case key
when String, Symbol
single_arithmetic(op, key, delta, options)
when Array, Hash
multi_arithmetic(op, key, delta)
else
raise # something
end
end
def expand_arithmetic_args(args)
options = if args.size > 1 && args.last.respond_to?(:to_h)
args.pop
else
{}
end
delta = if args.size > 1 && args.last.respond_to?(:to_int)
args.pop
else
options[:delta] || 1
end
key = args.size == 1 ? args.first : args
[key, delta, options]
end
def single_arithmetic(op, key, delta, options = {})
if async?
java_async_arithmetic(op, key, delta)
else
result = java_arithmetic(op, key, delta)
set_default_arithmetic_or_raise(key, result, options)
end
end
def set_default_arithmetic_or_raise(key, result, options)
if result < 0
if options[:initial] || options[:create] || set_default_arithmetic_init?
value = if options[:initial]
options[:initial]
elsif options[:create]
0
else
default_arithmetic_init_int
end
set(key, value, options) && value
else
not_found_error(true)
end
else
result
end
end
def set_default_arithmetic_init?
default_arithmetic_init == true ||
default_arithmetic_init.respond_to?(:to_int) &&
default_arithmetic_init > 0
end
def default_arithmetic_init_int
default_arithmetic_init == true ? 0 : default_arithmetic_init
end
def multi_arithmetic(op, keys, delta)
{}.tap do |results|
if keys.respond_to?(:each_pair)
keys.each_pair do |k, v|
results[k] = single_arithmetic(op, k, v)
end
else
keys.each do |k|
results[k] = single_arithmetic(op, k, delta)
end
end
end
end
def java_arithmetic(op, key, delta)
case op
when :incr
java_incr(key, delta)
when :decr
java_decr(key, delta)
end
end
def java_async_arithmetic(op, key, delta)
case op
when :incr
java_async_incr(key, delta)
when :decr
java_async_decr(key, delta)
end
end
def java_incr(key, delta)
client.incr(key, delta)
end
def java_async_incr(key, delta)
client.asyncIncr(key, delta)
end
def java_decr(key, delta)
client.decr(key, delta)
end
def java_async_decr(key, delta)
client.asyncDecr(key, delta)
end
end
end