diff --git a/README.md b/README.md index 4c5d430b6..497388b85 100644 --- a/README.md +++ b/README.md @@ -1486,6 +1486,62 @@ Merged ; => true ``` +`:union` is similar to `:or`, except `:union` combines map schemas in different disjuncts with `:or`. +For example, `UnionMaps` is equivalent to `[:map [:x [:or :int :string]] [:y [:or :int :string]]]`. + +```clojure +(def OrMaps + (m/schema + [:or + [:map [:x :int] [:x :string]] + [:map [:x :string] [:x :int]]] + {:registry registry})) + +(def UnionMaps + (m/schema + [:union + [:map [:x :int] [:x :string]] + [:map [:x :string] [:x :int]]] + {:registry registry})) + +(m/validate OrMaps {:x "kikka" :y "kikka"}) +; => false + +(m/validate UnionMaps {:x "kikka" :y "kikka"}) +; => true +``` + +`:merge` and `:union` differ on schemas with common keys. `:merge` chooses the right-most +schema of common keys, and `:union` combines them with `:or`. +For example, `MergedCommon` is equivalent to `[:map [:x :int]]`, and `UnionCommon` +is equivalent to `[:map [:x [:or :string :int]]]`. + +```clojure +(def MergedCommon + (m/schema + [:merge + [:map [:x :string]] + [:map [:x :int]]] + {:registry registry})) + +(def UnionCommon + (m/schema + [:union + [:map [:x :string]] + [:map [:x :int]]] + {:registry registry})) + +(m/validate MergedCommon {:x "kikka"}) +; => true +(m/validate MergedCommon {:x 1}) +; => false +(m/validate UnionCommon {:x "kikka"}) +; => true +(m/validate UnionCommon {:x 1}) +; => true +``` + + ## Persisting schemas Writing and Reading schemas as [EDN](https://github.com/edn-format/edn), no `eval` needed. diff --git a/test/malli/util_test.cljc b/test/malli/util_test.cljc index e5fe993c9..1f3652726 100644 --- a/test/malli/util_test.cljc +++ b/test/malli/util_test.cljc @@ -822,6 +822,32 @@ (is (= {:x [:str "x"]} (m/parse s {:x "x"}))) (is (= {:x 1} (m/parse s {:x 1}))))) + (testing "merge vs union" + (let [->s #(->> [% + [:map [:x :string]] + [:map [:x :int]]]) + u (->s :union) + m (->s :merge)] + (is (m/validate u {:x 1})) + (is (m/validate u {:x "a"})) + (is (m/validate m {:x 1})) + (is (m/explain m {:x "a"})))) + + (testing "union vs or" + (let [->s #(->> [% + [:map [:x :string] [:y :int]] + [:map [:x :int] [:y :string]]]) + u (->s :union) + o (->s :or)] + (is (m/validate u {:x 1 :y 1})) + (is (m/validate u {:x 1 :y "a"})) + (is (m/validate u {:x "a" :y 1})) + (is (m/validate u {:x "a" :y "a"})) + (is (m/explain o {:x 1 :y 1})) + (is (m/validate o {:x 1 :y "a"})) + (is (m/validate o {:x "a" :y 1})) + (is (m/explain o {:x "a" :y "a"})))) + (testing "select-keys" (let [s (->> [:select-keys [:schema