Mark Allen Jensen > REST-Neo4p-0.2222 > REST::Neo4p::Constrain

Download:
REST-Neo4p-0.2222.tar.gz

Dependencies

Annotate this POD

CPAN RT

New  3
Open  3
View/Report Bugs
Module Version: 0.2002   Source   Latest Release: REST-Neo4p-0.2242

NAME ^

REST::Neo4p::Constrain - Create and apply Neo4j app-level constraints

SYNOPSIS ^

 use REST::Neo4p;
 use REST::Neo4p::Constrain qw(:all); # not included by REST::Neo4p
 
 # create some constraints
 
  create_constraint (
  tag => 'owner',
  type => 'node_property',
  condition => 'only',
  constraints => {
    name => qr/[a-z]+/i,
    species => 'human'
  }
 );
 
 create_constraint(
  tag => 'pet',
  type => 'node_property',
  condition => 'all',
  constraints => {
    name => qr/[a-z]+/i,
    species => qr/^dog|cat|ferret|mole rat|platypus$/
  }
 );
 
 create_constraint(
  tag => 'OWNS_props',
  type => 'relationship_property',
  rtype => 'OWNS',
  condition => 'all',
  constraints => {
    year_purchased => qr/^20[0-9]{2}$/
  }
 );

 create_constraint(
  tag => 'owners_own_pets',
  type => 'relationship',
  rtype => 'OWNS',
  constraints =>  [{ owner => 'pet' }] # note arrayref
 );

 create_constraint(
  tag => 'loves',
  type => 'relationship',
  rtype => 'LOVES',
  constraints =>  [{ pet => 'owner' },
                   { owner => 'pet' }] # both directions ok
 );

 create_constraint(
  tag => 'ignore'
  type => 'relationship',
  rtype => 'IGNORES',
  constraints =>  [{ pet => 'owner' },
                   { owner => 'pet' }] # both directions ok
 );

 create_constraint(
  tag => 'allowed_rtypes',
  type => 'relationship_type',
  constraints => [qw( OWNS FEEDS LOVES )] 
  # IGNORES is missing, see below
 );

 # constrain by automatic exception-throwing

 constrain();

 $fred = REST::Neo4p::Node->new( 
  { name => 'fred', species => 'human' }
 );
 $fluffy = REST::Neo4p::Node->new( 
  { name => 'fluffy', species => 'mole rat' }
 );

 $r1 = $fred->relate_to(
  $fluffy, 'OWNS', {year_purchased => 2010}
 ); # valid
 eval {
   $r2 = $fluffy->relate_to($fred, 'OWNS', {year_purchased => 2010});
 };
 if (my $e = REST::Neo4p::ConstraintException->caught()) {
   print STDERR "Pet can't own an owner, ignored\n";
 }

 eval {
   $r3 = $fluffy->relate_to($fred, 'IGNORES');
 };
 if (my $e = REST::Neo4p::ConstraintException->caught()) {
   print STDERR "Pet can't ignore an owner, ignored\n";
 }

 # allow relationship types that are not explictly
 # allowed -- a relationship constraint is still required

 $REST::Neo4p::Constraint::STRICT_RELN_TYPES = 0;

 $r3 = $fluffy->relate_to($fred, 'IGNORES'); # no throw now

 relax(); # stop automatic constraints

 # use validation

 $r2 = $fluffy->relate_to(
  $fred, 'OWNS',
  {year_purchased => 2010}
 ); # not valid, but auto-constraint not in force

 if ( validate_properties($r2) ) {
   print STDERR "Relationship properties are valid\n";
 }
 if ( !validate_relationship($r2) ) {
   print STDERR 
    "Relationship does not meet constraints, ignoring...\n";
 }

 # try a relationship

 if ( validate_relationship( $fred => $fluffy, 'LOVES' ) {
   $fred->relate_to($fluffy, 'LOVES');
 }
 else {
   print STDERR 
    "Prospective relationship fails constraints, ignoring...\n";
 }

 # try a relationship type

 if ( validate_relationship( $fred => $fluffy, 'EATS' ) {
   $fred->relate_to($fluffy, 'EATS');
 }
 else {
   print STDERR 
    "Relationship type disallowed, ignoring...\n";
 }

 # serialize all constraints

 open $f, ">my_constraints.json";
 print $f serialize_constraints();
 close $f;

 # remove current constraints

 while ( my ($tag, $constraint) = 
           each REST::Neo4p::Constraint->get_all_constraints ) {
   $constraint->drop;
 }

 # restore constraints

 open $f, "my_constraints.json";
 local $/ = undef;
 $json = <$f>;
 load_constraints($json);

DESCRIPTION ^

Neo4j, as a NoSQL database, is intentionally lenient. One of its only hardwired constraints is its refusal to remove a Node that is involved in a Relationship. Other constraints to database content (properties and their values, "kinds" of relationships, and relationship types) must be applied at the application level.

REST::Neo4p::Constrain and REST::Neo4p::Constraint attempt to provide a flexible framework for creating and enforcing Neo4j content constraints for applications using REST::Neo4p.

The use case that inspired these modules is the following: You start out with a set of well categorized things, that have some well defined relationships. Each thing will be represented as a node, that's fine. But you want to guarantee (to your client, for example) that

1. You can classify every node you add or read unambiguously into a well-defined group;
2. You never relate two nodes belonging to particular groups in a way that doesn't make sense according to your well-defined relationships.

These modules allow you to create a set of constraints on node and relationship properties, relationships themselves, and relationship types to meet this use case and others. It is flexible, in that you can choose the level at which the validation is applied:

The "SYNOPSIS" is a full example.

Types of Constraints

REST::Neo4p::Constrain handles four types of constraints.

Specifying Constraints

REST::Neo4p::Constrain exports create_constraint(), which creates and registers the different constraint types. (It also returns the REST::Neo4p::Constraint object so created, which can be useful.)

create_constraint accepts a hash of parameters. The following are required:

 create_constraint(
  tag => $tag, # a (preferably) simple and meaningful alias for this
               # constraint
  type => $type, # node_property|relationship_property|
                 # relationship|relationship_type
  priority => $integer_priority, # to determine which constraints
                                 # are evaluated first during validation
  constraints => $constraints, # a reference that depends on the
                             # constraint type, see below
 );

Other parameters and the form of the constraint values depend on the constraint type:

Using Constraints

create_constraint() registers the created constraint so that it is included in all relevant validations.

drop_constraint() deregisters and removes the constraint specified by its tag:

 drop_constraint('owner');
 drop_constraint('pet');

Automatic validation

Execute constrain() to force REST::Neo4p to raise a REST::Neo4p::ConstraintException whenever the construction or modification of a Node or Relationship would violate the registered constraints.

Executing relax() causes REST::Neo4p to ignore all constraint and create and modify entities as usual.

constrain() and relax() can be used anywhere at any time. The effects are global.

When constrain() is in force, any new constraints created are immediately available to the validation.

"Manual" validation

To control validation directly, use the :validate export tag:

 use REST::Neo4p::Constrain qw(:validate);

This provides three functions for checking properties, relationships, and relationship types against registered constraints. They return true if the object or spec satisfies the current constraints and false if it violates the current constraints. No constraint exceptions are raised.

Controlling relationship validation strictness

You can set whether relationship types or relationship properties are strictly validated or not, even when constraints are in force. Relaxing one or both of these can allow you to follow constraints you have defined strictly, while enabling other kinds of relationships to be created ad hoc outside of validation.

See REST::Neo4p::Constraint for details.

FUNCTIONS ^

Exported by default

create_constraint()
 create_constraint( 
  tag => $meaningful_tag,
  type => $constraint_type,   # [node_property|relationship_property|
                              #  relationship|relationship_type]
  condition => $condition     # all|only|none, depends on type
  rtype => $relationship_type # relationship type tag
  constraints => $spec_ref    # hashref or arrayref, depends on type
 );

Creates and registers a constraint. Returns the created REST::Neo4p::Constraint object.

See "Specifying Constraints" for details.

drop_constraint()
 drop_constraint($constraint_tag);

Deregisters a constraint identified by its tag. Returns the constraint object.

constrain()
relax()
 constrain();
 eval {
   $node = REST::Neo4p::Node->create({foo => bar, baz => 1});
 };
 if ($e = REST::Neo4p::ConstraintException->caught()) {
   relax();
   print "Got ".$e->msg.", but creating anyway\n";
   $node = REST::Neo4p::Node->create({foo => bar, baz => 1});
 }

constrain() forces REST::Neo4p constructors and property setters to comply with the currently registered constraints. REST::Neo4p::Exceptionss are thrown if constraints are not met.

relax() turns off the automatic validation of constrain().

Effects are global.

Serialization functions

Use these functions to freeze and thaw the currently registered constraints to and from a JSON representation.

Import with

 use REST::Neo4p::Constrain qw(:serialize);
serialize_constraints()
 open $f, ">constraints.json";
 print $f serialize_constraints();

Returns a JSON-formatted representation of all currently registered constraints.

load_constraints()
 open $f, "constraints.json";
 {
   local $/ = undef;
   load_constraints(<$f>);
 }

Creates and registers a list of constraints specified by a JSON string as produced by "serialize_constraints()".

Validation functions

Functional interface. Returns the registered constraint object with the highest priority that the argument satisfies, or false if no constraint is satisfied.

Import with

 use REST::Neo4p::Constrain qw(:validate);
validate_properties()
 validate_properties( $node_object )
 validate_properties( $relationship_object );
 validate_properties( { name => 'Steve', instrument => 'banjo' } );
validate_relationship()
 validate_relationship ( $relationship_object );
 validate_relationship ( $node_object1 => $node_object2, 
                         $reln_type );
 validate_relationship ( { name => 'Steve', instrument => 'banjo' } =>
                         { name => 'Marcia', instrument => 'blunt' },
                         'avoids' );
validate_relationship_type()
 validate_relationship_type( 'avoids' )

These methods can also be exported from REST::Neo4p::Constraint.

SEE ALSO ^

REST::Neo4p, REST::Neo4p::Constraint

AUTHOR ^

    Mark A. Jensen
    CPAN ID: MAJENSEN
    majensen -at- cpan -dot- org

LICENSE ^

Copyright (c) 2012-2013 Mark A. Jensen. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

syntax highlighting: