forked from 8thlight/hyperion
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gae.clj
137 lines (117 loc) · 4.78 KB
/
gae.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
(ns hyperion.gae
(:require [chee.string :refer [gsub spear-case]]
[chee.util :refer [->options]]
[clojure.string :as str]
[hyperion.abstr :refer [Datastore]]
[hyperion.filtering :as filter]
[hyperion.gae.types]
[hyperion.sorting :as sort])
(:import [com.google.appengine.api.datastore Entity Entities Query DatastoreService DatastoreServiceFactory Query$FilterOperator
Query$SortDirection FetchOptions$Builder EntityNotFoundException KeyFactory Key]))
(defn create-key [kind id]
(if (number? id)
(KeyFactory/createKey kind (long id))
(KeyFactory/createKey kind (str id))))
(defn key? [key]
(isa? (class key) Key))
(defn key->string [^Key key]
(try
(KeyFactory/keyToString key)
(catch Exception e nil)))
(defn string->key [value]
(try
(KeyFactory/stringToKey value)
(catch Exception e nil)))
(defn ->key [value]
(cond
(key? value) value
(string? value) (string->key value)
(nil? value) nil
:else (string->key (:key value))))
(defn ->native [entity]
(let [key (string->key (:key entity))
native (if key (Entity. key) (Entity. (:kind entity)))]
(doseq [[field value] (dissoc entity :kind :key )]
(.setProperty native (name field) value))
native))
(defn <-native [native]
(reduce
(fn [record entry]
(assoc record (key entry) (val entry)))
{:kind (.getKind native) :key (key->string (.getKey native))}
(.getProperties native)))
(defn save-record [service record]
(let [native (->native record)]
(.put service native)
(<-native native)))
(def filter-operators {:= Query$FilterOperator/EQUAL
:< Query$FilterOperator/LESS_THAN
:<= Query$FilterOperator/LESS_THAN_OR_EQUAL
:> Query$FilterOperator/GREATER_THAN
:>= Query$FilterOperator/GREATER_THAN_OR_EQUAL
:!= Query$FilterOperator/NOT_EQUAL
:contains? Query$FilterOperator/IN})
(def sort-directions {:asc Query$SortDirection/ASCENDING
:desc Query$SortDirection/DESCENDING})
(defn- build-query [service kind filters sorts]
(let [query (Query. (name kind))]
(doseq [filter filters]
(.addFilter
query
(name (filter/field filter))
((filter/operator filter) filter-operators)
(filter/value filter)))
(doseq [sort sorts]
(.addSort
query
(name (sort/field sort))
((sort/order sort) sort-directions)))
(.prepare service query)))
(defn- build-fetch-options [limit offset]
(let [fetch-options (FetchOptions$Builder/withDefaults)]
(when limit (.limit fetch-options limit))
(when offset (.offset fetch-options offset))
fetch-options))
(defn- find-by-kind [service kind filters sorts limit offset]
(let [query (build-query service kind filters sorts)
fetching (build-fetch-options limit offset)]
(map <-native (iterator-seq (.asQueryResultIterator query fetching)))))
(defn- delete-records [service keys]
(.delete service (map ->key keys)))
(defn- delete-by-kind [service kind filters]
(let [query (build-query service kind filters nil)
records (iterator-seq (.asQueryResultIterator query))
keys (map #(.getKey %) records)]
(.delete service keys)))
(defn- count-by-kind [service kind filters]
(.countEntities (build-query service kind filters nil)))
(defn- find-by-key [service value]
(when-let [key (->key value)]
(try
(<-native (.get service key))
(catch EntityNotFoundException e
nil))))
(defn- all-kinds [service]
(let [query (.prepare service (Query. Entities/KIND_METADATA_KIND))
fetching (build-fetch-options nil nil)]
(map #(.getName (.getKey %)) (.asIterable query fetching))))
(deftype GaeDatastore [service]
Datastore
(ds-save [this records] (doall (map #(save-record service %) records)))
(ds-delete-by-kind [this kind filters]
(delete-by-kind service kind filters))
(ds-delete-by-key [this key]
(.delete service [(->key key)]))
(ds-count-by-kind [this kind filters] (count-by-kind service kind filters))
(ds-find-by-key [this key]
(find-by-key service key))
(ds-find-by-kind [this kind filters sorts limit offset]
(find-by-kind service kind filters sorts limit offset))
(ds-all-kinds [this] (all-kinds service))
(ds-pack-key [this value] (string->key value))
(ds-unpack-key [this value] (key->string value)))
(defn new-gae-datastore [& args]
(cond
(nil? (seq args)) (GaeDatastore. (DatastoreServiceFactory/getDatastoreService))
(and (= 1 (count args)) (.isInstance DatastoreService (first args))) (GaeDatastore. (first args))
:else (let [options (->options args)] (GaeDatastore. (or (:service options) (DatastoreServiceFactory/getDatastoreService))))))