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; |