What's the meaning of the following error message, and how can I investigate and fix it?
:db.error/lookup-ref-attr-not-unique Attribute values not unique: :user/email
Here is the source code that will recreate the error:
(ns grok.db.add-user
(:require [grok.db.core :as SUT]
[grok.db.schema :refer [schema]]
[datomic.api :as d]))
(def sample-user
{:user/id (d/squuid)
:user/email "<mailto:test@test.com|test@test.com>"
:user/password "password12345"})
(defn fresh-db []
(let [db-uri (str "datomic:mem://" (gensym))
conn (SUT/create-conn db-uri)]
(d/transact conn schema)
(d/transact conn [sample-user])
conn))
(def conn (fresh-db))
conn
(d/entity (d/db conn) [:user/email "<mailto:test@test.com|test@test.com>"])
The code will create a user in the mem database, and retrieve it by its email address (for the purpose of having a user for tests)
Here is the related schema code for user:
[
;; ## User
;; - id (uuid)
;; - full-name (string)
;; - username (string)
;; - email (string => unique)
;; - password (string => hashed)
;; - token (string)
{:db/ident :user/id
:db/valueType :db.type/uuid
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity
:db/doc "ID of the User"}
{:db/ident :user/email
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "Email of the User"}
{:db/ident :user/password
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "Hashed Password of the User"}
]
Here is the code of create-conn:
(defn create-conn [db-uri]
(when db-uri
(d/create-database db-uri)
(let [conn (d/connect db-uri)]
conn)))
Here is the full error trace:
2. Unhandled clojure.lang.Compiler$CompilerException
Error compiling test/grok/db/add_user.clj at (21:1)
#:clojure.error{:phase :compile-syntax-check,
:line 21,
:column 1,
:source
"/home/yshen/programming/clojure/learn-immutable-stack-with-live-coding-ankie/grok/server/test/grok/db/add_user.clj"}
Compiler.java: 7648 clojure.lang.Compiler/load
REPL: 1 user/eval19658
REPL: 1 user/eval19658
Compiler.java: 7177 clojure.lang.Compiler/eval
Compiler.java: 7132 clojure.lang.Compiler/eval
core.clj: 3214 clojure.core/eval
core.clj: 3210 clojure.core/eval
interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn/fn
AFn.java: 152 clojure.lang.AFn/applyToHelper
AFn.java: 144 clojure.lang.AFn/applyTo
core.clj: 665 clojure.core/apply
core.clj: 1973 clojure.core/with-bindings*
core.clj: 1973 clojure.core/with-bindings*
RestFn.java: 425 clojure.lang.RestFn/invoke
interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn
main.clj: 437 clojure.main/repl/read-eval-print/fn
main.clj: 437 clojure.main/repl/read-eval-print
main.clj: 458 clojure.main/repl/fn
main.clj: 458 clojure.main/repl
main.clj: 368 clojure.main/repl
RestFn.java: 137 clojure.lang.RestFn/applyTo
core.clj: 665 clojure.core/apply
core.clj: 660 clojure.core/apply
regrow.clj: 20 refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 152 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
AFn.java: 22 clojure.lang.AFn/run
session.clj: 202 nrepl.middleware.session/session-exec/main-loop/fn
session.clj: 201 nrepl.middleware.session/session-exec/main-loop
AFn.java: 22 clojure.lang.AFn/run
Thread.java: 834 java.lang.Thread/run
1. Caused by datomic.impl.Exceptions$IllegalArgumentExceptionInfo
:db.error/lookup-ref-attr-not-unique Attribute values not unique: :user/email
{:cognitect.anomalies/category :cognitect.anomalies/incorrect,
:cognitect.anomalies/message "Attribute values not unique: :user/email",
:db/error :db.error/lookup-ref-attr-not-unique}
error.clj: 79 datomic.error/arg
error.clj: 74 datomic.error/arg
error.clj: 77 datomic.error/arg
error.clj: 74 datomic.error/arg
db.clj: 590 datomic.db/resolve-lookup-ref
db.clj: 569 datomic.db/resolve-lookup-ref
db.clj: 610 datomic.db/extended-resolve-id
db.clj: 606 datomic.db/extended-resolve-id
db.clj: 621 datomic.db/resolve-id
db.clj: 614 datomic.db/resolve-id
db.clj: 2295 datomic.db.Db/entity
api.clj: 171 datomic.api/entity
api.clj: 169 datomic.api/entity
add_user.clj: 21 grok.db.add-user/eval19672
add_user.clj: 21 grok.db.add-user/eval19672
Compiler.java: 7177 clojure.lang.Compiler/eval
Compiler.java: 7636 clojure.lang.Compiler/load
REPL: 1 user/eval19658
REPL: 1 user/eval19658
Compiler.java: 7177 clojure.lang.Compiler/eval
Compiler.java: 7132 clojure.lang.Compiler/eval
core.clj: 3214 clojure.core/eval
core.clj: 3210 clojure.core/eval
interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn/fn
AFn.java: 152 clojure.lang.AFn/applyToHelper
AFn.java: 144 clojure.lang.AFn/applyTo
core.clj: 665 clojure.core/apply
core.clj: 1973 clojure.core/with-bindings*
core.clj: 1973 clojure.core/with-bindings*
RestFn.java: 425 clojure.lang.RestFn/invoke
interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn
main.clj: 437 clojure.main/repl/read-eval-print/fn
main.clj: 437 clojure.main/repl/read-eval-print
main.clj: 458 clojure.main/repl/fn
main.clj: 458 clojure.main/repl
main.clj: 368 clojure.main/repl
RestFn.java: 137 clojure.lang.RestFn/applyTo
core.clj: 665 clojure.core/apply
core.clj: 660 clojure.core/apply
regrow.clj: 20 refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 152 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
AFn.java: 22 clojure.lang.AFn/run
session.clj: 202 nrepl.middleware.session/session-exec/main-loop/fn
session.clj: 201 nrepl.middleware.session/session-exec/main-loop
AFn.java: 22 clojure.lang.AFn/run
Thread.java: 834 java.lang.Thread/run
I ran into this problem when I followed the coding example at https://www.youtube.com/watch?v=Fz6LxSSc_GE at 6:06 (The Immutable Stack - Building Anki Clone using Clojure, Datomic and ClojureScript (Part 5)). In the video, there was no error, bui I did, and could reproduce it as above.I guess "lookup ref attr not declared as :db/unique" would be a more helpful error message.
It's strange that once I added :db/unique to the existing schema without transacting, but just evaluate the schema definition, it works for retrieving the user by email address.
But later, when I actually transacted the updated schema, then I run into trouble of "Error: {:db/error :db.error/unique-without-index, :attribute :user/email}"
The schema for :user/email does not include a :db/unique constraint, therefore you cannot use this attribute for lookups as you do in your d/entity call
Thus “lookup ref attr not unique” in the error message
@favila Thanks for the quick and effective help!
How do I fix this error?
"Error: {:db/error :db.error/unique-without-index, :attribute :user/email}"
The error happened when I added more entities to the schema and did (d/transact conn schema)
again to update the schema.
This is a consequence of my prior error referred in https://clojurians.slack.com/archives/C03RZMDSH/p1604199338251700 where I had to change my schema to add :db/unique constraint to :user/email
I wouldn't mind to start from scratch with a new database. But I have not learned how to do that yet.
But if it were in a production system, when I modify my schema, what's the proper way to correct and update?
Are you using cloud or on prem? You cite cloud docs but this sounds like an on-prem problem. (Cloud indexs all values by default)
On on-prem, there is a :db/index true
I'm using on-prem, actually just dev one. I'll take look of :db/index tree @favila Thanks for the pointer!
https://docs.datomic.com/on-prem/schema.html#altering-schema-attributes
> All alterations happen synchronously, except for adding an AVET index. If you want to know when the AVET index is available, call https://docs.datomic.com/on-prem/javadoc/datomic/Connection.html#syncSchema(long). In order to add :db/unique, you must first have an AVET index including that attribute.
(quote from the docs)
@favila Yes, the following worked:
(def tx-add-index @(d/transact conn [{:db/id :user/email
:db/index true}]))
(def tx-fix @(d/transact conn [{:db/id :user/email
:db/unique :db.unique/identity}]))
where conn
is a connection to the on-prem (dev) database.
Thanks again for your coaching!I wish there was a catalog of datomic errors with explanations and guidance. Searching the net for "datomic unique-wirhout-index&t" yields nothing :-(
You need a value index before you can make a value unique. See the docs on schema changes, it has a table of all legal transitions