File Coverage

blib/lib/FalkorDB/Graph.pm
Criterion Covered Total %
statement 12 91 13.1
branch 0 30 0.0
condition 0 12 0.0
subroutine 4 17 23.5
pod 7 10 70.0
total 23 160 14.3


line stmt bran cond sub pod time code
1             package FalkorDB::Graph;
2              
3 6     6   32 use strict;
  6         9  
  6         159  
4 6     6   16 use warnings;
  6         7  
  6         243  
5 6     6   23 use Scalar::Util qw(looks_like_number);
  6         8  
  6         276  
6 6     6   2277 use FalkorDB::QueryResult;
  6         13  
  6         5455  
7              
8             sub new {
9 0     0 0   my ( $class, $db, $name ) = @_;
10 0           my $self = {
11             db => $db,
12             name => $name,
13             };
14 0           return bless $self, $class;
15             }
16              
17             sub db {
18 0     0 0   my ($self) = @_;
19 0           return $self->{db};
20             }
21              
22             sub name {
23 0     0 0   my ($self) = @_;
24 0           return $self->{name};
25             }
26              
27             sub delete {
28 0     0 1   my ($self) = @_;
29 0           return $self->db->delete_graph( $self->name );
30             }
31              
32             sub query {
33 0     0 1   my ( $self, $cypher, $params ) = @_;
34 0           my $query_str = $self->_build_query_string( $cypher, $params );
35 0           my ( $raw_res, $error ) = $self->db->redis->__std_cmd( "GRAPH.QUERY", $self->name, $query_str );
36 0 0         if ( defined $error ) {
37 0           require Carp;
38 0           Carp::croak("[GRAPH.QUERY] $error");
39             }
40 0           return FalkorDB::QueryResult->new_from_raw($raw_res);
41             }
42              
43             sub ro_query {
44 0     0 1   my ( $self, $cypher, $params ) = @_;
45 0           my $query_str = $self->_build_query_string( $cypher, $params );
46 0           my ( $raw_res, $error ) =
47             $self->db->redis->__std_cmd( "GRAPH.RO_QUERY", $self->name, $query_str );
48 0 0         if ( defined $error ) {
49 0           require Carp;
50 0           Carp::croak("[GRAPH.RO_QUERY] $error");
51             }
52 0           return FalkorDB::QueryResult->new_from_raw($raw_res);
53             }
54              
55             sub explain {
56 0     0 1   my ( $self, $cypher, $params ) = @_;
57 0           my $query_str = $self->_build_query_string( $cypher, $params );
58 0           my ( $raw_res, $error ) =
59             $self->db->redis->__std_cmd( "GRAPH.EXPLAIN", $self->name, $query_str );
60 0 0         if ( defined $error ) {
61 0           require Carp;
62 0           Carp::croak("[GRAPH.EXPLAIN] $error");
63             }
64 0           return $raw_res;
65             }
66              
67             sub profile {
68 0     0 1   my ( $self, $cypher, $params ) = @_;
69 0           my $query_str = $self->_build_query_string( $cypher, $params );
70 0           my ( $raw_res, $error ) =
71             $self->db->redis->__std_cmd( "GRAPH.PROFILE", $self->name, $query_str );
72 0 0         if ( defined $error ) {
73 0           require Carp;
74 0           Carp::croak("[GRAPH.PROFILE] $error");
75             }
76 0           return $raw_res;
77             }
78              
79             sub create_index {
80 0     0 1   my ( $self, $label, $property ) = @_;
81 0           return $self->query("CREATE INDEX FOR (n:$label) ON (n.$property)");
82             }
83              
84             sub drop_index {
85 0     0 1   my ( $self, $label, $property ) = @_;
86 0           return $self->query("DROP INDEX FOR (n:$label) ON (n.$property)");
87             }
88              
89             # --- Parameter Serialization ---
90              
91             sub _build_query_string {
92 0     0     my ( $self, $cypher, $params ) = @_;
93              
94 0 0 0       return $cypher unless $params && ref $params eq 'HASH';
95              
96 0           my @serialized;
97 0           while ( my ( $k, $v ) = each %$params ) {
98 0           push @serialized, "$k=" . _serialize_param($v);
99             }
100              
101 0 0         if (@serialized) {
102 0           return "CYPHER " . join( " ", @serialized ) . " " . $cypher;
103             }
104              
105 0           return $cypher;
106             }
107              
108             sub _serialize_param {
109 0     0     my ($val) = @_;
110              
111 0 0         if ( !defined $val ) {
112 0           return 'null';
113             }
114              
115 0 0         if ( ref $val ) {
116 0           my $ref_type = ref $val;
117              
118             # Handle boolean wrappers (e.g. JSON::PP::Boolean, Types::Serialiser::Boolean)
119 0 0 0       if ( $ref_type eq 'JSON::PP::Boolean'
    0          
    0          
120             || $ref_type eq 'Types::Serialiser::Boolean' )
121             {
122 0 0         return $$val ? 'true' : 'false';
123             }
124             elsif ( $ref_type eq 'ARRAY' ) {
125 0           my @elems = map { _serialize_param($_) } @$val;
  0            
126 0           return '[' . join( ', ', @elems ) . ']';
127             }
128             elsif ( $ref_type eq 'HASH' ) {
129 0           my @pairs;
130 0           while ( my ( $k, $v ) = each %$val ) {
131 0           my $safe_key = $k;
132 0 0         if ( $k =~ /[^a-zA-Z0-9_]/ ) {
133 0           $safe_key = "`$k`";
134             }
135 0           push @pairs, "$safe_key: " . _serialize_param($v);
136             }
137 0           return '{' . join( ', ', @pairs ) . '}';
138             }
139             else {
140             # Stringify fallback for unrecognized references
141 0           return '"' . _escape_string("$val") . '"';
142             }
143             }
144              
145             # Check for boolean keywords passed as strings
146 0 0 0       if ( $val eq 'true' || $val eq 'false' ) {
147 0           return $val;
148             }
149              
150             # Check if value is numeric (so we avoid quoting it)
151 0 0 0       if ( looks_like_number($val) && $val !~ /^0\d+/ ) {
152 0           return $val;
153             }
154              
155             # Default: quote as string
156 0           return '"' . _escape_string($val) . '"';
157             }
158              
159             sub _escape_string {
160 0     0     my ($str) = @_;
161 0           $str =~ s/\\/\\\\/g;
162 0           $str =~ s/"/\\"/g;
163 0           $str =~ s/\n/\\n/g;
164 0           $str =~ s/\r/\\r/g;
165 0           $str =~ s/\t/\\t/g;
166 0           return $str;
167             }
168              
169             1;
170             __END__