|
5 | 5 | [clojure.string :as str] |
6 | 6 | [clojure.core.cache :as c] |
7 | 7 | [schema.core :as s] |
8 | | - [naga.schema.structs :as st :refer [EPVPattern FilterPattern Pattern Results Value]] |
| 8 | + [naga.schema.structs :as st :refer [EPVPattern FilterPattern Pattern Results Value Axiom]] |
9 | 9 | [naga.store :as store] |
10 | 10 | [naga.util :as u] |
| 11 | + [naga.storage.store-util :as store-util] |
11 | 12 | [naga.storage.memory.index :as mem]) |
12 | 13 | (:import [clojure.lang Symbol IPersistentVector IPersistentList] |
13 | 14 | [naga.store Storage])) |
|
166 | 167 |
|
167 | 168 | (s/defn modify-pattern :- [s/Any] |
168 | 169 | "Creates a new EPVPattern from an existing one, based on existing bindings. |
169 | | - Uses the mapping to copy from columns in 'existing' to overwrite variableis in 'pattern'. |
| 170 | + Uses the mapping to copy from columns in 'existing' to overwrite variables in 'pattern'. |
170 | 171 | The variable locations have already been found and are in the 'mapping' argument" |
171 | 172 | [existing :- [Value] |
172 | 173 | mapping :- {s/Num s/Num} |
|
269 | 270 | {:cols (st/vars fpath)})] |
270 | 271 | (reduce ljoin part-result rpath))) |
271 | 272 |
|
| 273 | +(s/defn group-exists? :- [s/Any] |
| 274 | + "Determines if a group is instantiating a new piece of data, |
| 275 | + and if so checks if it already exists." |
| 276 | + [storage |
| 277 | + group :- [Axiom]] |
| 278 | + (let [[entity _ val] (some (fn [[_ a _ :as axiom]] (when (= a :db/ident) axiom)) group)] |
| 279 | + (seq (store/resolve-pattern storage ['?e :db/ident val])))) |
| 280 | + |
| 281 | +(s/defn offset-mappings :- {s/Num s/Num} |
| 282 | + "Build a pattern->data mapping that returns offsets into a pattern mapped to corresponding |
| 283 | + offsets into data. If a data offset is negative, then this indicates a node must be built |
| 284 | + instead of reading from the data." |
| 285 | + [storage |
| 286 | + full-pattern :- [s/Any] |
| 287 | + data :- Results] |
| 288 | + (let [data-vars (:cols (meta data)) |
| 289 | + known-vars (set data-vars) |
| 290 | + var-positions (matching-vars full-pattern data-vars) |
| 291 | + fresh-map (->> full-pattern |
| 292 | + (filter #(and (st/vartest? %) (not (known-vars %)))) |
| 293 | + set |
| 294 | + (map-indexed (fn [n v] [v (- (inc n))])) |
| 295 | + (into {}))] |
| 296 | + (->> full-pattern |
| 297 | + (map-indexed |
| 298 | + (fn [n v] (if (and (nil? (var-positions n)) (st/vartest? v)) [n (fresh-map v)]))) |
| 299 | + (filter identity) |
| 300 | + (into var-positions)))) |
| 301 | + |
| 302 | +(s/defn new-nodes :- [s/Num] |
| 303 | + "Returns all the new node references that appears in a map of offsets. |
| 304 | + Node references are negative numbers." |
| 305 | + [offset-map :- {s/Num s/Num}] |
| 306 | + (seq (set (filter neg? (vals offset-map))))) |
272 | 307 |
|
273 | 308 | (s/defn project :- Results |
274 | | - "Converts each row from a result, into just the requested columns, as per the pattern arg. |
275 | | - Any specified value in the pattern will be copied into that position in the projection. |
276 | | - e.g. For pattern [?h1 :friend ?h2] |
| 309 | + "Converts each row from a result, into just the requested columns, as per the patterns arg. |
| 310 | + Any specified value in the patterns will be copied into that position in the projection. |
| 311 | + Unbound patterns will generate new nodes for each row. |
| 312 | + e.g. For patterns [[?h1 :friend ?h2]] |
277 | 313 | data: [[h1=frodo h3=bilbo h2=gandalf] |
278 | 314 | [h1=merry h3=pippin h2=frodo]] |
279 | 315 | leads to: [[h1=frodo :friend h2=gandalf] |
280 | 316 | [h1=merry :friend h2=frodo]]" |
281 | | - [pattern :- [s/Any] |
| 317 | + [storage |
| 318 | + patterns :- [[s/Any]] |
| 319 | + data :- Results] |
| 320 | + (let [full-pattern (vec (apply concat patterns)) |
| 321 | + pattern->data (offset-mappings storage full-pattern data) |
| 322 | + nodes (new-nodes pattern->data)] |
| 323 | + (map #(store-util/project-row storage full-pattern nodes pattern->data %) data))) |
| 324 | + |
| 325 | +(s/defn insert-project :- Results |
| 326 | + "Similar to project, only the generated data will be in triples for insertion. |
| 327 | + If triples describe entities with existing dc/ident fields, then they will be dropped." |
| 328 | + [storage |
| 329 | + patterns :- [[s/Any]] |
282 | 330 | data :- Results] |
283 | | - (let [pattern->data (matching-vars pattern (:cols (meta data)))] |
284 | | - (map #(modify-pattern % pattern->data pattern) data))) |
| 331 | + (let [full-pattern (vec (apply concat patterns)) |
| 332 | + pattern->data (offset-mappings storage full-pattern data) |
| 333 | + nodes (new-nodes pattern->data)] |
| 334 | + (->> data |
| 335 | + (map #(partition 3 (store-util/project-row storage full-pattern nodes pattern->data %))) |
| 336 | + (remove (partial group-exists? storage)) |
| 337 | + (apply concat)))) |
285 | 338 |
|
286 | 339 | (s/defn add-to-graph |
287 | 340 | [graph |
|
333 | 386 | (count-fn pattern) |
334 | 387 | (count (mem/resolve-pattern graph pattern)))) |
335 | 388 |
|
336 | | - (query [_ output-pattern patterns] |
337 | | - (project output-pattern (join-patterns graph patterns))) |
| 389 | + (query [this output-pattern patterns] |
| 390 | + (project this output-pattern (join-patterns graph patterns))) |
338 | 391 |
|
339 | 392 | (assert-data [_ data] |
340 | 393 | (->MemoryStore (add-to-graph graph data))) |
341 | 394 |
|
342 | | - (query-insert [this assertion-pattern patterns] |
| 395 | + (query-insert [this assertion-patterns patterns] |
343 | 396 | (->> (join-patterns graph patterns) |
344 | | - (project assertion-pattern) |
| 397 | + (insert-project this assertion-patterns) |
345 | 398 | (add-to-graph graph) |
346 | 399 | ->MemoryStore))) |
347 | 400 |
|
|
0 commit comments