diff --git a/CHANGELOG.md b/CHANGELOG.md index d054a1da..96e6320f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `(datascript/datom e a v & [tx added])` call for creating new datoms - Added missing aggregate funs: `avg`, `median`, `variance`, `stddev` and `count-distinct` (issue #42, thx [@montyxcantsin](https://github.com/montyxcantsin)) - `min` and `max` aggregates use comparator instead of default js `<` comparison +- Fixed a bug when fn inside a query on empty relation returned non-empty result # 0.6.0 diff --git a/src/datascript/query.cljs b/src/datascript/query.cljs index a6bb6ea6..945e9931 100644 --- a/src/datascript/query.cljs +++ b/src/datascript/query.cljs @@ -181,19 +181,26 @@ ;; -(defn in->rel [form value] - (condp looks-like? form - '[_ ...] ;; collection binding [?x ...] - (reduce sum-rel - (map #(in->rel (first form) %) value)) - '[[*]] ;; relation binding [[?a ?b]] - (reduce sum-rel - (map #(in->rel (first form) %) value)) - '[*] ;; tuple binding [?a ?b] - (reduce prod-rel - (map #(in->rel %1 %2) form value)) - '_ ;; regular binding ?x - (Relation. {form 0} [#js [value]]))) +(defn in->rel + ([form] + (let [attrs (as-> form form + (flatten form) + (filter #(and (symbol? %) (not= '... %) (not= '_ %)) form) + (zipmap form (range)))] + (Relation. attrs ()))) + ([form value] + (condp looks-like? form + '[_ ...] ;; collection binding [?x ...] + (reduce sum-rel + (map #(in->rel (first form) %) value)) + '[[*]] ;; relation binding [[?a ?b]] + (reduce sum-rel + (map #(in->rel (first form) %) value)) + '[*] ;; tuple binding [?a ?b] + (reduce prod-rel + (map #(in->rel %1 %2) form value)) + '_ ;; regular binding ?x + (Relation. {form 0} [#js [value]])))) (defn parse-rules [rules] (let [rules (if (string? rules) (cljs.reader/read-string rules) rules)] ;; for datascript.js interop @@ -347,15 +354,15 @@ (context-resolve-val context f)) [context production] (rel-prod-by-attrs context (filter symbol? args)) tuple-fn (-call-fn context production fun args) - new-rel (->> (:tuples production) - (map #(let [val (tuple-fn %) - rel (in->rel out val)] - (prod-rel (Relation. (:attrs production) [%]) rel))) - (reduce sum-rel))] + new-rel (if-let [tuples (not-empty (:tuples production))] + (->> tuples + (map #(let [val (tuple-fn %) + rel (in->rel out val)] + (prod-rel (Relation. (:attrs production) [%]) rel))) + (reduce sum-rel)) + (prod-rel production (in->rel out)))] (update-in context [:rels] conj new-rel))) - - ;;; RULES (defn rule? [context clause] diff --git a/test/test/datascript.cljs b/test/test/datascript.cljs index 7f75ec60..d3393c54 100644 --- a/test/test/datascript.cljs +++ b/test/test/datascript.cljs @@ -569,7 +569,15 @@ [(+ ?a1 ?a2) ?a12] [(= ?a12 ?a3)]] db) - #{[1 2 3] [2 1 3]}))))) + #{[1 2 3] [2 1 3]}))) + + (testing "Function on empty rel" + (is (= (d/q '[:find ?e ?y + :where [?e :salary ?x] + [(+ ?x 100) ?y]] + [[0 :age 15] [1 :age 35]]) + #{}))) + )) (deftest test-rules @@ -960,3 +968,4 @@ (is (= db (cljs.reader/read-string (pr-str db))))))) ;; (t/test-ns 'test.datascript) +