首页 > 解决方案 > 在 Vertex 类中实现顶点属性的类型检查

问题描述

我有一个 laravel 服务器,在我的 laravel 服务器中,我有代表 GRAPH 数据库中顶点标签的顶点类。

我所有的顶点类都扩展为 Gremlin 类,该类具有 gremlin 步骤的 orm 实现。

我的问题是我的图形数据库对其属性的数据类型并不严格,我想在我的顶点类中添加另一个层来检查进入它的属性的数据类型。

这是我的 Gremlin 课

<?php
namespace App\Database;
use \Brightzone\GremlinDriver\Connection;
use Webpatser\Uuid\Uuid;

use App\Enums\EdgesEnum;
use App\Enums\DetailTypeEnum;

use App\Database\Vertices\Detail;

class Gremlin 
{
    protected $db;
    protected $partitionKey = 'pk';
    protected $queryBuilder;
    protected $vertexLabel;

    function __construct($username = null) {
        $this->db = new Connection([
            'host' => config('graphdb.defaults.host'),
            'username' => $username ? $username : config('graphdb.defaults.username'),
            'password' => config('graphdb.defaults.password'),
            'port' => config('graphdb.defaults.port'),
            'ssl' => config('graphdb.defaults.ssl')
        ]);
    }

    public function getDb(){
        return $this->db;
    }

    public function g() {
        $this->queryBuilder = "g";
        return $this;
    }

    public function label() {
        return $this->vertexLabel;
    }

    public function V($value = null) {
        $subquery = '';
        
        if ($value) {
            $subquery = "'".$value."'";
        }
        
        $this->queryBuilder = $this->queryBuilder.".V($subquery)";
            
        return $this;
    }

    public function E($value = null) {
        $subquery = '';
        
        if ($value) {
            $subquery = "'".$value."'";
        }
        
        $this->queryBuilder = $this->queryBuilder.".E($subquery)";
            
        return $this;
    }

    public function by(...$values) {

        $subquery = '';

        foreach ($values as $index => $value) {
            if($index != 0 && ($value =='incr' || $value =='decr')){
                $subquery = $subquery."$value,";
            } else {
                $subquery = $subquery.$this->getSubQueryByValue($value).",";
            }
            
        }

        $subquery = substr($subquery, 0, -1);
            
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".by($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."by($subquery)";
        }

        return $this;
    }

    public function limit($value) {
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".limit($value)";
        } else {
            $this->queryBuilder = $this->queryBuilder."limit($value)";
        }

        return $this;
    }

    public function where($value) {
        $subquery = $this->getSubQueryByValue($value);
            
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".where($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."where($subquery)";
        }

        return $this;
    }

    public function valueMap(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery. ($value === true ? 'true' : "'$value'").",";
        }

        $subquery = substr($subquery, 0, -1);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".valueMap($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."valueMap($subquery)";
        }

        return $this;
    }

    public function has($property, $value) {
        $subquery = $this->getSubQueryByValue($value);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".has('$property', $subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."has('$property', $subquery)";
        }

        return $this;
    }

    public function _has($property) {
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".has('$property')";
        } else {
            $this->queryBuilder = $this->queryBuilder."has('$property')";
        }

        return $this;
    }

    public function order() {
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".order()";
        } else {
            $this->queryBuilder = $this->queryBuilder."order()";
        }

        return $this;
    }

    public function choose(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery. $this->getSubQueryByValue($value).",";
        }

        $subquery = substr($subquery, 0, -1);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".choose($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."choose($subquery)";
        }

        return $this;
    }

    public function is($value) {
        $subquery = $this->getSubQueryByValue($value);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".is($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."is($subquery)";
        }

        return $this;
    }

    public function inside($value1, $value2) {
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".inside($value1, $value2)";
        } else {
            $this->queryBuilder = $this->queryBuilder."inside($value1, $value2)";
        }

        return $this;
    }

    public function option($value, $traversal) {
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".option(".$this->getSubQueryByValue($value).", ".$this->getSubQueryByValue($traversal).")";
        } else {
            $this->queryBuilder = $this->queryBuilder."option(".$this->getSubQueryByValue($value).", ".$this->getSubQueryByValue($traversal).")";
        }

        return $this;
    }

    public function hasId($id) {
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".hasId('$id')";
        } else {
            $this->queryBuilder = $this->queryBuilder."hasId('$id')";
        }

        return $this;
    }

    public function getQuery(){
        return $this->queryBuilder;
    }

    public function queryBuilder(){
        return $this->queryBuilder;
    }

    public function setQueryBuilder($value) {
        $this->queryBuilder = $value;
    }

    public function from($value) {
        $subquery = $this->getSubQueryByValue($value);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".from($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."from($subquery)";
        }

        return $this;       
    }

    public function sideEffect($value) {
        $subquery = $this->getSubQueryByValue($value);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".sideEffect($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."sideEffect($subquery)";
        }

        return $this;       
    }

    public function repeat($value) {
        $subquery = $this->getSubQueryByValue($value);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".repeat($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."repeat($subquery)";
        }

        return $this;   
    }

    public function emit($value=null) {
        $subquery = '';

        if($value) {
            $subquery = $this->getSubQueryByValue($value);
        }

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".emit($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."emit($subquery)";
        }

        return $this;   
    }

    private function getSubQueryByValue($value) {
        $subquery = '';

        switch (gettype($value)) {
            case 'string':
            case 'NULL':
                $subquery = $subquery."'".addslashes($value)."'";
                break;
            case 'boolean':
                $subquery = $subquery.($value ? 'true' : 'false');
                break;
            case 'object':
                if(is_callable($value)) {
                    $subquery = $subquery.call_user_func_array($value, [new $this])->queryBuilder();
                }
                break;
            default:
                $subquery = $subquery.$value;
                break;
        }

        return $subquery;
    }

    public function to($value) {
        $subquery = $this->getSubQueryByValue($value);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".to($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."to($subquery)";
        }

        return $this;  
    }

    public function addV($value) {
        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".addV('$value')";
        } else {
            $this->queryBuilder = $this->queryBuilder."addV('$value')";
        }

        return $this;
    }

    public function property($property, $value) {
        switch (gettype($value)) {
            case 'string':
            case 'NULL':
                $value = "'" . addslashes($value) . "'";
                break;
            case 'boolean':
                $value = $value ? "true" : "false";
                break;
        }

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".property('$property', $value)";
        } else {
            $this->queryBuilder = $this->queryBuilder."property('$property', $value)";
        }
        
        return $this;
    }

    public function propertyWithNoSlashes($property, $value) {         
        if (gettype($value) == 'string' || gettype($value) == 'NULL') {
            $value = "'" . $value . "'";
        } else if (gettype($value) == 'boolean') {
            $value = $value ? "true" : "false";
        }


        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".property('$property', $value)";
        } else {
            $this->queryBuilder = $this->queryBuilder."property('$property', $value)";
        }
            
        return $this;
    }

    public function properties(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery."'".$value."',";
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".properties($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."properties($subquery)";
        }
            
        return $this;
    }

    public function as($value) {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".as('$value')";
        } else {
            $this->queryBuilder = $this->queryBuilder."__.as('$value')";
        }

        return $this;
    }

    public function addE($label) {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".addE('$label')";
        } else {
            $this->queryBuilder = $this->queryBuilder."addE('$label')";
        }

        return $this;
    }

    public function group() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".group()";
        } else {
            $this->queryBuilder = $this->queryBuilder."group()";
        }

        return $this;
    }

    public function isDirty($initalVertex, $updatedVertex) {
        foreach ($initalVertex as $key => $value) {
            
            if(isset($updatedVertex[$key]) ) {
                if($updatedVertex[$key] != $value){
                    return true;
                }
            }
        }
        
        return false;
    }

    public function softDeleteWithLog($vertexids, $geolocation) {
        $query = 'g.V()';
        
        foreach ($vertexids as $key => $id) {
            $this->db->open();

            $query = "g.V().hasId('$id')
                .property('deleted_at', ".time().")
                .addE('".EdgesEnum::DELETED."')
                .property('longitude', ".($geolocation['longitude'] ? $geolocation['longitude'] : "''").")
                .property('latitude', ".($geolocation['latitude'] ? $geolocation['latitude'] : "''").")
                .property('date', ".time().")
                .from(g.V('".auth()->user()->id."'))";
            
            $result = $this->db->send($query);

            $this->db->close();

            $query = "";
        }

        return;
    }

    public function unfold() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".unfold()";
        } else {
            $this->queryBuilder = $this->queryBuilder."unfold()";
        }

        return $this;
    }

    public function fold() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".fold()";
        } else {
            $this->queryBuilder = $this->queryBuilder."fold()";
        }

        return $this;
    }

    public function select(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            if($value == 'keys' || $value == 'values') {
                $subquery = $subquery."$value,";
            } else {
                $subquery = $subquery."'$value',";
            }
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".select($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."select($subquery)";
        }

        return $this;
    }

    public function values(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery."'$value',";
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".values($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."values($subquery)";
        }

        return $this;
    }

    public function out(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery."'$value',";
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".out($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."out($subquery)";
        }

        return $this;
    }
    
    public function in($value = null) {
        if($value) {
            $value = "'".$value."'";
        }

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".in($value)";
        } else {
            $this->queryBuilder = $this->queryBuilder."__.in($value)";
        }

        return $this;
    }

    public function inE($value = null) {
        if($value) {
            $value = "'".$value."'";
        }

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".inE($value)";
        } else {
            $this->queryBuilder = $this->queryBuilder."__.inE($value)";
        }

        return $this;
    }

    public function outE($value = null) {
        if($value) {
            $value = "'".$value."'";
        }

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".outE($value)";
        } else {
            $this->queryBuilder = $this->queryBuilder."outE($value)";
        }

        return $this;
    }

    public function inV() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".inV()";
        } else {
            $this->queryBuilder = $this->queryBuilder."__.inV()";
        }

        return $this;
    }

    public function dedup() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".dedup()";
        } else {
            $this->queryBuilder = $this->queryBuilder."__.dedup()";
        }

        return $this;
    }

    public function outV() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".outV()";
        } else {
            $this->queryBuilder = $this->queryBuilder."__.outV()";
        }

        return $this;
    }

    public function hasNot($key) {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".hasNot('$key')";
        } else {
            $this->queryBuilder = $this->queryBuilder."hasNot('$key')";
        }

        return $this;
    }

    public function coalesce(...$values) {
        $subquery = '';
        
        foreach ($values as $value) {
            $subquery = $subquery.call_user_func_array($value, [new $this])->queryBuilder().",";
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".coalesce($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."coalesce($subquery)";
        }

        return $this;
    }

    public function union(...$values) {
        $subquery = '';
        
        foreach ($values as $value) {
            $subquery = $subquery.call_user_func_array($value, [new $this])->queryBuilder().",";
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".union($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."union($subquery)";
        }

        return $this;
    }

    public function count() {
        $this->queryBuilder = $this->queryBuilder.".count()";

        $this->db->open();

        $result = $this->db->send($this->queryBuilder);

        $this->db->close();

        $this->queryBuilder = null;

        return $result;
    }

    public function _count() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".count()";
        } else {
            $this->queryBuilder = $this->queryBuilder."count()";
        }

        return $this;
    }

    public function hasLabel($value) {
        $subquery = $this->getSubQueryByValue($value);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".hasLabel($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."hasLabel($subquery)";
        }

        return $this;
    }

    public function get() {
        // try{
            $this->db->open();
        
            $result = $this->db->send($this->queryBuilder);
            
            $this->db->close();

            $this->queryBuilder = null; //set to null to reset query builder and be available to next queries

            return $result;
        // }catch(\Exception $e) {
        //     dd($this->queryBuilder);
        // }
    }

    public function or(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery. $this->getSubQueryByValue($value).",";
        }

        $subquery = substr($subquery, 0, -1);

        if($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".or($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."or($subquery)";
        }

        return $this;
    }

    public function hydrateList($list) {
        $hydratedList = [];
        foreach ($list as $key => $vertex) {
            $tempVertex = [];
            
            foreach ($vertex as $property => $value) {
                if (is_array($value)) {
                    $tempVertex[$property] = $this->getActualValue($value[0]); 

                } else {
                    $tempVertex[$property] = $this->getActualValue($value);
                }
            }
            
            array_push($hydratedList, $tempVertex);
        }

        return $hydratedList;
    }

    protected function getActualValue($value) {
        if($value ==='null') {
            return null;
        }
        if($value === 'false') {
            return false;
        }
        if($value === 'true') {
            return true;
        }
        return $value;
    }

    public function drop() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".drop()";
        } else {
            $this->queryBuilder = $this->queryBuilder."drop()";
        }
            
        return $this;
    }

    public function range($x, $y) {
        $this->queryBuilder = $this->queryBuilder.".range($x, $y)";
            
        return $this;
    }

    public function partitionKey($value) {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".property('$this->partitionKey', '$value')";
        } else {
            $this->queryBuilder = $this->queryBuilder."property('$this->partitionKey', '$value')";
        }

        return $this;
    }

    public function project(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery."'$value',";
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".project($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."project($subquery)";
        }

        return $this;
    }

    public function constant($value) {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".constant('$value')";
        } else {
            $this->queryBuilder = $this->queryBuilder."constant('$value')";
        }

        return $this;
    }

    // Predicates
    public function without(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery."'$value',";
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".without($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."without($subquery)";
        }

        return $this;
    }

    public function eq($value) {
        $subquery = $this->getSubQueryByValue($value);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".eq($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."eq($subquery)";
        }

        return $this;
    }

    public function gt($value) {
        $subquery = $this->getSubQueryByValue($value);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".gt($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."gt($subquery)";
        }

        return $this;
    }

    public function within(...$values) {
        $subquery = '';

        foreach ($values as $value) {
            $subquery = $subquery.$this->getSubQueryByValue($value).",";
        }

        $subquery = substr($subquery, 0, -1);

        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".within($subquery)";
        } else {
            $this->queryBuilder = $this->queryBuilder."within($subquery)";
        }

        return $this;
    }

    public function TextP() {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".TextP";
        } else {
            $this->queryBuilder = $this->queryBuilder."TextP";
        }

        return $this;
    }

    public function containing($value) {
        if ($this->queryBuilder) {
            $this->queryBuilder = $this->queryBuilder.".containing('$value')";
        } else {
            $this->queryBuilder = $this->queryBuilder."containing('$value')";
        }

        return $this;
    }

}

我有一个扩展到 Gremlin 类的产品类。然后当我想保存产品时,我只是这样做:

Product::g()->addV('product')
->property('pk', 'product')
->property('name', 'product1')
->property('sku', '123255')
->property('description', '')
->get();

这里的问题是name,skudescription可以是多种数据类型。

我想添加一个实现,其中属性的数据类型可以限制为一种数据类型,但我不确定什么是正确的方法。

Laravel 版本:“laravel/framework”:“^8.0” Php 版本:“php”:“^8.0”,

标签: phplaravelgremlin

解决方案


你没有说你使用什么图形数据库,但最好的选择是使用一个允许你定义模式的数据库,以便它是处理数据类型强制和强制的数据库。如果您无法切换数据库,那么您的查询构建器方法将需要简单地检查所有可能预期为多个类型的参数,并将它们转换为 Gremlin 可以理解的适当形式。在 TinkerPop 中,我们在所有Translators中进行这种类型检查。GroovyTranslator 你可以在这里看到它在 Java 中是如何工作的。Translator 您可以在此处的 Python 中看到类似的模式,它也生成了 Groovy 形式的 Gremlin。


推荐阅读