Skip to content

Commit

Permalink
move mutation to separate namespace, update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
tamizhvendan committed Aug 16, 2023
1 parent 4357a42 commit 2d37b06
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 117 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
## HoneyEQL

HoneyEQL is a Clojure library enables you to query the database declaratively using the [EDN Query Language](https://edn-query-language.org)(EQL). It aims to simplify the effort required to work with the relational databases in Clojure.
HoneyEQL is a Clojure library enables you to query the database declaratively using the [EDN Query Language](https://edn-query-language.org)(EQL). It aims to simplify the effort required to work with the relational databases in Clojure. It also provides database modification operations support using Clojure maps with namespace qualified keys.

[![Clojars Project](https://img.shields.io/clojars/v/com.github.tamizhvendan/honeyeql.svg)](https://clojars.org/com.github.tamizhvendan/honeyeql)

> It currently supports Postgres (9.4 & above) and MySQL (8.0 & above) only.
> Supports Postgres (9.4 & above) and MySQL (8.0 & above)
## Rationale

Expand Down Expand Up @@ -33,7 +33,7 @@ The query result would look like
...]
```

Then we need to group by `first_name` & `last_name` to get the exact result result that we want!
Then we need to do the **group by** operation on the `first_name` & `last_name` attributes at the application layer to get the exact result result that we want!

How about making these steps truly declarative?

Expand Down
3 changes: 2 additions & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
edn-query-language/eql {:mvn/version "2021.07.18"}
com.github.seancorfield/honeysql {:mvn/version "2.4.1045"}
org.clojure/data.json {:mvn/version "2.4.0"}}
:aliases {:dev {:extra-deps {com.mysql/mysql-connector-j {:mvn/version "8.1.0"}
:aliases {:dev {:extra-paths ["dev"]
:extra-deps {com.mysql/mysql-connector-j {:mvn/version "8.1.0"}
org.postgresql/postgresql {:mvn/version "42.6.0"}
djblue/portal {:mvn/version "0.45.1"}}}
:test {:extra-paths ["test"]
Expand Down
38 changes: 38 additions & 0 deletions dev/playground.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
(ns playground
(:require [honeyeql.core :as heql]
[honeyeql.mutation :as hm]
[honeyeql.db :as heql-db]
[portal.api :as p]
[edn-query-language.core :as eql]
[clojure.spec.alpha :as s]
[next.jdbc :as jdbc]))

(def logs (atom {}))

(defn- logger [x]
(swap! logs merge x))

(defn- reset-logger []
(reset! logs {})
(remove-tap logger)
(add-tap logger))

(defn- setup-portal [x]
(add-tap #'p/submit)
(tap> x)
(p/open {:launcher :vs-code}))

(comment
(reset-logger)

(def pg-adapter (heql-db/initialize {:dbtype "postgres"
:dbname "honeyeql"
:user "postgres"
:password "postgres"}))

(def mysql-adapter (heql-db/initialize {:dbtype "mysql"
:dbname "honeyeql"
:user "root"
:password "mysql123"}))

)
4 changes: 3 additions & 1 deletion doc/aggregate-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ HoneyEQL supports the standard aggregate functions `count`, `max`, `min`, `sum`
[:count [:distinct :course/rating]]
```

> NOTE: `[:count :*]` is not supported.
> NOTE:
> * `[:count :*]` is not supported.
> * This function syntax is not officially supported in [EDN Query Language Specification](https://edn-query-language.org/eql/1.0.0/specification.html).
We'll use this sample database schema to walk us through the syntax
![](./img/author_course_er_diagram.png)
Expand Down
4 changes: 2 additions & 2 deletions doc/cljdoc.edn
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
["Getting Started" {:file "doc/getting-started.md"}]
["Attributes" {:file "doc/attributes.md"}]
["Query Syntax" {:file "doc/query-syntax.md"}]
["Mutation" {:file "doc/mutation.md"}]
["Sorting" {:file "doc/sorting.md"}]
["Pagination" {:file "doc/pagination.md"}]
["Filters" {:file "doc/filters.md"}]
["Cast Operation" {:file "doc/cast-operation.md"}]
["Aggregate Queries" {:file "doc/aggregate-queries.md"}]
["Cast Operation" {:file "doc/cast-operation.md"}]
["Configuration" {:file "doc/configuration.md"}]
["Coercion" {:file "doc/coercion.md"}]
["Metadata" {:file "doc/metadata.md"}]
["Debugging & Troubleshooting" {:file "doc/debugging.md"}]]}
Binary file removed doc/img/graphql-playground.png
Binary file not shown.
92 changes: 92 additions & 0 deletions doc/mutation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Mutation
All the mutation operations are available under the `honeyeql.mutation` namespace

```clojure
(require '[honeyeql.mutation :as hm])
```

For the given `employee` schema,

![](./img/employee_self_ref_er_diagram.png)

we can perform the following database operations using the HoneyEQL's mutation support as described.

## Insert

```clojure
;; inserting first employee
(hm/insert! db-adapter
#:employee{:first-name "Brown"
:last-name "Tara"})
;; the above expression returns (for postgres)
#:employee{:id 1, :first-name "Brown", :last-name "Tara", :employee-reports-to-id nil}

;; inserting second employee
(hm/insert! db-adapter
#:employee{:first-name "Lauryn"
:last-name "Mario"
:employee-reports-to-id 1})
;; it returns (for postgres)
#:employee{:id 2, :first-name "Lauryn", :last-name "Mario", :employee-reports-to-id 1}
```

## Insert Multiple

```clojure
(hm/insert-multi! db-adapter
[#:employee{:first-name "Harmon"
:last-name "Bernie"
:employee-reports-to-id 1}
#:employee{:first-name "Harold"
:last-name "Ambrose"
:employee-reports-to-id 2}
#:employee{:first-name "Bryce"
:last-name "Hoeger"
:employee-reports-to-id 2}])

;; it returns (for postgres)
(#:employee{:id 3, :first-name "Harmon", :last-name "Bernie", :employee-reports-to-id 1}
#:employee{:id 4, :first-name "Harold", :last-name "Ambrose", :employee-reports-to-id 2}
#:employee{:id 5, :first-name "Bryce", :last-name "Hoeger", :employee-reports-to-id 2})

```

## Update

```clojure
;; (update! db-adapter <data-to-update> <where-condition>)
(hm/update! db-adapter
#:employee{:first-name "Margaret"}
#:employee{:id 1})
;; returns
#:next.jdbc{:update-count 1}

;; where-condition can have multiple attributes and it supports only the AND condition
; UPDATE employee SET last_name = 'Adam' WHERE id = 1 AND first_name
(hm/update! db-adapter
#:employee{:last-name "Adam"}
#:employee{:last-name "Tara" :first-name "Margaret"})
;; returns
#:next.jdbc{:update-count 1}
```

## Delete

```clojure
;; (delete! db-adapter <where-condition>)
(hm/delete! db-adapter
#:employee {:id 9})
;; like the where-condition in the update! function, it can have multiple attributes and it also supports only the AND condition
```

## Transaction Support

To perform Database transactipn we can make use of the `next.jdbc`'s `with-transaction` [macro](https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.3.883/doc/getting-started/transactions) along with two functions, `db-spec` and `use-tx` from the `honeyeql.core` namespace.

```clojure
(jdbc/with-transaction [tx (heql/db-spec db-adapter)]
(let [tx-aware-db-adapter (heql/use-tx db-adapter tx)]
(hm/delete! tx-aware-db-adapter {:employee/id 10})
(hm/delete! tx-aware-db-adapter {:employee/id 11})
(hm/delete! tx-aware-db-adapter {:employee/id 12})))
```
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.tamizhvendan</groupId>
<artifactId>honeyeql</artifactId>
<version>0.1.0-alpha51</version>
<version>0.1.0-alpha52</version>

<name>Honey EQL</name>
<description>HoneyEQL enables you to query database using the EQL.</description>
Expand All @@ -21,7 +21,7 @@
<url>https://github.com/tamizhvendan/honeyeql</url>
<connection>scm:git:git://github.com/tamizhvendan/honeyeql.git</connection>
<developerConnection>scm:git:ssh://[email protected]/tamizhvendan/honeyeql.git</developerConnection>
<tag>0.1.0-alpha51</tag>
<tag>0.1.0-alpha52</tag>
</scm>

<dependencies>
Expand Down
56 changes: 0 additions & 56 deletions src/honeyeql/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
(:require [clojure.data.json :as json]
[clojure.string :as str]
[edn-query-language.core :as eql]
[next.jdbc.result-set :as rs]
[next.jdbc.sql :as sql]
[honey.sql.helpers :as hsql-helpers]
[honeyeql.db-adapter.core :as db]
[honeyeql.debug :refer [trace>>]]
Expand Down Expand Up @@ -373,60 +371,6 @@
(defn query-single [db-adapter eql-query]
(first (query db-adapter eql-query)))

(defn- entity-name [entity]
(-> entity keys first namespace))

(defn- table-name [entity]
(-> (entity-name entity) (str/replace #"-" "_") keyword))

(defn- sqlize-entity [db-adapter entity]
(into {}
(map (fn [[k v]]
[(-> (name k) (str/replace #"-" "_") keyword)
(heql-md/coerce-attr-value db-adapter :to-db k v)])
entity)))

(defn- namespacify-attributes [entity-name entity]
(update-keys entity #(keyword entity-name (name %))))

(defn insert! [db-adapter entity]
(namespacify-attributes
(entity-name entity)
(sql/insert! (:db-spec db-adapter)
(table-name entity)
(sqlize-entity db-adapter entity)
{:column-fn (db/table-fn db-adapter)
:table-fn (db/table-fn db-adapter)
:builder-fn rs/as-kebab-maps})))

(defn insert-multi! [db-adapter entities]
(when (seq entities)
(let [entities (map #(into (sorted-map) %) entities)
first-entity (first entities)
first-entity-name (entity-name first-entity)
sqlized-entities (map (partial sqlize-entity db-adapter) entities)]
(map
#(namespacify-attributes first-entity-name %)
(sql/insert-multi! (:db-spec db-adapter)
(table-name first-entity)
(keys (first sqlized-entities))
(map vals sqlized-entities)
{:column-fn (db/table-fn db-adapter)
:table-fn (db/table-fn db-adapter)
:builder-fn rs/as-kebab-maps})))))

(defn update! [db-adapter update-params where-params]
(sql/update! (:db-spec db-adapter) (table-name update-params)
(sqlize-entity db-adapter update-params) (sqlize-entity db-adapter where-params)
{:column-fn (db/table-fn db-adapter)
:table-fn (db/table-fn db-adapter)}))

(defn delete! [db-adapter where-params]
(sql/delete! (:db-spec db-adapter) (table-name where-params)
(sqlize-entity db-adapter where-params)
{:column-fn (db/table-fn db-adapter)
:table-fn (db/table-fn db-adapter)}))

(defn db-spec [db-adapter]
(:db-spec db-adapter))

Expand Down
60 changes: 60 additions & 0 deletions src/honeyeql/mutation.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
(ns honeyeql.mutation
(:require [next.jdbc.result-set :as rs]
[next.jdbc.sql :as sql]
[clojure.string :as str]
[honeyeql.db-adapter.core :as db]
[honeyeql.meta-data :as heql-md]))

(defn- entity-name [entity]
(-> entity keys first namespace))

(defn- table-name [entity]
(-> (entity-name entity) (str/replace #"-" "_") keyword))

(defn- sqlize-entity [db-adapter entity]
(into {}
(map (fn [[k v]]
[(-> (name k) (str/replace #"-" "_") keyword)
(heql-md/coerce-attr-value db-adapter :to-db k v)])
entity)))

(defn- namespacify-attributes [entity-name entity]
(update-keys entity #(keyword entity-name (name %))))

(defn insert! [db-adapter entity]
(namespacify-attributes
(entity-name entity)
(sql/insert! (:db-spec db-adapter)
(table-name entity)
(sqlize-entity db-adapter entity)
{:column-fn (db/table-fn db-adapter)
:table-fn (db/table-fn db-adapter)
:builder-fn rs/as-kebab-maps})))

(defn insert-multi! [db-adapter entities]
(when (seq entities)
(let [entities (map #(into (sorted-map) %) entities)
first-entity (first entities)
first-entity-name (entity-name first-entity)
sqlized-entities (map (partial sqlize-entity db-adapter) entities)]
(map
#(namespacify-attributes first-entity-name %)
(sql/insert-multi! (:db-spec db-adapter)
(table-name first-entity)
(keys (first sqlized-entities))
(map vals sqlized-entities)
{:column-fn (db/table-fn db-adapter)
:table-fn (db/table-fn db-adapter)
:builder-fn rs/as-kebab-maps})))))

(defn update! [db-adapter update-params where-params]
(sql/update! (:db-spec db-adapter) (table-name update-params)
(sqlize-entity db-adapter update-params) (sqlize-entity db-adapter where-params)
{:column-fn (db/table-fn db-adapter)
:table-fn (db/table-fn db-adapter)}))

(defn delete! [db-adapter where-params]
(sql/delete! (:db-spec db-adapter) (table-name where-params)
(sqlize-entity db-adapter where-params)
{:column-fn (db/table-fn db-adapter)
:table-fn (db/table-fn db-adapter)}))
2 changes: 1 addition & 1 deletion test/database/mysql/honeyeql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,4 @@ UNLOCK TABLES;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2023-08-14 12:02:44
-- Dump completed on 2023-08-14 12:02:44
Loading

0 comments on commit 2d37b06

Please sign in to comment.