4

再利用可能と思われる一般的なデータ型クエリを作成するのに問題があります。

たとえば、この投稿のフォローアップとして、特定のデータ分割からすべての ID を取得する標準的な方法はありますか? 、次のスキーマがインストールされています

{[63 :account/password] 
 [64 :account/firstName] 
 [65 :account/lastName] 
 [62 :account/username]
 [69 :email/prority]
 [68 :email/address]}

特定の名前空間を持つ属性のみを表示する関数が必要です。

この関数は、「:account」名前空間のすべての属性を表示します

(d/q '[:find ?e ?ident :where
        [?e :db/ident ?ident]
        [_ :db.install/attribute ?e]
        [(.toString ?ident) ?val]
        [(.startsWith ?val ":account")]] (d/db *conn*))

;; => [62 :account/username] [63 :account/password]  
;;    [64 :account/firstName]  [65 :account/lastName]

ただし、入力を受け取ることができる関数を書きたい場合は、機能させるためにどこにでも引用符を付ける必要があります。

(defn get-ns-attrs [?ns db]
  (d/q [':find '?e '?ident ':where
         ['?e ':db/ident '?ident]
         ['_ ':db.install/attribute '?e]
         [(list '.toString '?ident) '?val]
         [(list '.startsWith '?val (str ":" ?ns))]] db))

(get-ns-attrs "account" (d/db *conn*))
;; => [62 :account/username] [63 :account/password]  
;;    [64 :account/firstName]  [65 :account/lastName]

(get-ns-attrs "email" (d/db *conn*))
;; => [69 :email/prority] [68 :email/address]

これを行うより良い方法はありますか?

- - - アップデート - - - -

人々が試すための完全なコードは次のとおりです。

(ns schema.start
  (:require [datomic.api :as d])
  (:use [clojure.pprint :only [pprint]]))

(def *uri* "datomic:mem://login-profile")
(d/create-database *uri*)
(def *conn* (d/connect *uri*))

(defn boolean? [x]
  (instance? java.lang.Boolean x))

(defn db-pair [attr kns val f]
  (list (keyword (str "db/" (name attr)))
        (f val kns)))

(defn db-enum [val kns]
  (keyword (str "db." (name kns) "/" (name val))))

(def DB-KNS
  {:ident        {:required true
                  :check keyword?}
   :type         {:required true
                  :check #{:keyword :string :boolean :long :bigint :float
                           :double :bigdec :ref :instant :uuid :uri :bytes}
                  :attr :valueType
                  :fn db-enum}
   :cardinality  {:required true
                  :check #{:one :many}
                  :fn db-enum}
   :unique       {:check #{:value :identity}
                  :fn db-enum}
   :doc          {:check string?}
   :index        {:check boolean?}
   :fulltext     {:check boolean?}
   :component?   {:check keyword?}
   :no-history   {:check boolean?}})

(defn process-kns [m kns params res]
  (let [val (m kns)]
    (cond (nil? val)
          (if (:required params)
            (throw (Exception. (str "key " kns " is a required key")))
            res)

          :else
          (let [chk  (or (:check params) (constantly true))
                f    (or (:fn params) (fn [x & xs] x))
                attr (or (:attr params) kns)]
            (if (chk val)
              (apply assoc res (db-pair attr kns val f))
              (throw (Exception. (str "value " val " failed check"))))))))

(defn schema [m]
  (loop [db-kns# DB-KNS
         output  {}]
    (if-let [entry (first db-kns#)]
      (recur (rest db-kns#)
             (process-kns m (first entry) (second entry) output))
      (assoc output
        :db.install/_attribute :db.part/db
        :db/id (d/tempid :db.part/db)))))

(def account-schema
  [(schema {:ident       :account/username
            :type        :string
            :cardinality :one
            :unique      :value
            :doc         "The username associated with the account"})
   (schema {:ident       :account/password
            :type        :string
            :cardinality :one
            :doc         "The password associated with the account"})
   (schema {:ident       :account/firstName
            :type        :string
            :cardinality :one
            :doc         "The first name of the user"})
   (schema {:ident       :account/lastName
            :type        :string
            :cardinality :one
            :doc         "The first name of the user"})
   (schema {:ident       :account/otherEmails
            :type        :ref
            :cardinality :many
            :doc         "Other email address of the user"})
   (schema {:ident       :account/primaryEmail
            :type        :ref
            :cardinality :one
            :doc         "The primary email address of the user"})])

(def email-schema
  [(schema {:ident       :email/address
            :type        :string
            :cardinality :one
            :unique      :value
            :doc         "An email address"})
   (schema {:ident       :email/priority
            :type        :long
            :cardinality :one
            :doc         "An email address's priority"})])

(d/transact *conn* account-schema)
(d/transact *conn* email-schema)

(defn get-ns-attrs1 [?ns db]
  (d/q [':find '?e '?ident ':where
        ['?e ':db/ident '?ident]
        ['_ ':db.install/attribute '?e]
        [(list '.toString '?ident) '?val]
        [(list '.startsWith '?val (str ":" ?ns))]] db))

(defn get-ns-attrs2 [?ns db]
  (d/q '[:find ?e ?ident :where
        [?e :db/ident ?ident]
        [_ :db.install/attribute ?e]
        [(.toString ?ident) ?val]
        [(.startsWith ?val ~(str ":" ?ns))]] db))


(get-ns-attrs1 "account" (d/db *conn*))
(get-ns-attrs1 "email" (d/db *conn*))

(get-ns-attrs2 "account" (d/db *conn*))
(get-ns-attrs2 "email" (d/db *conn*))
4

2 に答える 2

5

Clojure の名前空間を使用すると、もう少し簡単に解決できます。

(d/q '[:find ?name :in $ ?ns 
       :where [_ :db.install/attribute ?a] 
              [?a :db/ident ?name] 
              [(namespace ?name) ?attr-ns] 
              [(= ?attr-ns ?ns)]] (d/db *conn*) "account")

戻り値:

#{[:account/password]
  [:account/firstName]
  [:account/lastName]
  [:account/username]}
于 2013-06-12T15:36:14.557 に答える
5

もう少し読んだ後、:inキーワードがこれらすべての鍵であることがわかりました。例は、チュートリアルの「高度なクエリ」セクション ( http://docs.datomic.com/tutorial.html ) に記載されています。

:accountこれは、ネームスペース内のすべての属性を一覧表示する同等のクエリです。

(d/q '[:find ?e ?ident ?ns :in $ ?ns :where
        [?e :db/ident ?ident]
        [_ :db.install/attribute ?e]
        [(.toString ?ident) ?val]
        [(.startsWith ?val ?ns)]] 
     (d/db *conn*)
     "account")
;; => #<HashSet [[68 :account/firstName], [67 :account/password], [71 :account/primaryEmail], [66 :account/username], [69 :account/lastName], [70 :account/otherEmails]]>

これは関数で同等です

(defn get-ns-attrs [_ns db]
  (d/q '[:find ?e ?ident :in $ ?ns :where
         [?e :db/ident ?ident]
         [_ :db.install/attribute ?e]
         [(.toString ?ident) ?val]
         [(.startsWith ?val ?ns) ]] db (str _ns)))

(get-ns-attrs :account (d/db *conn*))

;; => #<HashSet [[68 :account/firstName], [67 :account/password], [71 :account/primaryEmail], [66 :account/username], [69 :account/lastName], [70 :account/otherEmails]]>

%より多くのモジュール性が必要な場合は、一連のルールを渡すために関数をさらに分解できます。

(def rule-nsAttrs
  '[[nsAttrs ?e ?ident ?ns]
    [?e :db/ident ?ident]
    [_ :db.install/attribute ?e]
    [(.toString ?ident) ?val]
    [(.startsWith ?val ?ns)]])

(defn get-ns-attrs [_ns db]
  (d/q '[:find ?e ?ident :in $ % ?ns :where
         (nsAttrs ?e ?ident ?ns)]
       (d/db *conn*)
       [rule-nsAttrs]
       (str _ns)))

(get-ns-attrs :account (d/db *conn*))
;; => #<HashSet [[68 :account/firstName], [67 :account/password], [71 :account/primaryEmail], [66 :account/username], [69 :account/lastName], [70 :account/otherEmails]]>
于 2013-02-09T06:17:45.260 に答える