在Clojure应用程序中声明Datomic架构的推荐方法

时间:2015-07-14 20:12:36

标签: clojure schema datomic

我开始开发基于Datomic支持的Clojure应用,我想知道什么是声明架构的最佳方式,以解决以下问题:

  1. 具有简洁,可读的架构表示
  2. 在运行我的应用程序的新版本之前确保架构已安装并且是最新的。
  3. 直观地说,我的方法如下:

    1. 声明一些辅助函数使模式声明比使用原始映射更简洁
    2. 自动安装架构作为应用初始化的一部分(我还不知道是否总是有效)。
    3. 这是最好的方式吗?人们通常如何做到这一点?

6 个答案:

答案 0 :(得分:4)

我使用符合性,请参阅Conformity repository。 Yeller Here还有一个非常有用的博客文章,它将指导您如何使用符合性。

答案 1 :(得分:4)

  1. 原始地图很详细,但与使用某些高级 api相比有一些很大的优势:

    • 架构以交易形式定义,您指定的是可交易的(假设该词存在)
    • 您的架构与特定库或规范版本无关,它始终有效。
    • 您的架构可序列化(edn),无需调用spec API。
    • 因此,您可以在分布式环境中更轻松地存储和部署架构,因为它是以数据形式而非代码形式。
  2. 由于这些原因,我使用原始地图。

    1. 自动安装架构。
    2. 我也不这样做。

      通常,当您对模式进行更改时,可能会发生许多事情:

      • 添加新属性
      • 更改现有属性类型
      • 为属性创建全文
      • 从其他值创建新属性
      • 其他

      在一个可能需要一段时间的过程中,您可能需要以一些非显而易见且非一般的方式更改现有数据。

      do use some automatization用于应用模式和模式更改列表,但总是在受控制的部署中#34;当有关数据更新的更多事情可能发生时。

      假设您有users.schema.ednroles.schema.edn个文件:

      (require '[datomic-manage.core :as manager])
      (manager/create uri)
      (manager/migrate uri [:users.schema
                            :roles.schema])
      

答案 2 :(得分:2)

对于#1,datomic-schema可能会有所帮助。我还没有使用它,但the example看起来很有希望。

答案 3 :(得分:1)

我建议使用Tupelo Datomic开始使用。我编写了这个库来简化Datomic架构的创建并简化理解,就像你在问题中提到的一样。

举个例子,假设我们正试图跟踪世界首映间谍机构的信息。让我们创建一些适用于我们的英雄的属性。恶棍(见可执行代码in the unit test)。

  (:require [tupelo.datomic   :as td]
            [tupelo.schema    :as ts])

  ; Create some new attributes. Required args are the attribute name (an optionally namespaced
  ; keyword) and the attribute type (full listing at http://docs.datomic.com/schema.html). We wrap
  ; the new attribute definitions in a transaction and immediately commit them into the DB.
  (td/transact *conn* ;   required              required              zero-or-more
                      ;  <attr name>         <attr value type>       <optional specs ...>
    (td/new-attribute   :person/name         :db.type/string         :db.unique/value)      ; each name      is unique
    (td/new-attribute   :person/secret-id    :db.type/long           :db.unique/value)      ; each secret-id is unique
    (td/new-attribute   :weapon/type         :db.type/ref            :db.cardinality/many)  ; one may have many weapons
    (td/new-attribute   :location            :db.type/string)     ; all default values
    (td/new-attribute   :favorite-weapon     :db.type/keyword ))  ; all default values

对于:weapon / type属性,我们想要使用枚举类型,因为我们的对手只有有限数量的选择:

  ; Create some "enum" values. These are degenerate entities that serve the same purpose as an
  ; enumerated value in Java (these entities will never have any attributes). Again, we
  ; wrap our new enum values in a transaction and commit them into the DB.
  (td/transact *conn*
    (td/new-enum :weapon/gun)
    (td/new-enum :weapon/knife)
    (td/new-enum :weapon/guile)
    (td/new-enum :weapon/wit))

让我们创建一些拮抗剂并将它们加载到数据库中。请注意,我们在这里只使用普通的Clojure值和文字,我们不必担心任何特定于Datomic的转换。

  ; Create some antagonists and load them into the db.  We can specify some of the attribute-value
  ; pairs at the time of creation, and add others later. Note that whenever we are adding multiple
  ; values for an attribute in a single step (e.g. :weapon/type), we must wrap all of the values
  ; in a set. Note that the set implies there can never be duplicate weapons for any one person.
  ; As before, we immediately commit the new entities into the DB.
  (td/transact *conn*
    (td/new-entity { :person/name "James Bond" :location "London"     :weapon/type #{ :weapon/gun :weapon/wit   } } )
    (td/new-entity { :person/name "M"          :location "London"     :weapon/type #{ :weapon/gun :weapon/guile } } )
    (td/new-entity { :person/name "Dr No"      :location "Caribbean"  :weapon/type    :weapon/gun                 } ))

享受! 艾伦

答案 4 :(得分:1)

我的偏好(我有偏见,作为图书馆的作者)位于datomic-schema - 它专注于仅转换到正常的数据模式 - 从那里,你像平常那样交易模式

我希望使用相同的数据来计算实时数据库实例和定义之间的模式迁移 - 以便枚举,类型和基数得到更改以符合您的定义。

datomic-schema的重要部分(对我来说)是退出路径非常干净 - 如果你发现它不支持某些东西(我无法以任何理由实现),那么你可以将您的模式转储为普通edn,将其保存并删除依赖项。

如果您想进行某种数据迁移或更具体的迁移(清理数据或首先重命名为其他内容),

Conformity将非常有用。

答案 5 :(得分:1)

提案:使用事务函数在EDN 中声明架构属性更简洁 ,这样可以保留在{1}中所示的在EDN中声明架构的好处

示例:

;; defining helper function
[{:db/id #db/id[:db.part/user]
  :db/doc "Helper function for defining entity fields schema attributes in a concise way."
  :db/ident :utils/field
  :db/fn #db/fn {:lang :clojure
                 :require [datomic.api :as d]
                 :params [_ ident type doc opts]
                 :code [(cond-> {:db/cardinality :db.cardinality/one
                                 :db/fulltext true
                                 :db/index true
                                 :db.install/_attribute :db.part/db

                                 :db/id (d/tempid :db.part/db)
                                 :db/ident ident
                                 :db/valueType (condp get type
                                                 #{:db.type/string :string} :db.type/string
                                                 #{:db.type/boolean :boolean} :db.type/boolean
                                                 #{:db.type/long :long} :db.type/long
                                                 #{:db.type/bigint :bigint} :db.type/bigint
                                                 #{:db.type/float :float} :db.type/float
                                                 #{:db.type/double :double} :db.type/double
                                                 #{:db.type/bigdec :bigdec} :db.type/bigdec
                                                 #{:db.type/ref :ref} :db.type/ref
                                                 #{:db.type/instant :instant} :db.type/instant
                                                 #{:db.type/uuid :uuid} :db.type/uuid
                                                 #{:db.type/uri :uri} :db.type/uri
                                                 #{:db.type/bytes :bytes} :db.type/bytes
                                                 type)}
                                doc (assoc :db/doc doc)
                                opts (merge opts))]}}]

;; ... then (in a later transaction) using it to define application model attributes
[[:utils/field :person/name :string "A person's name" {:db/index true}]
 [:utils/field :person/age :long "A person's name" nil]]