Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how to achieve unique key with elephant

3 views
Skip to first unread message

Roby Sadeli

unread,
Mar 12, 2010, 11:39:47 PM3/12/10
to
Hi lispers!

I have been tinkering with cl-elephant and I have a question:
How do I create unique key with elephant objects?

(defpclass book ()
((isbn :initarg :isbn
:accessor isbn
:index t)
(title :initarg :title
:accessor title
:index t)
(author :initarg :author
:accessor author
:index t)))

I know that there is oid (object id) for every object but what if I
want isbn to be unique? Do I have to check on my objects before
creating a new one?

Thanks for your help.

Alex Mizrahi

unread,
Mar 13, 2010, 11:16:35 AM3/13/10
to
RS> I know that there is oid (object id) for every object but what if I
RS> want isbn to be unique? Do I have to check on my objects before
RS> creating a new one?

Um, yes, I think you should do that check yourself. What to do in case of
non-unique keys is application-dependent, and so you need to implement a
reaction to it yourself anyway.

Roby Sadeli

unread,
Mar 13, 2010, 12:34:45 PM3/13/10
to
On Mar 13, 11:16 pm, "Alex Mizrahi" <udode...@users.sourceforge.net>
wrote:

Hi Alex. Thanks for your reply.
So I have to create a function (for example isbn-exists?) to check
whether a certain ISBN is already in the database, cl-elephant could
not do this automatically. I just find it strange to have to do this
manually.

Nicolas Neuss

unread,
Mar 13, 2010, 4:10:10 PM3/13/10
to
"Alex Mizrahi" <udod...@users.sourceforge.net> writes:

Throwing CFFI out of my asdf-registry in favor of UFFI, I succeeded in
making Elephant work, and very soon I also missed that feature
(especially since I am a used to CLSQL where one can enforce this with
":db-kind :key" in the slot definition).

As a remedy, I inserted such a test for uniqueness in the
initialize-instance method as follows:

(defmethod initialize-instance :before
((ml ele-mailing-list) &key name &allow-other-keys)
...
(assert (null (find-ele-ml name)) () "This list already exists"))

Having a ":db-kind :key" slot option would make such kind of tests
superfluous and indicate intent as early as possible, and this I would
like. OTOH, since CLOS itself does not have this feature, I do not
consider it a necessesity for Elephant.

Nicolas

Roby Sadeli

unread,
Mar 13, 2010, 11:10:07 PM3/13/10
to
On Mar 14, 4:10 am, Nicolas Neuss <lastn...@kit.edu> wrote:

Hi Nicolas,

Thanks for giving me a hint about using initialize-instance :before to
check whether a key is in the database. That solves my question.
I am a newbie, and I learn by looking at simple example codes and I
just think cl-elephant example codes out there are a rare.

Cheers.

Message has been deleted

Alex Mizrahi

unread,
Mar 14, 2010, 4:07:05 AM3/14/10
to
RS> So I have to create a function (for example isbn-exists?) to check
RS> whether a certain ISBN is already in the database,
RS> cl-elephant could not do this automatically.
RS> I just find it strange to have to do this manually.

Well, Elephant is a CLOS persistence layer, not an object-relational
mapping. You're supposed to write application in CLOS-style, Lisp-style
instead of mimicking concepts or relational databases.

It is not like automatic constraint checks really buy you anything. Let's
suppose you have to show an error message when book with given ISBN already
exists.
Here's how a function which adds book into a database might look with manual
checks:

(defun add-book (isbn title author)
(if (get-instances-by-value 'book 'isbn isbn)
(show-error "Book with this ISBN already exists.")
(make-instance 'book :isbn isbn :title title :author author)))

And how it could look like if make-instance would throw an error on unique
constraing violation:

(defun add-book (isbn title author)
(handler-case
(make-instance 'book :isbn isbn :title title :author author)
(unique-constraint-violation ()
(show-error "Book with this ISBN already exists."))))

It didn't get shorter with automatic checking, and first version looks more
straightforward IMHO.

Automatic checking might prevent database inconsistency in case your code
has a bug -- you forgot to check it.
But as Common Lisp is dynamically-typed and doesn't usually check stuff
automatically, it would be kind of strange that you want automatic check for
this specific piece and not all over your code.

But if you think you actually need constraints, it's not that hard to
implement them.
E.g. something like (beware -- proof-of-concept quality code):

(defun effective->direct-slot-def (class esd)
(find (slot-definition-name esd) (class-direct-slots class)
:key #'slot-definition-name))

(defmethod (setf slot-value-using-class) :before
(new-value (class persistent-metaclass) (instance persistent-object)
(slot-def indexed-slot-definition))
(when (eq (indexed-p (effective->direct-slot-def class slot-def)) :unique)
(let ((sc (get-con instance)))
(ensure-transaction (:store-controller sc)
(let* ((idx (or (get-slot-def-index slot-def sc)
(ensure-slot-def-index slot-def sc)))
(other-oid (get-value new-value idx)))
(when (and other-oid (/= other-oid (oid instance)))
(error "unique constraint violation, new value:~a for slot ~a is already
present for instance ~a" new-value (slot-definition-name slot-def)
(get-value new-value idx))))))))

And now if you define it like this:

(defpclass book ()
((isbn :initarg :isbn
:accessor isbn

:index :unique)


(title :initarg :title
:accessor title
:index t)
(author :initarg :author
:accessor author
:index t)))

It will do check on that index.

ELE> (make-instance 'book :isbn "3" :author "a" :title "t")
#<BOOK oid:201>
ELE> (make-instance 'book :isbn "3" :author "a" :title "t")
unique constraint violation, new value:3 for slot ISBN is already present
for instance 201
[Condition of type SIMPLE-ERROR]
Restarts:
0: [ABORT] Return to SLIME's top level.
ELE> (get-instances-by-value 'book 'isbn "3")
(#<BOOK oid:201>)

So, it did not add second instance because of unique constrain violation.
But it only works so because signaling error have aborted transaction.

Alex Mizrahi

unread,
Mar 14, 2010, 4:11:36 AM3/14/10
to
RS> Thanks for giving me a hint about using initialize-instance :before to
RS> check whether a key is in the database. That solves my question.
RS> I am a newbie, and I learn by looking at simple example codes and I
RS> just think cl-elephant example codes out there are a rare.

Well, this code has absolutely nothing to do with Elephant, it is a plain
CLOS code. That illustrates a point -- it is a good idea to learn CLOS if
you're using Elephant.

Alex Mizrahi

unread,
Mar 14, 2010, 4:21:09 AM3/14/10
to
AM> E.g. something like (beware -- proof-of-concept quality code):

To clarify, this code was for illustrative purposes only, it works only in
simple situations when there is no class inheritance.
If you think that Elephant should implement unique constraints on indices,
please write about it into Elephant's mailing list.
It is actually pretty easy to add this feature, but I don't know whether
other Elephant developers agree on this.

Roby Sadeli

unread,
Mar 15, 2010, 6:09:49 AM3/15/10
to
On Mar 14, 3:21 pm, "Alex Mizrahi" <udode...@users.sourceforge.net>
wrote:

Alex, thanks for helping me out. I still have a lot to learn. Your
remark about cl-elephant being a persistent CLOS layer, not ORM, I am
seeing the light now.

I think I am sticking with manual check, the latter one is more
complex, and as you said, it is not desirable to implement unique
constraints on indices, but actually it is what I originally asked
for :).

Thanks.

0 new messages