Completed
Push — master ( 8fff44...e64760 )
by Richard
05:24
created

IndexDB::prepare_insert_statements()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 11
cts 11
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
nop 0
crap 1
1
<?php
2
/******************************************************************************
3
 * An implementation of dicto (scg.unibe.ch/dicto) in and for PHP.
4
 *
5
 * Copyright (c) 2016, 2015 Richard Klees <[email protected]>
6
 *
7
 * This software is licensed under The MIT License. You should have received
8
 * a copy of the license along with the code.
9
 */
10
11
namespace Lechimp\Dicto\App;
12
13
use Lechimp\Dicto\Graph;
14
use Doctrine\DBAL\Schema;
15
use Doctrine\DBAL\Types\Type;
16
use Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer;
17
use Doctrine\DBAL\Statement;
18
19
class IndexDB extends DB {
20
    protected $insert_per_transaction = 100;
21
22
    /**
23
     * Write the index to the database.
24
     *
25
     * @param   Graph\IndexDB   $index
26
     * @return  null
27
     */
28 1
    public function write_index(Graph\IndexDB $index) {
29 1
        $this->prepare_insert_statements();
30 1
        $max_id = $this->serialize_nodes($index);
31 1
        $this->serialize_relations($index, $max_id);
32 1
    }
33
34
    /**
35
     * Read the index from the database.
36
     *
37
     * @return  Graph\IndexDB   $index
38
     */
39 1
    public function read_index() {
40 1
        $index = $this->build_graph_index_db();
41 1
        $this->deserialize_nodes_to($index);
42 1
        $this->deserialize_relations_to($index);
43 1
        return $index;
44
    }
45
46
    protected function build_graph_index_db() {
47
        return new Graph\IndexDB();
48
    }
49
50
    // WRITE
51
52 1
    protected function serialize_nodes(Graph\IndexDB $index) {
53 1
        $max_id = 0;
54 1
        $this->connection->beginTransaction();
55 1
        $count = 0;
56 1
        foreach ($index->nodes() as $node) {
57 1
            $count++;
58 1
            $id = $node->id();
59 1
            $this->insert_entity_stmt->execute([$node->id(), $node->type()]);
60 1
            $max_id = max($max_id, $id);
61 1
            $this->insert_properties($id, $node);
62 1
            if ($count >= $this->insert_per_transaction) {
63
                $this->connection->commit();
64
                $this->connection->beginTransaction();
65
            }
66 1
        }
67 1
        $this->connection->commit();
68 1
        return $max_id;
69
    }
70
71 1
    protected function serialize_relations(Graph\IndexDB $index, $id) {
72 1
        $this->connection->beginTransaction();
73 1
        $count = 0;
74 1
        foreach ($index->nodes() as $node) {
75 1
            foreach ($node->relations() as $relation) {
76 1
                $count++;
77 1
                $id++;
78 1
                $this->insert_entity_stmt->execute([$id, $relation->type()]);
79 1
                $this->insert_properties($id, $relation);
80 1
                $this->insert_relation_stmt->execute([$node->id(), $id, $relation->target()->id()]);
81 1
                if ($count >= $this->insert_per_transaction) {
82
                    $this->connection->commit();
83
                    $this->connection->beginTransaction();
84
                }
85 1
            }
86 1
        }
87 1
        $this->connection->commit();
88 1
    }
89
90
    protected $insert_entity_stmt;
91
    protected $insert_property_stmt;
92
    protected $insert_relation_stmt;
93
94 1
    protected function prepare_insert_statements() {
95 1
        $this->insert_entity_stmt = $this->connection->prepare
96 1
            ( "INSERT INTO entities (id, type)\n"
97
            . "VALUES (?, ?)"
98 1
            );
99 1
        $this->insert_property_stmt = $this->connection->prepare
100 1
            ( "INSERT INTO properties (entity_id, key, is_entity, value)\n"
101
            . "VALUES (?, ?, ?, ?)"
102 1
            );
103 1
        $this->insert_relation_stmt = $this->connection->prepare
104 1
            ( "INSERT INTO relations (source_id, entity_id, target_id)\n"
105
            . "VALUES (?, ?, ?)"
106 1
            );
107 1
    }
108
109 1
    protected function insert_properties($id, Graph\Entity $e) {
110 1
        foreach ($e->properties() as $key => $value) {
111 1
            $this->insert_property($id, $key, $value);
112 1
        }
113 1
    }
114
115 1
    protected function insert_property($entity_id, $key, $value) {
116 1
        $is_entity = false;
117 1
        if ($value instanceof Graph\Node) {
118 1
            $is_entity = true;
119 1
            $value = $value->id();
120 1
        }
121 1
        if ($value instanceof Graph\Relation) {
122
            throw new \LogicException("Can't serialize Relation properties (NYI!)");
123
        }
124 1
        $value = serialize($value);
125 1
        $this->insert_property_stmt->execute([$entity_id, $key, $is_entity, $value]);
126 1
    }
127
128
    // READ
129
130 1
    protected function deserialize_nodes_to(Graph\IndexDB $index) {
131 1
        $res = $this->connection->executeQuery
132 1
            ( "SELECT entities.id, entities.type FROM entities\n"
133
            . "LEFT JOIN relations ON entities.id = relations.entity_id\n"
134 1
            . "WHERE relations.source_id IS NULL"
135 1
            );
136 1
        while ($row = $res->fetch()) {
137 1
            $properties = $this->select_properties($row["id"], $index);
138 1
            $node = $index->create_node($row["type"], $properties);
0 ignored issues
show
Unused Code introduced by
$node is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
139 1
            assert('$row["id"] == $node->id()');
140 1
        }
141 1
    }
142
143 1
    protected function deserialize_relations_to(Graph\IndexDB $index) {
144 1
        $res = $this->connection->executeQuery
145 1
            ( "SELECT source_id, entity_id, target_id, type FROM relations\n"
146
            . "JOIN entities ON entities.id = relations.entity_id\n"
147 1
            . "ORDER BY source_id, entity_id"
148 1
            );
149 1
        $node = null;
150 1
        while ($row = $res->fetch()) {
151 1
            if ($node === null || $node->id() != $row["source_id"]) {
152 1
                $node = $index->node((int)$row["source_id"]);
153 1
            }
154 1
            $other = $index->node((int)$row["target_id"]);
155 1
            $properties = $this->select_properties($row["entity_id"], $index);
156 1
            $node->add_relation($row["type"], $properties, $other);
157 1
        }
158 1
    }
159
160
161 1
    protected function select_properties($id, $index) {
162 1
        $id = (int)$id;
163 1
        $res = $this->connection->executeQuery
164 1
            ( "SELECT key, is_entity, value FROM properties\n"
165 1
            . "WHERE entity_id = $id"
166 1
            );
167 1
        $props = [];
168 1
        while ($row = $res->fetch()) {
169 1
            $value = unserialize($row["value"]);
170 1
            if ($row["is_entity"]) {
171 1
                $value = $index->node($value);
172 1
            }
173 1
            $props[$row["key"]] = $value;
174 1
        }
175 1
        return $props;
176
    }
177
178
    // INIT
179
180 1
    public function init_database_schema() {
181 1
        $schema = new Schema\Schema();
182
183 1
        $entity_table = $schema->createTable("entities");
184 1
        $entity_table->addColumn
185 1
            ("id", "integer"
186 1
            , ["notnull" => true, "unsigned" => true]
187 1
            );
188 1
        $entity_table->addColumn
189 1
            ( "type", "string"
190 1
            , ["notnull" => true]
191 1
            );
192 1
        $entity_table->setPrimaryKey(["id"]);
193
194 1
        $property_table = $schema->createTable("properties");
195 1
        $property_table->addColumn
196 1
            ("entity_id", "integer"
197 1
            , ["notnull" => true, "unsigned" => true]
198 1
            );
199 1
        $property_table->addColumn
200 1
            ( "key", "string"
201 1
            , ["notnull" => true]
202 1
            );
203 1
        $property_table->addColumn
204 1
            ( "is_entity", "boolean"
205 1
            , ["notnull" => true]
206 1
            );
207 1
        $property_table->addColumn
208 1
            ( "value", "string"
209 1
            , ["notnull" => true]
210 1
            );
211 1
        $property_table->addForeignKeyConstraint
212 1
            ( $entity_table
213 1
            , array("entity_id")
214 1
            , array("id")
215 1
            );
216 1
        $property_table->setPrimaryKey(["entity_id", "key"]);
217
218 1
        $relation_table = $schema->createTable("relations");
219 1
        $relation_table->addColumn
220 1
            ( "source_id", "integer"
221 1
            , ["notnull" => true, "unsigned" => true]
222 1
            );
223 1
        $relation_table->addColumn
224 1
            ( "entity_id", "integer"
225 1
            , ["notnull" => true, "unsigned" => true]
226 1
            );
227 1
        $relation_table->addColumn
228 1
            ( "target_id", "integer"
229 1
            , ["notnull" => true, "unsigned" => true]
230 1
            );
231 1
        $relation_table->addForeignKeyConstraint
232 1
            ( $entity_table
233 1
            , array("source_id")
234 1
            , array("id")
235 1
            );
236 1
        $relation_table->addForeignKeyConstraint
237 1
            ( $entity_table
238 1
            , array("entity_id")
239 1
            , array("id")
240 1
            );
241 1
        $relation_table->addForeignKeyConstraint
242 1
            ( $entity_table
243 1
            , array("target_id")
244 1
            , array("id")
245 1
            );
246 1
        $relation_table->setPrimaryKey(["entity_id"]);
247
248 1
        $sync = new SingleDatabaseSynchronizer($this->connection);
249 1
        $sync->createSchema($schema);
250 1
    }
251
}
252