从数据创建规范
create spec from data
我正在尝试仅根据数据创建规范。我有非常复杂的数据结构 - 所有嵌套映射。
{: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)
...
什么工具可以为这样的结构创建规范?
为什么不使用在交易之前插入旧数据的触发器来创建历史记录table。
像这样,
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;
(请检查语法)
借助 HANA 2 SPS 03,您可以使用 系统版本控制 tables 功能。
对于 system-versioned tables,HANA 会自动保留一个单独的 table 旧记录版本,可以独立于主 table.
进行访问。
I am trying to use this tool: https://github.com/stathissideris/spec-provider
spec-provider 没有给您想要的结果,因为您的数据是一个复杂的 nested/recursive 结构。其中一些地图最好用 multi-specs 进行规范,但规范提供者不会那样做;其文档中的警告之一说 没有尝试推断多规格。
正确指定其中一些地图的唯一方法是使用多规格,它们的规格将取决于它们的 :importer.datamodel/type
值。
首先,让我们看一下顶级键(假设映射位于名为 data
的绑定中):
(keys data) => (:contexts :datasources :iois)
为最外面的地图创建一个 s/keys
规范:
(s/def ::my-map
(s/keys :req-un [::contexts ::datasources ::iois]))
这些键是非限定的,但我们必须使用带: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)]
...}
(即将推出的规范 alpha 应该可以更轻松地从这些数据中以编程方式定义规范。)
多规格
我们可以看到有些地图形状没有 :importer.datamodel/type
,但我们可以为有的地图形状编写多规格。首先定义一个 multimethod 用于调度类型键:
(defmulti type-spec :importer.datamodel/type)
然后为每个:importer.datamodel/type
值写一个defmethod
。这里有几个例子:
(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
下指定的其中一个映射中删除所需的密钥,spec 可以告诉您哪里出了问题。例如,从 "organisation"
映射中删除 :name
键:
(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
规范的输入将自动覆盖更多数据。
What tool can create the spec for such a structure?
如您所见,为该结构编写完整的规范并非易事,但却是可能的。我不知道有任何现有工具可以自动推断出此结构的完整 "correct" 规范。它必须凭直觉认为 :importer.datamodel/type
是一个可用于分派到不同 s/keys
规范的密钥——而且它仍然会做出可能无效的假设。我认为工具-辅助规范生成在这种情况下更现实和实用。
我正在尝试仅根据数据创建规范。我有非常复杂的数据结构 - 所有嵌套映射。
{: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)
...
什么工具可以为这样的结构创建规范?
为什么不使用在交易之前插入旧数据的触发器来创建历史记录table。
像这样,
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;
(请检查语法)
借助 HANA 2 SPS 03,您可以使用 系统版本控制 tables 功能。 对于 system-versioned tables,HANA 会自动保留一个单独的 table 旧记录版本,可以独立于主 table.
进行访问。I am trying to use this tool: https://github.com/stathissideris/spec-provider
spec-provider 没有给您想要的结果,因为您的数据是一个复杂的 nested/recursive 结构。其中一些地图最好用 multi-specs 进行规范,但规范提供者不会那样做;其文档中的警告之一说 没有尝试推断多规格。
正确指定其中一些地图的唯一方法是使用多规格,它们的规格将取决于它们的 :importer.datamodel/type
值。
首先,让我们看一下顶级键(假设映射位于名为 data
的绑定中):
(keys data) => (:contexts :datasources :iois)
为最外面的地图创建一个 s/keys
规范:
(s/def ::my-map
(s/keys :req-un [::contexts ::datasources ::iois]))
这些键是非限定的,但我们必须使用带: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)]
...}
(即将推出的规范 alpha 应该可以更轻松地从这些数据中以编程方式定义规范。)
多规格
我们可以看到有些地图形状没有 :importer.datamodel/type
,但我们可以为有的地图形状编写多规格。首先定义一个 multimethod 用于调度类型键:
(defmulti type-spec :importer.datamodel/type)
然后为每个:importer.datamodel/type
值写一个defmethod
。这里有几个例子:
(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
下指定的其中一个映射中删除所需的密钥,spec 可以告诉您哪里出了问题。例如,从 "organisation"
映射中删除 :name
键:
(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
规范的输入将自动覆盖更多数据。
What tool can create the spec for such a structure?
如您所见,为该结构编写完整的规范并非易事,但却是可能的。我不知道有任何现有工具可以自动推断出此结构的完整 "correct" 规范。它必须凭直觉认为 :importer.datamodel/type
是一个可用于分派到不同 s/keys
规范的密钥——而且它仍然会做出可能无效的假设。我认为工具-辅助规范生成在这种情况下更现实和实用。