Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compatibility with Neo4j server and Neo4j::Driver #38

Merged
merged 8 commits into from
Dec 11, 2024
4 changes: 2 additions & 2 deletions Build.PL
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ my $build = $class->new
'experimental' => 0,
'MIME::Base64' => 0,
perl => 5.010001,
'Neo4j::Driver' => '0.19',
'Neo4j::Driver' => '0.26', # ServerInfo->agent
},
recommends => {
'Mojo::UserAgent' => 0,
Expand All @@ -96,6 +96,7 @@ my $build = $class->new
'Mock::Quick' => 0,
'List::MoreUtils' => 0,
'Mojo::Exception' => 0,
'Neo4j::Driver' => '0.26', # cypher_params v2
experimental => 0,
'IPC::Run' => 0,
'IO::Pty' => 0,
Expand All @@ -105,7 +106,6 @@ my $build = $class->new
'Test::Pod' => 1.0,
'Mojo::UserAgent' => 0,
'HTTP::Thin' => 0,
'Neo4j::Driver' => '0.19',
},
meta_merge => {
resources => {
Expand Down
4 changes: 2 additions & 2 deletions lib/REST/Neo4p.pm
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use URI;
use URI::Escape;
use HTTP::Tiny;
use JSON::ize;
use Neo4j::Driver 0.1801;
use Neo4j::Driver 0.26;
use REST::Neo4p::Agent;
use REST::Neo4p::Node;
use REST::Neo4p::Index;
Expand Down Expand Up @@ -164,7 +164,7 @@ sub get_neo4j_version {
my ($url, $user, $pass) = @_;
my $driver = Neo4j::Driver->new($url);
$driver->basic_auth($user, $pass) if $user || $pass;
my $version = $driver->session->server->version;
my $version = $driver->session->server->agent;
my ($major, $minor, $patch, $milestone) =
$version =~ /^Neo4j\/(?:([0-9]+)\.)(?:([0-9]+)\.)?([0-9]+)?(?:-M([0-9]+))?/;
return wantarray ? ($major, $minor, $patch, $milestone) : $version;
Expand Down
6 changes: 4 additions & 2 deletions lib/REST/Neo4p/Agent/Neo4j/Driver.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package REST::Neo4p::Agent::Neo4j::Driver;
use v5.10;
use lib '../../../../../lib'; # testing
use base qw/REST::Neo4p::Agent/;
use Neo4j::Driver 0.1803;
use Neo4j::Driver 0.26;
use JSON::ize;
use REST::Neo4p::Agent::Neo4j::DriverActions;
use REST::Neo4p::Exceptions;
Expand Down Expand Up @@ -176,7 +176,7 @@ sub connect {
for (my $i = $REST::Neo4p::Agent::RQ_RETRIES; $i>0; $i--) {
my $f;
try {
my $version = $drv->session->server->version;
my $version = $drv->session->server->agent;
$version =~ s|^\S+/||; # server version strings look like "Neo4j/3.2.1"
$self->{_actions}{neo4j_version} = $version or
die "Can't find neo4j_version from server";
Expand Down Expand Up @@ -229,6 +229,7 @@ sub run_in_session {
$params = {} unless defined $params;
try {
$self->{_last_result} = $self->session->run($qry, $params);
$self->{_last_result}->has_next; # Make sure the query has executed and any errors have been thrown
} catch {
$self->{_last_errors} = $_;
};
Expand All @@ -243,6 +244,7 @@ sub run_in_transaction {
$params = {} unless defined $params;
try {
$self->{_last_result} = $tx->run($qry, $params);
$self->{_last_result}->has_next; # Make sure the query has executed and any errors have been thrown
} catch {
$self->{_last_errors} = $_;
};
Expand Down
26 changes: 24 additions & 2 deletions lib/REST/Neo4p/Agent/Neo4j/DriverActions.pm
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,8 @@ sub get_index {
$result = $self->is_version_4 ?
$self->run_in_session('call db.indexes() yield name, type, entityType where type = "FULLTEXT" and entityType = $ent return entityType, name, type',{ent => uc $ent}) :
$self->run_in_session('call db.index.explicit.list()');
# Neo4j 4.3+ syntax:
# $self->run_in_session('show indexes yield name, type, entityType where type = "FULLTEXT" and entityType = $ent return entityType, name, type',{ent => uc $ent}) :
}
else {
# find things
Expand Down Expand Up @@ -697,6 +699,8 @@ sub delete_index {
}
else {
$result = $tx->run("match ()-[r]->() where exists(r.__${idx}__keys) return distinct r.__${idx}__keys");
# Neo4j 4.3+ syntax:
# $result = $tx->run("match ()-[r]->() where r.`__${idx}__keys` is not null return distinct r.`__${idx}__keys`");
}
my (%k,%remove);
for my $k ($result->list()) {
Expand All @@ -712,9 +716,13 @@ sub delete_index {
else { #relationship
$tx->run("match ()-[r]->() where exists(r.__${idx}__keys) set r += \$rm remove r.__${idx}__keys",
{ rm => \%remove });
# Neo4j 4.3+ syntax:
# $tx->run("match ()-[r]->() where r.`__${idx}__keys` is not null set r += \$rm remove r.`__${idx}__keys`",
}
$tx->commit;
$result = $self->run_in_session('call db.index.fulltext.drop($idx)', {idx => $idx});
# Neo4j 4.3+ syntax:
# $result = $self->run_in_session(sprintf 'DROP INDEX `%s`', $idx);
}
else {
$result = $self->run_in_session('call db.index.explicit.drop($idx)',{idx => $idx});
Expand Down Expand Up @@ -785,6 +793,14 @@ sub post_index {
token => $content->{type} // "__$$content{name}__index",
prop => "__$$content{name}__keys"
});
# Neo4j 4.3+ syntax:
# my $token = $content->{type} // "__$$content{name}__index";
# my $ent_pattern = ($ent eq 'node') ? "(x:`$token`)" : "()-[x:`$token`]-()";
# $result = $self->run_in_session(
# sprintf "CREATE FULLTEXT INDEX `%s` FOR %s ON EACH [x.`%s`] OPTIONS {indexConfig: {`fulltext.analyzer`:'whitespace'}}",
# $content->{name}, $ent_pattern, "__$$content{name}__keys"
# )->consume;
# $result = $self->run_in_session("RETURN 1");
}
else {
my $for = ($ent eq 'node') ? 'forNodes' : 'forRelationships';
Expand Down Expand Up @@ -858,7 +874,8 @@ sub post_index {
for ($ent) {
/^node$/ && do {
$result = $self->run_in_session("create (n) $set_clause return n");
$content->{id} = 0+$result->fetch->get(0)->id;
my $node = $result->fetch->get(0);
$content->{id} = do { no warnings 'deprecated'; 0 + $node->id };
if ($self->is_version_4) {
my $hkey = encode_base64url($content->{key},'');
my $xi_prop = "_xi_$hkey";
Expand All @@ -885,7 +902,8 @@ sub post_index {
$content->{start} = 0+$start;
$content->{end} = 0+$end;
$result = $self->run_in_session("match (s), (t) where id(s)=\$start and id(t)=\$end create (s)-[n:$type]->(t) $set_clause return n", $content);
$content->{id} = 0+$result->fetch->get(0)->id;
my $relationship = $result->fetch->get(0);
$content->{id} = do { no warnings 'deprecated'; 0 + $relationship->id };
if ($self->is_version_4) {
my $hkey = encode_base64url($content->{key},'');
my $xi_prop = "_xi_$hkey";
Expand Down Expand Up @@ -945,6 +963,8 @@ sub get_schema_constraint {
my @constraints;
my $result;
$result = $self->run_in_session('call db.constraints()');
# Neo4j 4.3+ syntax:
# $result = $self->run_in_session($self->is_version_4 ? 'SHOW CONSTRAINTS' : 'call db.constraints()');
if ($result) {
while (my $rec = $result->fetch) {
my ($node_label,$reln_type,$x_prop, $u_prop) =
Expand Down Expand Up @@ -1060,6 +1080,8 @@ sub get_schema_index {
my $q;
if ($maj > 3) {
$q = 'call db.indexes() yield labelsOrTypes as labels, properties where $lbl in labels return { label:$lbl, property_keys:properties }';
# Neo4j 4.3+ syntax:
# $q = 'show indexes yield labelsOrTypes as labels, properties where $lbl in labels return { label:$lbl, property_keys:properties }';
}
elsif ($maj==3 && $min>=5) { # patch
$q = 'call db.indexes() yield tokenNames as labels, properties where $lbl in labels return { label:$lbl, property_keys:properties }';
Expand Down
58 changes: 33 additions & 25 deletions lib/REST/Neo4p/Agent/Neo4j/ResultProcessor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package
REST::Neo4p::Agent::Neo4j::Driver;

use REST::Neo4p::Exceptions;
use strict;

our %result_processors;

Expand All @@ -18,8 +19,9 @@ $result_processors{get_node} = sub {
my @r = $_->list;
REST::Neo4p::NotFoundException->throw unless @r;
$r = $r[0]->get(0);
return { metadata => { id => $r->id, labels => [$r->labels] },
self => 'node/'.$r->id,
my $id = do { no warnings 'deprecated'; $r->id };
return { metadata => { id => $id, labels => [$r->labels] },
self => 'node/'.$id,
data => $r->properties };
}
else {
Expand All @@ -33,12 +35,13 @@ $result_processors{get_node} = sub {
my $ret = [];
while (my $rec = $_->fetch) {
$r = $rec->get(0);
my $id = do { no warnings 'deprecated'; $r->id };
push @$ret, {
metadata => { id => $r->id, type => $r->type },
self => 'relationship/'.$r->id,
metadata => { id => $id, type => $r->type },
self => 'relationship/'.$id,
data => $r->properties,
start => 'node/'.$r->start_id,
end => 'node/'.$r->end_id,
start => 'node/' . do { no warnings 'deprecated'; $r->start_id },
end => 'node/' . do { no warnings 'deprecated'; $r->end_id },
type => $r->type
}
}
Expand All @@ -58,9 +61,10 @@ $result_processors{post_node} = sub {
my @r = $_->list;
REST::Neo4p::NotFoundException->throw unless @r;
$r = $r[0]->get(0);
my $id = do { no warnings 'deprecated'; $r->id };
return {
metadata => { id => $r->id, labels => [] },
self => 'node/'.$r->id,
metadata => { id => $id, labels => [] },
self => 'node/'.$id,
data => {}
};
}
Expand All @@ -73,12 +77,13 @@ $result_processors{post_node} = sub {
my @r = $_->list;
REST::Neo4p::NotFoundException->throw unless @r;
$r = $r[0]->get(0);
my $id = do { no warnings 'deprecated'; $r->id };
return {
metadata => { id => $r-id, type => $r->type },
self => 'relationship/'.$r->id,
metadata => { id => $id, type => $r->type },
self => 'relationship/'.$id,
data => $r->properties,
start => 'node/'.$r->start_id,
end => 'node/'.$r->end_id,
start => 'node/' . do { no warnings 'deprecated'; $r->start_id },
end => 'node/' . do { no warnings 'deprecated'; $r->end_id },
type => $r->type
};
};
Expand All @@ -95,12 +100,13 @@ $result_processors{get_relationship} = sub {
my @r = $_->list;
REST::Neo4p::NotFoundException->throw() unless @r;
my $r = $r[0]->get(0);
my $id = do { no warnings 'deprecated'; $r->id };
return {
metadata => { id => $r->id, type => $r->type },
self => 'relationship/'.$r->id,
metadata => { id => $id, type => $r->type },
self => 'relationship/'.$id,
data => $r->properties,
start => 'node/'.$r->start_id,
end => 'node/'.$r->end_id,
start => 'node/' . do { no warnings 'deprecated'; $r->start_id },
end => 'node/' . do { no warnings 'deprecated'; $r->end_id },
type => $r->type
};
};
Expand All @@ -109,7 +115,7 @@ $result_processors{get_relationship} = sub {
($other[0] eq 'properties') && do {
my @r = $_->list;
REST::Neo4p::NotFoundException->throw unless @r;
$r = $r[0]->get(0);
my $r = $r[0]->get(0);
return $r;
};
return;
Expand All @@ -132,8 +138,9 @@ $result_processors{get_label} = sub {
my $ret = [];
while (my $rec = $_->fetch) {
my $r = $rec->get(0);
push @$ret, { metadata => { id => $r->id, labels => [$r->labels] },
self => 'node/'.$r->id,
my $id = do { no warnings 'deprecated'; $r->id };
push @$ret, { metadata => { id => $id, labels => [$r->labels] },
self => 'node/'.$id,
data => $r->properties };
}
return $ret;
Expand All @@ -159,8 +166,9 @@ $result_processors{get_index} = sub {
while (my $rec = $_->fetch) {
my $r = $rec->get(0);
my @labels = (ref($r) =~ /Node/ ? (labels => [$r->labels]) : ());
push @$ret, { metadata => { id => $r->id, @labels },
self => 'node/'.$r->id,
my $id = do { no warnings 'deprecated'; $r->id };
push @$ret, { metadata => { id => $id, @labels },
self => 'node/'.$id,
data => $r->properties };
}
REST::Neo4p::NotFoundException->throw unless @$ret;
Expand All @@ -181,14 +189,14 @@ $result_processors{post_index} = sub {
else {
my $n = $_->fetch->get(0);
return unless ref $n;
my $id = $n->id;
my $id = do { no warnings 'deprecated'; $n->id };
return {
metadata => { id => $id },
self => "$ent/$id",
($n->properties ? (data => $n->properties) : ()),
($n->can(start_id) ? (
start_id => $n->start_id,
end_id => $n->end_id,
($n->can('start_id') ? (
start_id => do { no warnings 'deprecated'; $n->start_id },
end_id => do { no warnings 'deprecated'; $n->end_id },
type => $n->type
) : ()),
indexed => "index/$ent/$idx/$$content{key}/$$content{value}/$id"
Expand Down
8 changes: 5 additions & 3 deletions lib/REST/Neo4p/Entity.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package REST::Neo4p::Entity;
use REST::Neo4p::Exceptions;
use Carp qw(croak carp);
use JSON;
use Scalar::Util qw(blessed);
use URI::Escape;
use strict;
use warnings;
Expand Down Expand Up @@ -65,7 +66,7 @@ sub new_from_json_response {
unless (defined $decoded_resp) {
REST::Neo4p::LocalException->throw("new_from_json_response() called with undef argument\n");
}
my $is_json = !(ref($decoded_resp) =~ /Neo4j::Driver/);
my $is_json = ! blessed $decoded_resp; # blessed via Neo4j::Driver
unless ($ENTITY_TABLE->{$entity_type}{_actions} || !$is_json) {
# capture the url suffix patterns for the entity actions:
for (keys %$decoded_resp) {
Expand All @@ -82,6 +83,7 @@ sub new_from_json_response {
($obj) = $self_url =~ /([a-z0-9_]+)\/?$/i;
}
else { # Driver
no warnings 'deprecated'; # id() in Neo4j 5
$obj = $decoded_resp->id;
$self_url = "$entity_type/$obj";
}
Expand All @@ -96,8 +98,8 @@ sub new_from_json_response {
}
else { # Driver
if ($decoded_resp->can('start_id')) {
$start_id = $decoded_resp->start_id;
$end_id = $decoded_resp->end_id;
$start_id = do { no warnings 'deprecated'; $decoded_resp->start_id };
$end_id = do { no warnings 'deprecated'; $decoded_resp->end_id };
$type = $decoded_resp->type;
}
}
Expand Down
2 changes: 2 additions & 0 deletions lib/REST/Neo4p/Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ functionality, the agent based on [Neo4j::Driver](https://metacpan.org/pod/Neo4j
indexes under the hood to emulate explicit indexes. This agent is used
automatically with Neo4j version 4.0 servers.

Note that this index emulation currently doesn't work with Neo4j 5.

# METHODS

- new()
Expand Down
2 changes: 2 additions & 0 deletions lib/REST/Neo4p/Index.pm
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ functionality, the agent based on L<Neo4j::Driver> uses fulltext
indexes under the hood to emulate explicit indexes. This agent is used
automatically with Neo4j version 4.0 servers.

Note that this index emulation currently doesn't work with S<Neo4j 5.>

=head1 METHODS

=over
Expand Down
8 changes: 4 additions & 4 deletions lib/REST/Neo4p/Node.pm
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ sub simple_from_json_response {
my $class = shift;
my ($decoded_resp) = @_;
my $ret;
for (ref $decoded_resp) {
/HASH/ && do {
for ($decoded_resp) {
ref eq 'HASH' && do {
# node id
($ret->{_node}) = $decoded_resp->{self} =~ m{.*/([0-9]+)$};
# node properties
Expand All @@ -235,8 +235,8 @@ sub simple_from_json_response {
}
last;
};
/Driver/ && do {
$ret->{_node} = $decoded_resp->id;
$_->isa('Neo4j::Types::Node') && do { # via Neo4j::Driver
$ret->{_node} = do { no warnings 'deprecated'; $decoded_resp->id };
$ret->{$_} = $decoded_resp->properties->{$_} for keys %{$decoded_resp->properties};
last;
};
Expand Down
Loading
Loading