private function isValidProperty(string $property, EntityInterface $meta) : bool
 {
     if ($meta->properties()->contains($property)) {
         return true;
     }
     $property = new Str($property);
     if (!$property->match('/[a-zA-Z]+(\\.[a-zA-Z]+)+/')) {
         return false;
     }
     $pieces = $property->split('.');
     $piece = (string) $pieces->get(0);
     if (!$meta->children()->contains($piece)) {
         return false;
     }
     $child = $meta->children()->get($piece);
     $relationship = $child->relationship();
     switch ($pieces->count()) {
         case 2:
             return $relationship->properties()->contains((string) $pieces->get(1));
         case 3:
             $subPiece = (string) $pieces->get(1);
             if (!$relationship->childProperty() === $subPiece) {
                 return false;
             }
             return $child->properties()->contains((string) $pieces->get(2));
     }
     return false;
 }
 /**
  * {@inheritdoc}
  */
 public function translate(EntityInterface $meta, SpecificationInterface $specification) : IdentityMatch
 {
     $variables = new Set('string');
     try {
         $mapping = (new AggregatePropertyMatchVisitor($meta))->visit($specification);
         $query = $this->addProperties((new Query())->match('entity', $meta->labels()->toPrimitive()), 'entity', $mapping)->with('entity');
         $meta->children()->foreach(function (string $property, ValueObject $child) use(&$query, $mapping, &$variables) {
             $relName = (new Str('entity_'))->append($property);
             $childName = $relName->append('_')->append($child->relationship()->childProperty());
             $variables = $variables->add((string) $relName)->add((string) $childName);
             $query = $this->addProperties($this->addProperties($query->match('entity')->linkedTo((string) $childName, $child->labels()->toPrimitive()), (string) $childName, $mapping)->through((string) $child->relationship()->type(), (string) $relName, Relationship::LEFT), (string) $relName, $mapping);
         });
     } catch (SpecificationNotApplicableAsPropertyMatchException $e) {
         $query = (new Query())->match('entity', $meta->labels()->toPrimitive())->with('entity');
         $meta->children()->foreach(function (string $property, ValueObject $child) use(&$query, &$variables) {
             $relName = (new Str('entity_'))->append($property);
             $childName = $relName->append('_')->append($child->relationship()->childProperty());
             $variables = $variables->add((string) $relName)->add((string) $childName);
             $query = $query->match('entity')->linkedTo((string) $childName, $child->labels()->toPrimitive())->through((string) $child->relationship()->type(), (string) $relName, Relationship::LEFT);
         });
         $condition = (new AggregateCypherVisitor($meta))->visit($specification);
         $query = $query->where($condition->get(0))->withParameters($condition->get(1)->toPrimitive());
     }
     return new IdentityMatch($query->return('entity', ...$variables->toPrimitive()), (new Map('string', EntityInterface::class))->put('entity', $meta));
 }
 /**
  * {@inheritdoc}
  */
 public function translate(EntityInterface $meta, IdentityInterface $identity) : IdentityMatch
 {
     $query = (new Query())->match('entity', $meta->labels()->toPrimitive())->withProperty($meta->identity()->property(), '{entity_identity}')->withParameter('entity_identity', $identity->value())->with('entity');
     $variables = new Set('string');
     $meta->children()->foreach(function (string $property, ValueObject $child) use(&$query, &$variables) {
         $relName = (new Str('entity_'))->append($property);
         $childName = $relName->append('_')->append($child->relationship()->childProperty());
         $variables = $variables->add((string) $relName)->add((string) $childName);
         $query = $query->match('entity')->linkedTo((string) $childName, $child->labels()->toPrimitive())->through((string) $child->relationship()->type(), (string) $relName, Relationship::LEFT);
     });
     return new IdentityMatch($query->return('entity', ...$variables->toPrimitive()), (new Map('string', EntityInterface::class))->put('entity', $meta));
 }
 /**
  * {@inheritdoc}
  */
 public function extract($entity, EntityInterface $meta) : CollectionInterface
 {
     if (!$meta instanceof Aggregate) {
         throw new InvalidArgumentException();
     }
     $data = $this->extractProperties($entity, $meta->properties());
     $data = $data->set($id = $meta->identity()->property(), (new ReflectionObject($entity))->extract([$id])->get($id)->value());
     $meta->children()->foreach(function (string $property, ValueObject $child) use(&$data, $entity) {
         $data = $data->set($property, $this->extractRelationship($child, $entity));
     });
     return $data;
 }
 /**
  * {@inheritdoc}
  */
 public function make(IdentityInterface $identity, EntityInterface $meta, CollectionInterface $data)
 {
     if (!$meta instanceof Aggregate) {
         throw new InvalidArgumentException();
     }
     $reflection = (new ReflectionClass((string) $meta->class()))->withProperty($meta->identity()->property(), $identity);
     $meta->properties()->foreach(function (string $name, Property $property) use(&$reflection, $data) {
         if ($property->type()->isNullable() && !$data->hasKey($name)) {
             return;
         }
         $reflection = $reflection->withProperty($name, $property->type()->fromDatabase($data->get($name)));
     });
     $meta->children()->foreach(function (string $property, ValueObject $meta) use(&$reflection, $data) {
         $reflection = $reflection->withProperty($property, $this->buildChild($meta, $data));
     });
     return $reflection->buildObject();
 }
 private function translateNode($identity, EntityInterface $meta, ResultInterface $result) : CollectionInterface
 {
     $node = $result->nodes()->filter(function (NodeInterface $node) use($identity, $meta) {
         $id = $meta->identity()->property();
         $properties = $node->properties();
         return $properties->hasKey($id) && $properties->get($id) === $identity;
     })->first();
     $data = (new Collection([]))->set($meta->identity()->property(), $node->properties()->get($meta->identity()->property()));
     $meta->properties()->foreach(function (string $name, Property $property) use(&$data, $node) {
         if ($property->type()->isNullable() && !$node->properties()->hasKey($name)) {
             return;
         }
         $data = $data->set($name, $node->properties()->get($name));
     });
     try {
         $meta->children()->foreach(function (string $name, ValueObject $meta) use(&$data, $node, $result) {
             $data = $data->set($name, $this->translateChild($meta, $result, $node));
         });
     } catch (MoreThanOneRelationshipFoundException $e) {
         throw $e->on($meta);
     }
     return $data;
 }