This repository has been archived by the owner on Jan 28, 2019. It is now read-only.
/
impl.clj
344 lines (259 loc) · 10.9 KB
/
impl.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
(ns shelving.impl
"Implementer API for Shelving stores.
Store consumers should prefer the API provided by `shelving.core`."
{:authors ["Reid \"arrdem\" McKenzie <me@arrdem.com>"],
:license "Eclipse Public License 1.0",
:added "0.0.0"}
(:refer-clojure :exclude [flush get])
(:require [clojure.tools.logging :as log]
[clojure.spec.alpha :as s]
[hasch.core :refer [uuid]]
[shelving.schema :as schema])
(:import me.arrdem.UnimplementedOperationException))
(defn- dx
([{:keys [type]}] type)
([{:keys [type]} _] type)
([{:keys [type]} _ _] type)
([{:keys [type]} _ _ _] type)
([{:keys [type]} _ _ _ _] type)
([{:keys [type]} _ _ _ _ _] type))
(defmacro ^:private required! [method]
`(defmethod ~method :default [~'& args#]
(throw
(UnimplementedOperationException.
(format "Shelves must implement method %s, no implementation for dispatch tag %s"
(pr-str (var ~method)) (apply dx args#))))))
;; Intentional interface for shelves
;;--------------------------------------------------------------------------------------------------
(defmulti open
"Opens a shelf for reading or writing.
Shelves must implement this method."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([config])}
#'dx)
(required! open)
(defmulti flush
"Flushes (commits) an open shelf.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn])}
#'dx)
(required! flush)
(defmulti close
"Closes an open shelf.
Shelves may implement this method.
By default just flushes."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn])}
#'dx)
(defmethod close :default [t]
(flush t))
(defmulti put-spec
"The \"raw\" put operation on values. Inserts a fully decomposed value (tuple) into the designated
spec, returning the ID at which it was inserted if an ID was not provided.
Users should universally prefer `#'shelving.core/put-spec`. This method is an unprotected
implementation detail not for general use.
Note that when inserting into \"record\" specs, all relations to the updated \"record\" ID must be
invalidated.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn spec id val])}
#'dx)
(defmulti get-spec
"Fetches a single tuple, being part of a record, from a shelf by its spec and ID.
Returns the record if it exists, otherwise returning the user-provided `not-found` value, taken to
be `nil` by default.
Implementation detail of `#'shelving.core/get-spec`, which should be preferred by users. This
method is an unprotected implementation detail not for general use.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn spec record-id]
[conn spec record-id not-found])}
#'dx)
(defmulti has?
"Indicates whether a shelf has a record of a spec.
Returns `true` if and only if the shelf contains a record if the given spec and ID. Otherwise
must return `false`.
Implementations may provide alternate implementations of this method."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn spec record-id])}
#'dx)
(defmethod has? :default [conn spec record-id]
(let [not-found (Object.)]
(not= not-found (get-spec conn spec record-id not-found))))
(required! put-spec)
(defmulti put-rel
"The \"raw\" put operation on relations.
Inserts a `[from rel to]` triple into the data store unconditionally.
Users should universally prefer `#'shelving.core/put-spec`. This method is an unprotected
implementation detail not for general use.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn spec rel-id from-id to-id])}
#'dx)
(required! put-rel)
(defmulti schema
"Returns the schema record for a given connection.
Schemas are fixed when the connection is opened.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn])}
#'dx)
(required! schema)
(defmulti set-schema
"Attempts to alter the live schema of the connection by applying the given transformer function to
the current schema state with any additional arguments.
Implementation detail of `#'shelving.core/alter-schema`, which should be universally
preferred. This method is an unprotected implementation detail not for general use.
Returns the schema record for a given connection.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/basic}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn schema])}
#'dx)
(required! set-schema)
(defmulti enumerate-specs
"Enumerates all the known specs.
Shelves may provide alternate implementations of this method."
{:categories #{:shelving.core/impl :shelving.core/schema}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn])}
#'dx)
(defmethod enumerate-specs :default [conn]
(-> conn schema :specs keys))
(defmulti enumerate-spec
"Enumerates all the known records of a spec by UUID.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/schema}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn spec])}
#'dx)
(required! enumerate-spec)
(defmulti count-spec
"Returns an upper bound on the cardinality of a given spec.
The bound should be as tight as possible if not precise. Implementations of this method should be
near constant time and should not require realizing the spec in question.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/query}
:stability :stability/unstable
:added "0.0.1"
:arglists '([conn spec])}
#'dx)
(required! count-spec)
(defmulti enumerate-rels
"Enumerates all the known rels by ID (their `[from-spec to-spec]` pair). Includes aliases.
Shelves may provide alternate implementation of this method."
{:categories #{:shelving.core/impl :shelving.core/rel}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn])}
#'dx)
(defmethod enumerate-rels :default [conn]
(-> conn schema :rels keys))
(defmulti enumerate-rel
"Enumerates the `(from-id to-id)` pairs of the given rel(ation).
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/rel}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn rel-id])}
#'dx)
(required! enumerate-rel)
(defmulti count-rel
"Returns an upper bound on the cardinality of a given relation.
The bound should be as tight as possible if not precise. Implementations of this method should be
near constant time and should not require realizing the rel in question.
Shelves must implement this method.
By default throws `me.arrdem.UnimplementedOperationException`."
{:categories #{:shelving.core/impl :shelving.core/query}
:stability :stability/unstable
:added "0.0.1"
:arglists '([conn rel-id])}
#'dx)
(required! count-rel)
(defmulti get-rel
"Given a rel(ation) and the ID of an record of the from-rel spec,
return a seq of the IDs of records it relates to. If the given ID does not exist on the left side
of the given relation, an empty seq must be produced.
If the given ID does not exist on the left side of the given relation, an empty seq must be
produced.
Note that if the rel `[a b]` was created with `#'shelving.core/spec-rel`, the rel `[b a]` also
exists and is the complement of mapping from `a`s to `b`s defined by `[a b]`.
By default uses `#'shelving.impl/enumerate-rel` to do a full scan of the pairs constituting this
relation.
Shelves may provide more efficient implementations of this method."
{:stability :stability/unstable
:added "0.0.0"
:arglists '([conn rel-id spec id])}
#'dx)
(defmethod get-rel :default [conn [from-spec to-spec :as rel-id] id]
(let [real-rel-id (schema/resolve-alias (schema conn) rel-id)
f (if (= rel-id real-rel-id)
#(when (= id (first %)) (second %))
#(when (= id (second %)) (first %)))]
(keep f (enumerate-rel conn real-rel-id))))
(defmulti q
"Query compilation.
Given a connection and a query datastructure, return a function of a
connection and 0 or more positional logic variable bindings per the
`:in` clause of the compiled query. Query functions return sequences
of maps from logic variables to values. Each produced map must
contain all lvars occurring in the query's `:find` clause.
See the datalog documentation for a full description of the
supported query form.
By default, `#'shelving.query.core/q` is used which attempts to
compile queries against only the published `shelving.impl`
API. Implementers may choose to provide their own query compilation
pipelines but are warned that they do so wholesale. Query
compilation is currently not modular."
{:categories #{:shelving.core/impl :shelving.core/query}
:stability :stability/stable
:added "0.0.0"
:arglists '([conn query])}
#'dx)
(defmethod q :default [conn query]
(require 'shelving.query.core)
((resolve 'shelving.query.core/q) conn query))
(defmulti fingerprint-query
"Query caching implementation detail.
Function of a connection and a query which returns a unique
identifier for the compilation of that pair."
{:stability :stability/unstable
:added "0.0.0"
:arglists '([conn query])}
#'dx)
(defn- get-multi-dispatch-fingerprint [^clojure.lang.MultiFn multifn args]
;; FIXME: doesn't handle :default, or dispatch preference correctly
(hash (.getMethod multifn (apply (.dispatchFn multifn) args))))
(defmethod fingerprint-query :default [conn query]
(require 'shelving.query.parser)
(uuid [(get-multi-dispatch-fingerprint q [conn query])
(s/conform :shelving.query.parser/datalog query)]))