| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#$Id$ |
|
2
|
|
|
|
|
|
|
package REST::Neo4p::Constraint; |
|
3
|
4
|
|
|
4
|
|
33736
|
use base 'Exporter'; |
|
|
4
|
|
|
|
|
18
|
|
|
|
4
|
|
|
|
|
743
|
|
|
4
|
4
|
|
|
4
|
|
46
|
use REST::Neo4p; |
|
|
4
|
|
|
|
|
16
|
|
|
|
4
|
|
|
|
|
173
|
|
|
5
|
4
|
|
|
4
|
|
34
|
use REST::Neo4p::Exceptions; |
|
|
4
|
|
|
|
|
14
|
|
|
|
4
|
|
|
|
|
123
|
|
|
6
|
4
|
|
|
4
|
|
24
|
use JSON; |
|
|
4
|
|
|
|
|
15
|
|
|
|
4
|
|
|
|
|
70
|
|
|
7
|
4
|
|
|
4
|
|
1245
|
use Data::Dumper; |
|
|
4
|
|
|
|
|
11
|
|
|
|
4
|
|
|
|
|
474
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
4
|
|
|
4
|
|
34
|
use Scalar::Util qw(looks_like_number); |
|
|
4
|
|
|
|
|
17
|
|
|
|
4
|
|
|
|
|
322
|
|
|
10
|
4
|
|
|
4
|
|
32
|
use strict; |
|
|
4
|
|
|
|
|
12
|
|
|
|
4
|
|
|
|
|
151
|
|
|
11
|
4
|
|
|
4
|
|
28
|
use warnings; |
|
|
4
|
|
|
|
|
15
|
|
|
|
4
|
|
|
|
|
1322
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our @EXPORT = qw(serialize_constraints load_constraints); |
|
14
|
|
|
|
|
|
|
our @VALIDATE = qw(validate_properties validate_relationship validate_relationship_type); |
|
15
|
|
|
|
|
|
|
our @EXPORT_OK = (@VALIDATE); |
|
16
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
|
17
|
|
|
|
|
|
|
validate => \@VALIDATE, |
|
18
|
|
|
|
|
|
|
auto => [@EXPORT], |
|
19
|
|
|
|
|
|
|
all => [@EXPORT,@EXPORT_OK] |
|
20
|
|
|
|
|
|
|
); |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $jobj = JSON->new->utf8; |
|
23
|
|
|
|
|
|
|
$jobj->allow_blessed(1); |
|
24
|
|
|
|
|
|
|
$jobj->convert_blessed(1); |
|
25
|
|
|
|
|
|
|
my $regex_to_json = sub { |
|
26
|
3
|
|
|
3
|
|
7
|
my $qr = shift; |
|
27
|
3
|
|
|
|
|
12
|
local $Data::Dumper::Terse=1; |
|
28
|
3
|
|
|
|
|
25
|
$qr = Dumper $qr; |
|
29
|
3
|
|
|
|
|
278
|
chomp $qr; |
|
30
|
3
|
|
|
|
|
16
|
return $qr; |
|
31
|
|
|
|
|
|
|
}; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
BEGIN { |
|
34
|
4
|
|
|
4
|
|
1562
|
$REST::Neo4p::Constraint::VERSION = '0.4003'; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# valid constraint types |
|
38
|
|
|
|
|
|
|
our @CONSTRAINT_TYPES = qw( node_property relationship_property |
|
39
|
|
|
|
|
|
|
relationship_type relationship ); |
|
40
|
|
|
|
|
|
|
our $CONSTRAINT_TABLE = {}; |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# flag - when set, disallow relationships that are not allowed by current |
|
44
|
|
|
|
|
|
|
# relationship types |
|
45
|
|
|
|
|
|
|
# default strict |
|
46
|
|
|
|
|
|
|
$REST::Neo4p::Constraint::STRICT_RELN_TYPES = 1; |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# flag - when set, require strict checking of relationship properties when |
|
49
|
|
|
|
|
|
|
# validating relationships -- i.e., a relationship with no properties is |
|
50
|
|
|
|
|
|
|
# disallowed unless there is a specific relationship_property constraint |
|
51
|
|
|
|
|
|
|
# allow this |
|
52
|
|
|
|
|
|
|
# default relaxed |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
$REST::Neo4p::Constraint::STRICT_RELN_PROPS = 0; |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# flag - when set, use the database to store constraints |
|
57
|
|
|
|
|
|
|
$REST::Neo4p::Constraint::USE_NEO4J = 0; |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub new { |
|
61
|
34
|
|
|
34
|
1
|
2888
|
my $class = shift; |
|
62
|
34
|
|
|
|
|
106
|
my ($tag, $constraints) = @_; |
|
63
|
34
|
|
|
|
|
69
|
my $self = bless {}, $class; |
|
64
|
34
|
100
|
|
|
|
82
|
unless (defined $tag) { |
|
65
|
1
|
|
|
|
|
49
|
REST::Neo4p::LocalException->throw("New constraint requires tag as arg 1\n"); |
|
66
|
|
|
|
|
|
|
} |
|
67
|
33
|
100
|
|
|
|
150
|
unless ($tag =~ /^[a-z0-9_.]+$/i) { |
|
68
|
1
|
|
|
|
|
4
|
REST::Neo4p::LocalException->throw("Constraint tag may contain only alphanumerics chars, underscore and period\n"); |
|
69
|
|
|
|
|
|
|
} |
|
70
|
32
|
50
|
|
|
|
492
|
if ( !grep /^$tag$/,keys %$CONSTRAINT_TABLE ) { |
|
71
|
32
|
|
|
|
|
151
|
$self->{_tag} = $tag; |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
else { |
|
74
|
0
|
|
|
|
|
0
|
REST::Neo4p::LocalException->throw("Constraint with tag '$tag' is already defined\n"); |
|
75
|
|
|
|
|
|
|
} |
|
76
|
32
|
|
|
|
|
141
|
$self->new_from_constraint_hash($constraints); |
|
77
|
31
|
|
|
|
|
96
|
$CONSTRAINT_TABLE->{$tag} = $self; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub new_from_constraint_hash { |
|
81
|
0
|
|
|
0
|
0
|
0
|
REST::Neo4p::AbstractMethodException->throw("new_from_constraint_hash() is an abstract method of ".__PACKAGE__."\n"); |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub TO_JSON { |
|
85
|
4
|
|
|
4
|
|
35
|
no warnings qw(redefine); |
|
|
4
|
|
|
|
|
7
|
|
|
|
4
|
|
|
|
|
8549
|
|
|
86
|
11
|
|
|
11
|
0
|
23
|
my $self = shift; |
|
87
|
11
|
|
|
|
|
15
|
my $store; |
|
88
|
11
|
|
|
|
|
22
|
my $old = *Regexp::TO_JSON{CODE}; |
|
89
|
11
|
|
|
|
|
20
|
*Regexp::TO_JSON = $regex_to_json; |
|
90
|
11
|
|
|
|
|
21
|
$store = $self->constraints; |
|
91
|
11
|
|
|
|
|
32
|
$store->{_condition} = $self->condition; |
|
92
|
11
|
|
|
|
|
24
|
$store->{_priority} = $self->priority; |
|
93
|
11
|
100
|
|
|
|
71
|
$store->{_relationship_type} = $self->rtype if $self->can('rtype'); |
|
94
|
11
|
|
|
|
|
26
|
my $ret = $jobj->encode({tag => $self->tag, type => $self->type, |
|
95
|
|
|
|
|
|
|
_constraint_hash => $store }); |
|
96
|
11
|
100
|
|
|
|
38
|
*Regexp::TO_JSON = $old if $old; |
|
97
|
11
|
|
|
|
|
62
|
return $ret; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub new_from_json { |
|
101
|
11
|
|
|
11
|
0
|
304
|
my $class = shift; |
|
102
|
11
|
|
|
|
|
20
|
my ($json) = @_; |
|
103
|
11
|
50
|
|
|
|
36
|
unless (ref($json)) { |
|
104
|
11
|
|
|
|
|
107
|
$json = decode_json($json); |
|
105
|
|
|
|
|
|
|
} |
|
106
|
11
|
50
|
33
|
|
|
52
|
unless ( $json->{tag} && $json->{type} ) { |
|
107
|
0
|
|
|
|
|
0
|
REST::Neo4p::LocalException->throw("json does not correctly specify a constraint object\n"); |
|
108
|
|
|
|
|
|
|
} |
|
109
|
11
|
|
|
|
|
24
|
my $subclass = $json->{type}; |
|
110
|
11
|
|
|
|
|
28
|
_fix_constraints($json->{_constraint_hash}); |
|
111
|
11
|
|
|
|
|
73
|
$subclass =~ s/^(.)/\U$1\E/; |
|
112
|
11
|
|
|
|
|
43
|
$subclass =~ s/_(.)/\U$1\E/; |
|
113
|
11
|
|
|
|
|
27
|
$subclass = 'REST::Neo4p::Constraint::'.$subclass; |
|
114
|
11
|
|
|
|
|
55
|
$subclass->new($json->{tag}, $json->{_constraint_hash}); |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub _fix_constraints { |
|
118
|
|
|
|
|
|
|
# make qr// strings into Regexp objects |
|
119
|
78
|
|
|
78
|
|
117
|
local $_ = shift; |
|
120
|
78
|
100
|
|
|
|
254
|
if (ref eq 'HASH') { |
|
|
|
100
|
|
|
|
|
|
|
121
|
17
|
|
|
|
|
63
|
while (my ($k, $v) = each %$_) { |
|
122
|
50
|
100
|
100
|
|
|
172
|
if ($v && ($v =~ /^qr\//)) { |
|
123
|
3
|
50
|
|
|
|
12
|
if ($v =~ /\(\?(\^|-[a-z]+):.*\)/) { |
|
124
|
0
|
|
|
|
|
0
|
$v =~ s{/\(\?(\^|-[a-z]+):}{/}; # kludge - eval wants to wrap (?:^...) around a qr string |
|
125
|
0
|
|
|
|
|
0
|
$v =~ s{\)/}{/}; # kludge - even if one is there already |
|
126
|
|
|
|
|
|
|
} |
|
127
|
3
|
|
|
|
|
295
|
$_->{$k} = eval $v; # replace with Regexp |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
else { |
|
130
|
47
|
|
|
|
|
72
|
_fix_constraints($v); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
elsif (ref eq 'ARRAY') { |
|
135
|
11
|
|
|
|
|
22
|
foreach my $v (@$_) { |
|
136
|
20
|
|
|
|
|
31
|
_fix_constraints($v); |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
88
|
|
|
88
|
1
|
312
|
sub tag { shift->{_tag} } |
|
142
|
281
|
|
|
281
|
1
|
997
|
sub type { shift->{_type} } |
|
143
|
125
|
|
|
125
|
1
|
2242
|
sub condition { shift->{_constraints}{_condition} } ## |
|
144
|
338
|
|
|
338
|
1
|
593
|
sub priority { shift->{_constraints}{_priority} } ## |
|
145
|
563
|
|
|
563
|
1
|
1501
|
sub constraints { shift->{_constraints} } |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub set_priority { |
|
148
|
3
|
|
|
3
|
1
|
8
|
my $self = shift; |
|
149
|
3
|
|
|
|
|
6
|
my ($priority_value) = @_; |
|
150
|
3
|
50
|
|
|
|
13
|
unless (looks_like_number($priority_value)) { |
|
151
|
0
|
|
|
|
|
0
|
REST::Neo4p::LocalException->throw("Priority value must be numeric\n"); |
|
152
|
|
|
|
|
|
|
} |
|
153
|
3
|
|
|
|
|
16
|
return $self->{_constraints}{_priority} = $priority_value; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub get_constraint { |
|
157
|
4
|
|
|
4
|
1
|
57
|
my $class = shift; |
|
158
|
4
|
100
|
|
|
|
15
|
if (ref $class) { |
|
159
|
1
|
|
|
|
|
29
|
REST::Neo4p::ClassOnlyException->throw("get_constraint is a class method only\n"); |
|
160
|
|
|
|
|
|
|
} |
|
161
|
3
|
|
|
|
|
10
|
my ($tag) = @_; |
|
162
|
3
|
|
|
|
|
25
|
return $CONSTRAINT_TABLE->{$tag}; |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub get_all_constraints { |
|
166
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
|
167
|
0
|
0
|
|
|
|
0
|
if (ref $class) { |
|
168
|
0
|
|
|
|
|
0
|
REST::Neo4p::ClassOnlyException->throw("get_constraint is a class method only\n"); |
|
169
|
|
|
|
|
|
|
} |
|
170
|
0
|
|
|
|
|
0
|
return %{$CONSTRAINT_TABLE}; |
|
|
0
|
|
|
|
|
0
|
|
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub drop { |
|
174
|
11
|
|
|
11
|
0
|
2605
|
my $self = shift; |
|
175
|
11
|
|
|
|
|
36
|
delete $CONSTRAINT_TABLE->{$self->tag}; |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub drop_constraint { |
|
179
|
1
|
|
|
1
|
0
|
2
|
my $class = shift; |
|
180
|
1
|
50
|
|
|
|
4
|
if (ref $class) { |
|
181
|
0
|
|
|
|
|
0
|
REST::Neo4p::ClassOnlyException->throw("get_constraint is a class method only\n"); |
|
182
|
|
|
|
|
|
|
} |
|
183
|
1
|
|
|
|
|
2
|
my ($tag) = @_; |
|
184
|
1
|
|
|
|
|
16
|
delete $CONSTRAINT_TABLE->{$tag}; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub add_constraint { |
|
188
|
0
|
|
|
0
|
1
|
0
|
REST::Neo4p::AbstractMethodException->throw("Cannot call add_constraint() from the Constraint parent class\n"); |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub remove_constraint { |
|
192
|
0
|
|
|
0
|
1
|
0
|
REST::Neo4p::AbstractMethodException->throw("Cannot call remove_constraint() from the Constraint parent class\n"); |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub set_condition { |
|
196
|
0
|
|
|
0
|
1
|
0
|
REST::Neo4p::AbstractMethodException->throw("Cannot call set_condition() from the Constraint parent class\n"); |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# return the first property constraint according to priority |
|
200
|
|
|
|
|
|
|
# that the property hash arg satisfies, or false if no match |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub validate_properties { |
|
203
|
|
|
|
|
|
|
# my $class = shift; |
|
204
|
|
|
|
|
|
|
# Exported |
|
205
|
20
|
|
|
20
|
1
|
37
|
my ($properties) = @_; |
|
206
|
20
|
50
|
|
|
|
40
|
return unless defined $properties; |
|
207
|
|
|
|
|
|
|
# if (ref $class) { |
|
208
|
|
|
|
|
|
|
# REST::Neo4p::ClassOnlyException->throw("validate_properties() is a class-only method\n"); |
|
209
|
|
|
|
|
|
|
# } |
|
210
|
|
|
|
|
|
|
|
|
211
|
20
|
50
|
33
|
|
|
79
|
unless ( (ref($properties) =~ /Neo4p::(Node|Relationship)$/) || |
|
212
|
|
|
|
|
|
|
(ref($properties) eq 'HASH') ) { |
|
213
|
0
|
|
|
|
|
0
|
REST::Neo4p::LocalException->throw("Arg to validate_properties() must be a hashref, a Node object, or a Relationship object"); |
|
214
|
|
|
|
|
|
|
} |
|
215
|
|
|
|
|
|
|
my $type = (ref($properties) =~ /Neo4p/) ? $properties->entity_type : |
|
216
|
20
|
50
|
50
|
|
|
84
|
(delete $properties->{__type} || ''); |
|
217
|
20
|
|
|
|
|
53
|
my @prop_constraints = grep { $_->type =~ /${type}_property$/ } values %$CONSTRAINT_TABLE; |
|
|
160
|
|
|
|
|
306
|
|
|
218
|
20
|
|
|
|
|
73
|
@prop_constraints = sort {$b->priority <=> $a->priority} @prop_constraints; |
|
|
160
|
|
|
|
|
254
|
|
|
219
|
20
|
|
|
|
|
31
|
my $ret; |
|
220
|
20
|
|
|
|
|
38
|
foreach (@prop_constraints) { |
|
221
|
66
|
100
|
|
|
|
141
|
if ($_->validate($properties)) { |
|
222
|
19
|
|
|
|
|
35
|
$ret = $_; |
|
223
|
19
|
|
|
|
|
66
|
last; |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
} |
|
226
|
20
|
|
|
|
|
63
|
return $ret; |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub validate_relationship { |
|
230
|
|
|
|
|
|
|
# my $class = shift; |
|
231
|
|
|
|
|
|
|
# Exported |
|
232
|
2
|
|
|
2
|
1
|
10
|
my ($from, $to, $reln_type, $reln_props) = @_; |
|
233
|
2
|
|
|
|
|
5
|
my ($reln) = @_; |
|
234
|
|
|
|
|
|
|
# if (ref $class) { |
|
235
|
|
|
|
|
|
|
# REST::Neo4p::ClassOnlyException->throw("validate_relationship() is a class-only method\n"); |
|
236
|
|
|
|
|
|
|
# } |
|
237
|
2
|
50
|
|
|
|
8
|
return unless defined $from; |
|
238
|
2
|
50
|
33
|
|
|
35
|
unless ( (ref($reln) =~ /Neo4p::Relationship$/) || |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
239
|
|
|
|
|
|
|
( (ref($from) =~ /Neo4p::Node|HASH$/) && (ref($to) =~ /Neo4p::Node|HASH$/) && |
|
240
|
|
|
|
|
|
|
defined $reln_type ) ) { |
|
241
|
0
|
|
|
|
|
0
|
REST::Neo4p::LocalException->throw("validate_relationship() requires a Relationship object, or two property hashrefs or nodes followed by a relationship type\n"); |
|
242
|
|
|
|
|
|
|
} |
|
243
|
2
|
|
|
|
|
10
|
my @reln_constraints = grep {$_->type eq 'relationship'} values %$CONSTRAINT_TABLE; |
|
|
16
|
|
|
|
|
30
|
|
|
244
|
2
|
|
|
|
|
9
|
@reln_constraints = sort {$a->priority <=> $b->priority} @reln_constraints; |
|
|
2
|
|
|
|
|
11
|
|
|
245
|
2
|
|
|
|
|
4
|
my $ret; |
|
246
|
2
|
|
|
|
|
6
|
foreach (@reln_constraints) { |
|
247
|
3
|
100
|
|
|
|
10
|
if ($_->validate($from => $to, $reln_type, $reln_props)) { |
|
248
|
1
|
|
|
|
|
2
|
$ret = $_; |
|
249
|
1
|
|
|
|
|
3
|
last; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
} |
|
252
|
2
|
|
|
|
|
12
|
return $ret; |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub validate_relationship_type { |
|
256
|
|
|
|
|
|
|
# my $class = shift; |
|
257
|
|
|
|
|
|
|
# Exported |
|
258
|
11
|
|
|
11
|
1
|
23
|
my ($reln_type) = @_; |
|
259
|
|
|
|
|
|
|
# if (ref $class) { |
|
260
|
|
|
|
|
|
|
# REST::Neo4p::ClassOnlyException->throw("validate_relationhip_type() is a class-only method\n"); |
|
261
|
|
|
|
|
|
|
# } |
|
262
|
11
|
50
|
|
|
|
36
|
return unless defined $reln_type; |
|
263
|
11
|
|
|
|
|
34
|
my @type_constraints = grep {$_->type eq 'relationship_type'} values %$CONSTRAINT_TABLE; |
|
|
88
|
|
|
|
|
158
|
|
|
264
|
11
|
|
|
|
|
28
|
@type_constraints = sort {$a->priority <=> $b->priority} @type_constraints; |
|
|
0
|
|
|
|
|
0
|
|
|
265
|
11
|
|
|
|
|
14
|
my $ret; |
|
266
|
11
|
|
|
|
|
25
|
foreach (@type_constraints) { |
|
267
|
11
|
100
|
|
|
|
31
|
if ($_->validate($reln_type)) { |
|
268
|
10
|
|
|
|
|
21
|
$ret = $_; |
|
269
|
10
|
|
|
|
|
19
|
last; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
} |
|
272
|
11
|
|
|
|
|
43
|
return $ret; |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub serialize_constraints { |
|
276
|
1
|
|
|
1
|
1
|
6
|
my $json = sprintf "%s", join(", ", map { $jobj->encode($_) } values %$CONSTRAINT_TABLE); |
|
|
6
|
|
|
|
|
27
|
|
|
277
|
1
|
|
|
|
|
7
|
return "[$json]"; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
sub load_constraints { |
|
281
|
1
|
|
|
1
|
1
|
562
|
my ($json) = @_; |
|
282
|
1
|
|
|
|
|
3
|
eval { |
|
283
|
1
|
|
|
|
|
25
|
$json = decode_json($json); |
|
284
|
|
|
|
|
|
|
}; |
|
285
|
1
|
50
|
|
|
|
13
|
if (my $e = Exception::Class->caught()) { |
|
286
|
0
|
|
|
|
|
0
|
REST::Neo4p::LocalException->throw("JSON error: $e"); |
|
287
|
|
|
|
|
|
|
} |
|
288
|
1
|
|
|
|
|
12
|
for (@$json) { |
|
289
|
6
|
|
|
|
|
16
|
REST::Neo4p::Constraint->new_from_json($_); |
|
290
|
|
|
|
|
|
|
} |
|
291
|
1
|
|
|
|
|
5
|
return 1; |
|
292
|
|
|
|
|
|
|
} |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=head1 NAME |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
REST::Neo4p::Constraint - Application-level Neo4j Constraints |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
See L, |
|
301
|
|
|
|
|
|
|
L, |
|
302
|
|
|
|
|
|
|
L for examples. |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
Objects of class REST::Neo4p::Constraint are used to capture and |
|
307
|
|
|
|
|
|
|
organize L application level constraints on Neo4j Node |
|
308
|
|
|
|
|
|
|
and Relationship content. |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
The L module provides a more convenient |
|
311
|
|
|
|
|
|
|
factory for REST::Neo4p::Constraint subclasses that specify L
|
|
312
|
|
|
|
|
|
|
property|REST::Neo4p::Constraint::Property>, L
|
|
313
|
|
|
|
|
|
|
property|REST::Neo4p::Property>, |
|
314
|
|
|
|
|
|
|
L, and |
|
315
|
|
|
|
|
|
|
L |
|
316
|
|
|
|
|
|
|
constraints. |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=head1 FLAGS |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=over |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=item C<$REST::Neo4p::Constraint::STRICT_RELN_TYPES> |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
When true, relationships are disallowed if the relationship type does |
|
325
|
|
|
|
|
|
|
not meet any current relationship type constraint. Default is true. |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=item C<$REST::Neo4p::Constraint::STRICT_RELN_PROPS> |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
When true, relationships are disallowed if their relationship |
|
330
|
|
|
|
|
|
|
properties do not meet any current relationship property constraint. |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
Default is false. This is so relationships without properties can be |
|
333
|
|
|
|
|
|
|
made freely. When relationship property checking is strict, you can |
|
334
|
|
|
|
|
|
|
allow relationships without properties by setting the following |
|
335
|
|
|
|
|
|
|
constraint: |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
create_constraint( |
|
338
|
|
|
|
|
|
|
tag => 'free_reln_prop', |
|
339
|
|
|
|
|
|
|
type => 'relationship_property', |
|
340
|
|
|
|
|
|
|
rtype => '*', |
|
341
|
|
|
|
|
|
|
condition => 'all', |
|
342
|
|
|
|
|
|
|
constraints => {} |
|
343
|
|
|
|
|
|
|
); |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=back |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=head1 METHODS |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=head2 Class Methods |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=over |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=item new() |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
$reln_pc = REST::Neo4p::Constraint::RelationshipProperty->new($constraints); |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Constructor. Construction also registers the constraint for |
|
358
|
|
|
|
|
|
|
validation. See subclass pod for details. |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=item get_constraint() |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
$c = REST::Neo4p::Constraint->get_constraint('spiffy_node'); |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
Get a registered constraint by constraint tag. Returns false if none found. |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=item get_all_constraints() |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
%constraints = REST::Neo4p::Constraint->get_all_constraints(); |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
Get a hash of all registered constraint objects, keyed by constraint tag. |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=back |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=head2 Instance Methods |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=over |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=item tag() |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=item type() |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=item condition() |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=item set_condition() |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
$reln_c->set_condition('only'); |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
Set the group condition for the constraint. See subclass pod for details. |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=item priority() |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=item set_priority() |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
$node_pc->set_priority(10); |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Constraints with larger priority values are checked before those with |
|
397
|
|
|
|
|
|
|
smaller values by the L|/Functional interface for |
|
398
|
|
|
|
|
|
|
validation> functions. |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=item constraints() |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
Returns the hashref of constraints. Format depends on the subclass. |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=item add_constraint() |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
$node_pc->add_constraint( 'warning_level' => qr/^[0-9]$/ ); |
|
407
|
|
|
|
|
|
|
$reln_c->add_constraint( { 'species' => 'genus' } ); |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
Add an individual constraint specification to an existing constraint |
|
410
|
|
|
|
|
|
|
object. See subclass pod for details. |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=item remove_constraint() |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
$node_pc->remove_constraint( 'warning_level' ); |
|
415
|
|
|
|
|
|
|
$reln_c->remove_constraint( { 'genus' => 'species' } ); |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Remove an individual constraint specification from an existing |
|
418
|
|
|
|
|
|
|
constraint object. See subclass pod for details. |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=back |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head2 Functional interface for validation |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=over |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=item validate_properties() |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
validate_properties( $node_object ) |
|
429
|
|
|
|
|
|
|
validate_properties( $relationship_object ); |
|
430
|
|
|
|
|
|
|
validate_properties( { name => 'Steve', instrument => 'banjo' } ); |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=item validate_relationship() |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
validate_relationship ( $relationship_object ); |
|
435
|
|
|
|
|
|
|
validate_relationship ( $node_object1 => $node_object2, |
|
436
|
|
|
|
|
|
|
$reln_type ); |
|
437
|
|
|
|
|
|
|
validate_relationship ( { name => 'Steve', instrument => 'banjo' } => |
|
438
|
|
|
|
|
|
|
{ name => 'Marcia', instrument => 'blunt' }, |
|
439
|
|
|
|
|
|
|
'avoids' ); |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=item validate_relationship_type() |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
validate_relationship_type( 'avoids' ) |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=back |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
Functional interface. Returns the registered constraint object with |
|
448
|
|
|
|
|
|
|
the highest priority that the argument satisfies, or false if none is |
|
449
|
|
|
|
|
|
|
satisfied. |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
These methods can be exported as follows: |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
use REST::Neo4p::Constraint qw(:validate) |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
They can also be exported from L: |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
use REST::Neo4p::Constrain qw(:validate) |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=head2 Serializing and loading constraints |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=over |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=item serialize_constraints() |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
open $f, ">constraints.json"; |
|
466
|
|
|
|
|
|
|
print $f serialize_constraints(); |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
Returns a JSON-formatted representation of all currently registered |
|
469
|
|
|
|
|
|
|
constraints. |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=item load_constraints() |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
open $f, "constraints.json"; |
|
474
|
|
|
|
|
|
|
{ |
|
475
|
|
|
|
|
|
|
local $/ = undef; |
|
476
|
|
|
|
|
|
|
load_constraints(<$f>); |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Creates and registers a list of constraints specified by a JSON string |
|
480
|
|
|
|
|
|
|
as produced by L. |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=back |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
L,L, |
|
487
|
|
|
|
|
|
|
L, L, |
|
488
|
|
|
|
|
|
|
L. L, L, |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=head1 AUTHOR |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
Mark A. Jensen |
|
493
|
|
|
|
|
|
|
CPAN ID: MAJENSEN |
|
494
|
|
|
|
|
|
|
majensen -at- cpan -dot- org |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=head1 LICENSE |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Copyright (c) 2012-2022 Mark A. Jensen. This program is free software; you |
|
499
|
|
|
|
|
|
|
can redistribute it and/or modify it under the same terms as Perl |
|
500
|
|
|
|
|
|
|
itself. |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=cut |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
1; |