Lina
Lina

Reputation: 1352

Bitwise operation alternative in Neo4j cypher query

I need to do a bitwise "and" in a cypher query. It seems that cypher does not support bitwise operations. Any suggestions for alternatives? This is what I want to detect ... For example 268 is (2^8 + 2^3 + 2^2) and as you can see 2^3 = 8 is a part of my original number. So if I use bitwise AND it will be (100001100) & (1000) = 1000 so this way I can detect if 8 is a part of 268 or not. How can I do this without bitwise support? any suggestions? I need to do this in cypher.

Upvotes: 1

Views: 644

Answers (5)

Dave Bennett
Dave Bennett

Reputation: 11216

Alternatively, if one had APOC available they could use apoc.bitwise.op which is a wrapper around the java bitwise operations.

RETURN apoc.bitwise.op(268, "&",8 ) AS `268_AND_8`

Which yields the following result

enter image description here

Upvotes: 2

Lina
Lina

Reputation: 1352

Thanks Dave. I tried your solutions and they all worked. They were a good hint for me to find another approach. This is how I solved it. I used String comparison.

 with '100001100' as number , '100000000' as sub_number
 with number,sub_number,range(length (number)-1,length (number)-length(sub_number),-1) as tail,length (number)-length(sub_number) as difference
 unwind tail as i
 with i,sub_number,number, i - length (number) + length (sub_number) as sub_number_position
 with sub_number_position, (split(number,''))[i-1] as bit_mask , (split(sub_number,''))[sub_number_position] as sub_bit 
 with collect(toInt(bit_mask) * toInt(sub_bit)) as result

return result

Obviously the number and sub_number can have different values.

Upvotes: 0

Dave Bennett
Dave Bennett

Reputation: 11216

If you absolutely have to do the operation in cypher probably a better solution would be to implement something like @evan's SO solution Alternative to bitwise operation using cypher.

You could start by converting your data using cypher that looks something like this...

// convert binary to a product of prime numbers
// start with the number to conver an a collection of primes
with '100001100' as number
, [2,3,5,7,13,17,19,23,29,31,37] as primes

// create an index based on the size of the binary number to convert
// take a slice of the prime array that is the size of the number to convert
with number
, range(length(number)-1,0,-1) as index
, primes[0..length(number)] as primes, decimals[0..length(number)] as decimals

// iterate over the index and match the prime number to the bits in the number to convert
unwind index as i
with (split(number,''))[i] as binary_place_holder, primes[-i-1] as prime_place_holder, decimals[-i-1] as decimal_place_holder

// collect the primes that are set by multiplying by the set bits
with collect(toInt(binary_place_holder) * prime_place_holder) as prime_placeholders

// filter out the zero bits
with filter(p in prime_placeholders where p > 0) as prime_placeholders

// return a product of primes of the set bits
return prime_placeholders, reduce(pp = 1, p in prime_placeholders | pp * p) as prime_product

Sample of the output of the above query. The query could be adapted to update attributes with the prime product.

enter image description here

Here is a screen cap of how the conversion breaks down

enter image description here

Then when you want to use it you could use the modulus of the prime number in the location of the bit you want to test.

// test if the fourth bit is set in the decimal 268
// 268 is the equivalent of a prime product of 1015
// a modulus 7 == 0 will indicate the bit is set
with 1015 as prime_product
, [2,3,5,7,13,17,19,23,29,31,37] as primes
, 4 as bit_to_test
with bit_to_test
, prime_product
, primes[bit_to_test-1] as prime
, prime_product % primes[bit_to_test-1] as mod_remains
with 
  case when mod_remains = 0 then
    'bit ' + toString(bit_to_test) + ' set'
  else
    'bit ' + toString(bit_to_test) + ' NOT set'
  end as bit_set
return bit_set

Upvotes: 1

Dave Bennett
Dave Bennett

Reputation: 11216

Another way to perform this type of test using cypher would be to convert your decimal values to collections of the decimals that represent the bits that are set.

// convert the binary number to a collection of decimal parts
// create an index the size of the number to convert
// create a collection of decimals that correspond to the bit locations
with '100001100' as number
, [1,2,4,8,16,32,64,128,256,512,1024,2048,4096] as decimals
with number
, range(length(number)-1,0,-1) as index
, decimals[0..length(number)] as decimals

// map the bits to decimal equivalents
unwind index as i
with number, i, (split(number,''))[i] as binary_placeholder, decimals[-i-1] as decimal_placeholder

// multiply the decimal value by the bits that are set
with collect(decimal_placeholder * toInt(binary_placeholder)) as decimal_placeholders

// filter out the zero values from the collection
with filter(d in decimal_placeholders where d > 0) as decimal_placeholders
return decimal_placeholders

Here is a sample of what this returns.

enter image description here

Then when you want to test whether the number is in the decimal, you can just test the actual decimal for presence in the collection.

with [4, 8, 256] as decimal_placeholders
, 8 as decimal_to_test
return 
  case 
    when decimal_to_test in decimal_placeholders then
      toString(decimal_to_test) + ' value bit is set'
    else
      toString(decimal_to_test) + ' value bit is NOT set'
    end as bit_set_test

Upvotes: 2

Dave Bennett
Dave Bennett

Reputation: 11216

It almost certainly defeats the purpose of choosing a bitwise operation in the first place but if you absolutely needed to AND the two binary numbers in cypher you could do something like this with collections.

with split('100001100', '') as bin_term_1
, split('000001000', '') as bin_term_2
, toString(1) as one
with bin_term_1, bin_term_2, one, range(0,size(bin_term_1)-1,1) as index
unwind index as i
with i, bin_term_1, bin_term_2, one,
case 
  when (bin_term_1[i] = one) and (bin_term_2[i] = one) then
    1
  else
    0
  end as r
return collect(r) as AND

Upvotes: 0

Related Questions