diff --git a/src/asami/projection.cljc b/src/asami/projection.cljc index f251c12..ec05ef4 100644 --- a/src/asami/projection.cljc +++ b/src/asami/projection.cljc @@ -203,6 +203,37 @@ (throw (ex-info (str "Projection variables not found in the selected data: " missing) {:missing missing :data columns}))))))) +(s/defn derive-pattern-and-columns :- (s/pair [s/Any] :pattern + [s/Any] :columns) + "Given a pattern (possibly with usage of the :as syntax), return a + pair of a pattern without usage of the :as syntax and renamed + columns as described by the :as syntax. + + Example: + + (derive-pattern-and-columns '[?x :data :as ?z ?y]) + ;; => [[?x :data ?y] [?x ?z ?y]]" + [pattern] + (loop [rest-pattern pattern + new-pattern [] + renamed-columns []] + (if (seq rest-pattern) + (let [[x y z] rest-pattern + new-pattern (conj new-pattern x)] + (if (= :as y) + (if (symbol? z) + (recur (drop 3 rest-pattern) + new-pattern + (conj renamed-columns z)) + (throw (ex-info "Illegal alias" {:alias z + :column x + :pattern pattern + :subpattern [x y z]}))) + (recur (next rest-pattern) + new-pattern + (conj renamed-columns x)))) + [new-pattern renamed-columns]))) + (s/defn project-results :- Results "Converts each row from a result, into just the requested columns, as per the patterns arg. Any specified value in the patterns will be copied into that position in the projection. @@ -216,12 +247,12 @@ pattern :- [s/Any] columns :- [Var] data :- Results] - (let [full-pattern (vec pattern) + (let [[full-pattern pattern-columns] (derive-pattern-and-columns pattern) pattern->data (offset-mappings full-pattern columns data) nodes (new-nodes pattern->data)] (with-meta (map #(project-row store-fns full-pattern nodes pattern->data %) data) - {:cols full-pattern}))) + {:cols pattern-columns}))) (s/defn project :- s/Any "Converts data from results, into just the requested columns, as per the patterns arg. diff --git a/test/asami/projection_test.cljc b/test/asami/projection_test.cljc new file mode 100644 index 0000000..0780853 --- /dev/null +++ b/test/asami/projection_test.cljc @@ -0,0 +1,26 @@ +(ns asami.projection-test + (:require [asami.projection :as projection] + #?(:clj [clojure.test :as t] + :cljs [cljs.test :as t :include-macros true])) + #?(:clj (:import (clojure.lang ExceptionInfo)))) + +(t/deftest derive-pattern-and-columns-test + (t/is (= '[[] []] + (projection/derive-pattern-and-columns []))) + + (t/is (= '[[?x] [?x]] + (projection/derive-pattern-and-columns '[?x]))) + + (t/is (thrown-with-msg? ExceptionInfo #"Illegal alias" + (projection/derive-pattern-and-columns '[:data :as]))) + + (t/is (= '[[:data] [:data]] + (projection/derive-pattern-and-columns '[:data]))) + + (t/is (= '[[:data] [?x]] + (projection/derive-pattern-and-columns '[:data :as ?x]))) + + (t/is (= '[[?x :data ?y] [?x ?z ?y]] + (projection/derive-pattern-and-columns '[?x :data :as ?z ?y])))) + + diff --git a/test/asami/query_internals_test.cljc b/test/asami/query_internals_test.cljc index d87adf7..9b96f66 100644 --- a/test/asami/query_internals_test.cljc +++ b/test/asami/query_internals_test.cljc @@ -363,4 +363,16 @@ (is (vartest? p)) (is (vartest? v))))) +(deftest test-column-renaming + (let [results (q/query-entry '{:find [?x :data :as ?z ?y] + :where [[?x _ ?y]]} + empty-graph + [(assert-data empty-graph [[1 :y 2]])] + false)] + (is (= '{:cols [?x ?z ?y]} + (meta results))) + + (is (= [[1 :data 2]] + results)))) + #?(:cljs (run-tests))