[elephant-devel] Querying Advice
Daniel Salama
lists at infoway.net
Tue Nov 14 19:50:19 UTC 2006
Ian et al,
Based on my comment to Robert and that of Pierre, could you, or
anyone, please clarify this for me (and maybe others):
If making a class persistent means that there is no need to add it to
root or to any persistent collection, when I look at Robert's sample
code, I see (excerpts):
(defclass User ()
((username :type 'string :initform "" :initarg :uname :accessor
username-of)
(password :type 'string :initform "" :initarg :pword :accessor
password-of)
(email :type 'string :initform "" :initarg :email :accessor email-
of)
(fullname :type 'string :initform "" :initarg :fullname :accessor
fullname-of)
(balance :type 'integer :initform 0 :initarg :balance :accessor
balance-of)))
(defun random-users (n)
(dotimes (x n)
(let ((u (make-instance
'User
:uname (format nil "user~A" x)
:pword (random-password)
:email (format nil "user~A at .nowheresville.org" x)
:fullname (format nil "~A~A ~A~A" (random-password) x
(random-password) x)
:balance (random 100))))
(add-to-root x u))))
There is an explicit add-to-root in random-users. I suppose the
reason for this is because User class does not inherit from
persistent-metaclass and in order to be able to "search for" or
retrieve that object (could this also be the reason for the
additional storage overhead, as you pointed out yesterday?). Right?
So, if my understanding is correct, defining User with defpclass
instead would mean that you don't have to add-to-root because it will
be automatically persisted. However, after the function exits, there
will be no reference to that persistent object and will therefore be
eventually garbage collected (whether or not the persistent space
will be reclaimed is a different story, as you mentioned in your
email). Is that right? If so, how could I avoid for the User objects
to be garbage collected in this case, since there really is no other
reference to these objects after creating them? Or, if the objects
are NOT garbage collected, how could I manually "delete" any of them?
Now, if I (or Robert) had defined the User class as:
(defpclass User ()
((username :type 'string :initform "" :initarg :uname :accessor
username-of :index t)
(password :type 'string :initform "" :initarg :pword :accessor
password-of)
(email :type 'string :initform "" :initarg :email :accessor email-
of)
(fullname :type 'string :initform "" :initarg :fullname :accessor
fullname-of)
(balance :type 'integer :initform 0 :initarg :balance :accessor
balance-of)))
where (notice how it's defined with defpclass) the username slot is
indexed, the system would automatically store a reference to the
object in the slot index, and there would be no need to use the add-
to-root in random-users. Correct? If that's the case, how would I
then go about removing this user object from persistence? Would it be
by setting the indexed slot value to NIL?
On another note, if I want to create a collection of users, I don't
have to store these users in a collection. Simply making them inherit
from persistent-metaclass and indexing them would do so automatically
(just like the example above). Right? How about this, then:
(asdf:operate 'asdf:load-op :elephant-tests)
(in-package :elephant-tests)
(setf *default-spec* *testbdb-spec*)
(open-store *default-spec*)
(defpclass state ()
((abbr :type 'string :initform "" :initarg :abbr :accessor abbr-
of :index t)
(name :type 'string :initform "" :initarg :name :accessor name-of)))
(defpclass zip-code ()
((zip :type 'string :initform "" :initarg :zip :accessor zip-
of :index t)
(city :type 'string :initform "" :initarg :city :accessor city-of)
(county :type 'string :initform "" :initarg :county :accessor
county-of)
(state :initform nil :initarg :state :accessor state-of)))
(defmethod print-object ((obj state) stream)
(format stream "State (abbr, name) = (~A, ~A)" (abbr-of obj) (name-
of obj)))
(defmethod print-object ((obj zip-code) stream)
(format stream "Zip (zip, city, county, state) = (~A, ~A, ~A, ~A)"
(zip-of obj) (city-of obj) (county-of obj) (state-of obj)))
(let* ((s1 (make-instance 'state :abbr "FL" :name "Florida"))
(s2 (make-instance 'state :abbr "NY" :name "New York"))
(z1 (make-instance 'zip-code :zip "33015" :city
"Miami" :county "Dade" :state s1))
(z2 (make-instance 'zip-code :zip "13605" :city
"Adams" :county "Jefferson" :state s2))
(z2 (make-instance 'zip-code :zip "33160" :city "Sunny Isles
Beach" :county "Dade" :state s1)))
(print s1)
(print s2)
(print z1)
(print z2)
(print z3))
Here, I'm creating a couple of state objects and a couple of zip-code
objects. Since a zip-code can only belong to one state, they have a
reference back to the state in order to "quickly" determine the state
they belong to.
A couple of questions/comments here:
1) If I wanted to ask a state to give me all the zip-code(s) within
it, would I create a slot in the state class to hold a collection of
zip-code references? Or would I simply create a state-class method like:
(defmethod get-zip-codes ((obj state))
(get-instances-by-value 'zip-code 'state obj))
This does not work because the state slot in zip-code is not indexed.
Also, from the code above, I don't know how to get a reference to the
btree in order to create a cursor so that I can linearly traverse the
zip-code(s) to return only those zip-code(s) which belong to that state.
Of course, I would probably want to index the state slot of zip-code
because there are tens of thousands of zip codes and I wouldn't want
to linearly traverse them, but I just wanted to illustrate the
problem to get some additional feedback (the overhead of maintaining
a secondary index on state wouldn't matter too much to me because
changes to this class/objects are very rare but access is much more
frequent)
2) Maybe this is a totally different issue and mainly caused by my
lisp ignorance:
If I have:
(defparameter *s1* (get-instances-by-value 'state 'abbr "FL"))
(defparameter *s2* (get-instances-by-value 'state 'abbr "NY"))
(defparameter *z1* (get-instances-by-value 'zip-code 'zip "33015"))
(defparameter *z2* (get-instances-by-value 'zip-code 'zip "13605"))
(defparameter *z3* (get-instances-by-value 'zip-code 'zip "33160"))
and I want to remove the association of zip code 33160 from the "FL"
state object, I would think all I have to do is:
(setf (state-of *z3*) nil)
However, when I do so, I get this error message:
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION (SETF STATE-OF) (1)>
when called with arguments
(NIL
(Zip (zip, city, county, state) = (33160, Sunny Isles Beach,
Dade, State (abbr, name) = (FL, Florida)))).
[Condition of type SIMPLE-ERROR]
Restarts:
0: [ABORT-REQUEST] Abort handling SLIME request.
1: [ABORT] Exit debugger, returning to top level.
Backtrace:
0: ((SB-PCL::FAST-METHOD NO-APPLICABLE-METHOD (T)) #<unavailable
argument> #<unavailable argument> #<STANDARD-GENERIC-FUNCTION (SETF
STATE-OF) (1)> (NIL (Zip (zip, city, county, state) = (33160, Sunny
Isles Beach, Dade, State (abbr, name) = (FL, Florida)))))
1: ((SB-PCL::FAST-METHOD NO-APPLICABLE-METHOD (T)) #<unavailable
argument> #<unavailable argument> #<STANDARD-GENERIC-FUNCTION (SETF
STATE-OF) (1)> (NIL (Zip (zip, city, county, state) = (33160, Sunny
Isles Beach, Dade, State (abbr, name) = (FL, Florida)))))
2: (SB-INT:EVAL-IN-LEXENV (SETF (STATE-OF *Z3*) NIL) #<NULL-LEXENV>)
3: (SWANK::EVAL-REGION "(setf (state-of *z3*) nil)" T)
3) So, going back to the previous questions in the email, if I wanted
to permanently remove zip code "33015", how would I go about it?
Thanks again and thanks for the patience.
- Daniel
On Nov 14, 2006, at 9:04 AM, Ian Eslick wrote:
> Persistent classes in the current model aren't easy to fully delete -
> you can only make the unreachable by having no reference to them
> reachable from the controller or class roots. You can drop an object
> from the index and there is a way to delete the object from the main
> system BTree - but until 4.4 you couldn't reclaim that space
> (unless it
> happened to be reused opportunistically). In 4.4 you can compact the
> DB, but elephant does not take advantage of this yet.
>
> Ideally there would be an automated way to 'migrate' which is a
> stop-and-copy model of GC - everything reachable from the
> controller and
> class roots gets copied, anything that isn't reachable stays in the
> old
> DB. This compacts and cleans the DB. I hope that we'll clean up the
> model of space reclamation and provide something akin to GC in the not
> too distant future. It's not a high priority in large part because
> on-disk space has little implication on in-memory space and disk is ~
> free. (Although I do suffer from too many backup copies of my 6GB
> DB -
> fortunately I just upgraded my computer and doubled my hard drive
> space
> so I'm OK for a awhile longer!)
>
> Ian
>
> Pierre THIERRY wrote:
>> Scribit Daniel Salama dies 13/11/2006 hora 21:00:
>>
>>> So, if I make my class persistent, it's persistent... period! I
>>> don't
>>> need to add it to a collection or to the root.
>>>
>>
>> Well, I did not know that before that discussion. I think it's not
>> clearly indicated in the docs...
>>
>> And then I wonder: if I use persistent classes, how do I remove an
>> object from the store?
>>
>> Curiously,
>> Nowhere man
>>
>> ---------------------------------------------------------------------
>> ---
>>
>> _______________________________________________
>> elephant-devel site list
>> elephant-devel at common-lisp.net
>> http://common-lisp.net/mailman/listinfo/elephant-devel
> _______________________________________________
> elephant-devel site list
> elephant-devel at common-lisp.net
> http://common-lisp.net/mailman/listinfo/elephant-devel
More information about the elephant-devel
mailing list