创建数据规范(create spec from data)

2019-09-29 02:19发布

我想刚刚从数据创建规范。 我有非常复杂的数据结构 - 所有嵌套地图。

{:contexts
 ({:importer.datamodel/global-id "01b4e69f86e5dd1d816e91da27edc08e",
   :importer.datamodel/type "province",
   :name "a1",
   :importer.datamodel/part-of "8cda1baed04b668a167d4ca28e3cef36"}
  {:importer.datamodel/global-id "8cda1baed04b668a167d4ca28e3cef36",
   :importer.datamodel/type "country",
   :name "AAA"}
  {:importer.datamodel/global-id "c78e5478e19f2d7c1b02088e53e8d8a4",
   :importer.datamodel/type "location",
   :importer.datamodel/center ["36." "2."],
   :importer.datamodel/part-of "01b4e69f86e5dd1d816e91da27edc08e"}
  {:importer.datamodel/global-id "88844f94f79c75acfcb957bb41386149",
   :importer.datamodel/type "organisation",
   :name "C"}
  {:importer.datamodel/global-id "102e96468e5d13058ab85c734aa4a949",
   :importer.datamodel/type "organisation",
   :name "A"}),
 :datasources
 ({:importer.datamodel/global-id "Source;ACLED",
   :name "ACLED",
   :url "https://www.acleddata.com"}),
 :iois
 ({:importer.datamodel/global-id "item-set;ACLED",
   :importer.datamodel/type "event",
   :datasource "Source;ACLED",
   :features
   ({:importer.datamodel/global-id
     "c74257292f584502f9be02c98829d9fda532a492e7dd41e06c31bbccc76a7ba0",
     :date "1997-01-04",
     :fulltext
     {:importer.datamodel/global-id "df5c7d6d075df3a7719ebdd39c6d4c7f",
      :text "bla"},
     :location-meanings
     ({:importer.datamodel/global-id
       "e5611219971164a15f06e07228fb7b51",
       :location "8cda1baed04b668a167d4ca28e3cef36",
       :contexts (),
       :importer.datamodel/type "position"}
      {:importer.datamodel/global-id
       "af36461d27ec1d8d28fd7f4a70ab7ce2",
       :location "c78e5478e19f2d7c1b02088e53e8d8a4",
       :contexts (),
       :importer.datamodel/type "position"}),
     :interaction-name "Violence",
     :importer.datamodel/type "description",
     :has-contexts
     ({:context "102e96468e5d13058ab85c734aa4a949",
       :context-association-type "actor",
       :context-association-name "actor-1",
       :priority "none"}
      {:context "88844f94f79c75acfcb957bb41386149",
       :context-association-type "actor",
       :context-association-name "actor-2",
       :priority "none"}),
     :facts
     ({:importer.datamodel/global-id
       "c46802ce6dcf33ca02ce113ffd9a855e",
       :importer.datamodel/type "integer",
       :name "fatalities",
       :value "16"}),
     :attributes
     ({:name "description",
       :importer.datamodel/type "string",
       :value "Violence"})}),
   :attributes (),
   :ioi-slice "per-item"})}

什么工具可以创建这样一个结构的规范呢? 我想使用这个工具: https://github.com/stathissideris/spec-provider

但它给了我这样的:

(spec/def :importer.datamodel/data
  (clojure.spec.alpha/coll-of
   (clojure.spec.alpha/or
    :collection
    (clojure.spec.alpha/coll-of
     (clojure.spec.alpha/keys
      :req
      [:importer.datamodel/global-id]
      :opt
      [:importer.datamodel/center
       :importer.datamodel/part-of
       :importer.datamodel/type]
      :opt-un
      [:importer.datamodel/attributes
       :importer.datamodel/datasource
       :importer.datamodel/features
       :importer.datamodel/ioi-slice
       :importer.datamodel/name
       :importer.datamodel/url]))
    :simple
    clojure.core/keyword?)))

这是不完整的解决方案...我使用(sp/pprint-specs (sp/infer-specs data :importer.datamodel/data) 'data 's) ...什么工具可以创建这样一个结构的规范呢?

Answer 1:

我想使用这个工具: https://github.com/stathissideris/spec-provider

SPEC-供应商没有给你想要的结果,因为你的数据是一个复杂的嵌套/递归结构。 其中一些地图将与被最好只具备多规格 ,但规格提供商不会做; 在其文档注意事项之一说, 有没有试图推断出多规格。

只有这样,才能正确地符合规范一些这些地图是使用多规格的规格将取决于他们:importer.datamodel/type值。

首先,让我们来看看顶级键(假设地图是一个结合命名的data ):

(keys data) => (:contexts :datasources :iois)

创建一个s/keys规格最外图:

(s/def ::my-map
  (s/keys :req-un [::contexts ::datasources ::iois]))

这些键是不合格的,但我们必须使用合格的关键字W / :req-un符合规范他们。 我们可以使用REPL来看看嵌套地图的形状和它们的关系:importer.datamodel/type ,步行嵌套结构和收集数据:

(let [keysets (atom #{})]
  (clojure.walk/postwalk
    (fn [v]
      (when (map? v)
        (swap! keysets conj [(:importer.datamodel/type v) (keys v)]))
      v)
    data)
  @keysets)
=>
#{...
  ["organisation" (:importer.datamodel/global-id :importer.datamodel/type :name)]
  [nil (:context :context-association-type :context-association-name :priority)]
  ["description"
   (:importer.datamodel/global-id :date :fulltext :location-meanings
    :interaction-name :importer.datamodel/type :has-contexts :facts :attributes)]
  ["event" (:importer.datamodel/global-id :importer.datamodel/type :datasource :features :attributes :ioi-slice)]
 ...}

(即将推出的规格阿尔法应该更容易以编程方式从这个数据定义规格)。

多规格

我们可以看到有一些没有地图的形状:importer.datamodel/type ,但是我们可以写多规格为做好些。 首先定义一个多方法分派的密钥类型:

(defmulti type-spec :importer.datamodel/type)

然后写一个defmethod每个:importer.datamodel/type值。 这里有一些例子:

(defmethod type-spec :default [_] (s/keys))
(defmethod type-spec "organisation" [_]
  (s/keys :req [:importer.datamodel/global-id]
          :req-un [::name]))
(defmethod type-spec "description" [_]
  (s/keys :req [:importer.datamodel/global-id]
          :req-un [::date ::fulltext ::location-meanings ::interaction-name
                   ::has-contexts ::facts ::attributes]))
(defmethod type-spec "event" [_]
  (s/keys :req-un [::features]))

然后定义s/multi-spec

(s/def ::datamodel
  (s/multi-spec type-spec :importer.datamodel/type))

现在,任何我们符合地图::datamodel可以解决基于其一个规范:importer.datamodel/type值。 我们可以分配给规范关键字规范将使用符合地图,例如最关键因素之一:

(s/def ::contexts (s/coll-of ::datamodel))

现在,如果你从我们下只具备了地图中的一个取出所需的关键:contexts ,规格可以告诉你,什么是错的。 例如,去掉:name从关键"organisation"地图:

(s/explain ::my-map data)
In: [:contexts 3]
val: #:importer.datamodel{:global-id "88844f94f79c75acfcb957bb41386149",
                          :type "organisation"}
fails spec: :playground.so/datamodel
at: [:contexts "organisation"]
predicate: (contains? % :name)

其他规格

对于没有地图:importer.datamodel/type你应该能够定义一个关键的规格。 例如,嵌套:has-contexts键具有的地图集合无:importer.datamodel/type ,但如果我们可以假设他们都会是类似的,我们可以这样写规范:

(s/def ::has-contexts
  (s/coll-of (s/keys :req-un [::context ::context-association-type
                              ::context-association-name ::priority])))

:has-contexts是我们已经覆盖了多规格上面的地图,并简单地注册一个规范这一关键将使规格符合其价值。 包含此规范的最关键的是:iois所以我们可以规范该键也:

(s/def ::iois (s/coll-of ::datamodel))

现在,符合输入到::my-map规范将自动覆盖更多的数据。

什么工具可以创建这样一个结构的规范呢?

正如你所看到的,写这个结构的全规格是不平凡的,但可能的。 我不知道任何现有的工具,可以自动推断出一个完整的,“正确的”规范这种结构。 这能有我们凭直觉认为:importer.datamodel/type是可用于分派到不同的关键s/keys规格-它仍然会使得潜在无效假设。 我认为工具- 帮助规范生成是在这种情况下,更现实和实用的。



Answer 2:

为什么不使用刚刚在交易之前插入旧数据的触发创建历史表。

像这样的事情,

CREATE TRIGGER SNAPSHOT_TRIGGER BEFORE
INSERT ON MY_TABLE REFERENCING NEW ROW MYNEWROW
FOR EACH ROW
BEGIN
 INSERT INTO "HISTORY_TABLE" VALUES(121,'','zzzz');
END;

(请检查语法)



Answer 3:

随着HANA 2 SPS 03,你可以使用系统版本表功能。 对于系统版本表 HANA自动保持能够独立于主表中可以访问的老唱片版本的一个单独的表。



文章来源: create spec from data