diff --git a/src/datascript/core.cljs b/src/datascript/core.cljs index 0ad8e8c5..4d8a13b5 100644 --- a/src/datascript/core.cljs +++ b/src/datascript/core.cljs @@ -290,13 +290,32 @@ (update-in [:db-after] with-datom datom) (update-in [:tx-data] conj datom))) -(defn- reverse-ref? [attr] - (= "_" (nth (name attr) 0))) +(defn- ^boolean reverse-ref? [attr] + (cond + (keyword? attr) + (= "_" (nth (name attr) 0)) + + (string? attr) + (boolean (re-matches #"(?:([^/]+)/)?_([^/]+)" attr)) + + :else + (throw (js/Error. "Bad attribute type: " attr ", expected keyword or string")))) (defn- reverse-ref [attr] - (if (reverse-ref? attr) - (keyword (namespace attr) (subs (name attr) 1)) - (keyword (namespace attr) (str "_" (name attr))))) + (cond + (keyword? attr) + (if (reverse-ref? attr) + (keyword (namespace attr) (subs (name attr) 1)) + (keyword (namespace attr) (str "_" (name attr)))) + + (string? attr) + (let [[_ ns name] (re-matches #"(?:([^/]+)/)?([^/]+)" attr)] + (if (= "_" (nth name 0)) + (if ns (str ns "/" (subs name 1)) (subs name 1)) + (if ns (str ns "/_" name) (str "_" name)))) + + :else + (throw (js/Error. "Bad attribute type: " attr ", expected keyword or string")))) (defn- validate-eid [eid at] (when-not (number? eid) diff --git a/src/datascript/impl/entity.cljs b/src/datascript/impl/entity.cljs index af188536..6a4ef2f3 100644 --- a/src/datascript/impl/entity.cljs +++ b/src/datascript/impl/entity.cljs @@ -79,13 +79,11 @@ (get [this attr] (if (= attr ":db/id") eid - (if-let [[_ ns name] (re-matches #"(?:([^/]+)/)?_([^/]+)" attr)] - (let [attr (if ns (str ns "/" name) name)] - (-> (-lookup-backwards db eid attr nil) - multival->js)) + (if (dc/reverse-ref? attr) + (-> (-lookup-backwards db eid (dc/reverse-ref attr) nil) + multival->js) (cond-> (-lookup this attr) - (dc/multival? db attr) - multival->js)))) + (dc/multival? db attr) multival->js)))) (forEach [this f] (doseq [[a v] (js-seq this)] (f v a this))) diff --git a/test/js/tests.html b/test/js/tests.html index 403f4215..e4d6dcaf 100644 --- a/test/js/tests.html +++ b/test/js/tests.html @@ -117,15 +117,33 @@ function test_db_with() { var db = d.empty_db(); var db1 = d.db_with(db, [[":db/add", 1, "name", "Ivan"], - [":db/add", 1, "age", 17]]); + [":db/add", 1, "age", 17]]); var db2 = d.db_with(db1, [{":db/id": 2, - "name": "Igor", - "age": 35}]); + "name": "Igor", + "age": 35}]); var q = '[:find ?n ?a :where [?e "name" ?n] [?e "age" ?a]]'; assert_eq_set([["Ivan", 17]], d.q(q, db1)); assert_eq_set([["Ivan", 17], ["Igor", 35]], d.q(q, db2)); } + function test_nested_maps() { + var q = '[:find ?e ?a ?v :where [?e ?a ?v]]'; + + var db0 = d.empty_db({"profile": {":db/valueType": ":db.type/ref"}}); + var db = d.db_with(db0, [{"name": "Igor", "profile": {"email": "@2"} }]); + assert_eq_set([[1, "name", "Igor"], [1, "profile", 2], [2, "email", "@2"]], d.q(q, db)); + + db = d.db_with(db0, [{"email": "@2", "_profile": {"name": "Igor"}}]); + assert_eq_set([[1, "email", "@2"], [2, "name", "Igor"], [2, "profile", 1]], d.q(q, db)); + + db0 = d.empty_db({"user/profile": {":db/valueType": ":db.type/ref"}}); + db = d.db_with(db0, [{"name": "Igor", "user/profile": {"email": "@2"} }]); + assert_eq_set([[1, "name", "Igor"], [1, "user/profile", 2], [2, "email", "@2"]], d.q(q, db)); + + db = d.db_with(db0, [{"email": "@2", "user/_profile": {"name": "Igor"}}]); + assert_eq_set([[1, "email", "@2"], [2, "name", "Igor"], [2, "user/profile", 1]], d.q(q, db)); + } + function test_init_db() { var q = '[:find ?n ?a ?tx :where [?e "name" ?n ?tx] [?e "age" ?a]]'; var db_ok = function(db) { @@ -359,6 +377,7 @@ function run_tests() { return test_fns([ test_db_with, + test_nested_maps, test_init_db, test_dbfn_call, test_schema,