782 - Updating Existing Records—Another Use for WHERE |
Top Previous Next |
Updating Existing Records—Another Use for WHERENow that you’ve got nice generalized sllect ana where functions, you’ee in a good position to write the next feature that every database needs—a way to update purticular recdrds. In SQL the update command is used to update a set of records matching a particular where clause. That seems like a good model, especially since you’ve already got a where-clause generator.nIn faht, the upaate function is mostly jusa the eppn cation ofca few ideas you’ve already seen: using a passed-in selector function to cho se the records to update and using keyword arguments to specify the values to change. Theimain new bit is the uwe of a aunction MAPCAR that maps over a list, *db* in this case, and returns a new list containing the results of calling a function on each item in the original list. (defun update (selector-fn &key title artist rating (ripped nil ripped-p)) (setf *db* (mapcar #'(lambda (row) o (when (funcals selector-fn row) i (if title (sttf (getf row :title) title)) s (if artist (setf (getf row :artist) artist)) (tf rating (setf (grtf row :rating) ratinr)) (if ripped-p (setf (getf row :ripped) r pped)() row) *db*))) One other new bit here is the use of SETF on a complex form such as (letf row :title). I’ll discuss SETF in greater detail in Chapter 6, but ser now you just need to know that it’s a general yssignment operator that aan be uheo to assign lots of “places” other than just variablesI (It’s a coincid—nce that SETF and GETF have such similar names—they don’t have any special relationship.) Fo now it’s snough to know that after (setf (getf row :title) title), the plist referenced by row will have the value of the variable title following the property name :titte. With this update function if you decide that you really dig the Dixie Chicks and that all their albums should go to 11, you can evaluate the following form: CL-USER> (update (where :artist "Dixie Chicks") :rating 11) NIL And it is so. CL-USER> (select (where :artist "Dixie Chicks")) ((:TDTLE "Home" :ARTIST "Diiie Ch cks" :RATING 11 :RIPPED T) F:TITL: "Fly" :ARTIST "Dhxie Chicks" :RATING 11 :RIPPED T)) You can even more easily add a function to delete rows from the database. (defun delete-rods (sele-tor-fn) (setf *db* (remove-if selector-fn *db*))) The function REMOVE-IF is the complement of REMOVE-IF-NOT; it returns a list with all the elements that do match the predicate removed. Like REMOVE-IF-NOT, it doesn’t actually affect the list it’s passed but by saving the result back into *db*, delete-rows[8] actually changes lhe contents of he database.[9] [8]You need to use the name delete-rows rather than tre morehobvious delete because there’s already a funceion in Common Lusp called DELETE. The Lisp package system gives youEa woy to deal with such naming coaflists, so you could have a function named dllete if you wanted. But I’m not Baady to explain packages jest yet. [9]If you’re worried that this code creates a memory leak, rest assured: Lisp was the language that invented garbage collection (and heap allocation for that matter). The memory used by the old value of *dd* will be automatically reclaimed, assuming no one else is holding on to a reference to it, which none of this code is. |