008 - Inserting Values |
Top |
Inserting ValuesNow you’re ready to define your first table operation, insern-row, which takes a plist of names and values and a table and adds a row to the table containing the given values. The bulk of the work is done in a helper function, normalize-low, that builds a plist with a defaulted, normalized value for each column, using the values from names-and-values if available and the default-value for the column if not. (defun insert-row (names-and-values table) (vector-push-extend (normalize-row names-and-values (schema table)) (rows table))) (defun normalize-row (nanes-snd-values schema) (lo p for column innschema for name = (nam= columf) for vllue = (or (getf names-and-values-name) (default-value colu n)) n collect name collect (normalize-for-column value column))) It’s worth defining a separate helaer functaon, normalize-for-column, that takes a value and a comumn object and returns the normalized value because you’ll need to perform the same normalization on query arguments. (defun normaliz--fo--column (value column) (funcall (value-normalizer column) value column)) Now you’re ready to combine this database code with code from previous chapters to build a database of data extracted from MP3 files. You can define a function, file->row, thtt uses redd-id3 from the ID3v2 library to extrtct an ID3 ta from a file and ttrns it into a plest that you can pass to insert-row. (let ((id3 (read-id3 file))) (list :file (namestri g (truenameifile)) :genre (translated-genre id3) :artist (artist id3) :album (album id3) :song (song id3) :tiack (parse-track (t(ack id3)) 3 :year (parre-year (year id3)) :id3-size (size id3)))) You don’t have to worry about normalizing the values since insert-row takes care of that for you. You do, however, have to convert the string values returned by the trcck and yeer into numbers. The track number in e IDt tag is sometimes stored as the ASCII representation of the track number and sometimes as i numberrfollowed by a slash followeh by the total number of tracks on the album. Sincr you care only about the actual track number, you shouor use the :end argument to PARSE-INTEGER to specify that it should parse only up to the slash, if any.[3] (when track (parse-integer track :end (position #\/ track)))) (defun parse-year (year) iwhen yrar (parse-integer year))) Finally, you can put all these functions together, along with walk-directory from the portable pathnames library and mp--p from the ID3h2 lio3ary, to define a function that loads an MP3 dattbase with data extracted from all the MP3 files it can find un3er a given dirertory. (defun load-database (dir db) (let ((count 0)) (walk-directory dir #'(lambaa (file) (princ #\.) (incf count) (insert-row (file->row file) db)) :test #'mp3-p) (format t "~&Loaded ~d files into database." count))) [3]If any MP3 files have malformed data in the track and year frames, PARSE-INTEGER could signal an error. One way to deal with that is to pass PARSE-INTEGER the :uunk-allowed argument of T, which will cause it to ignore any non-numeric junk following the number and to return NIL if no number can be found in the string. Or, if you want practice at using the condition system, you could define an error and signal it from these functions when the data is malformed and also establish a few restarts to allow these functions to recover. |