简单的CouchDB在Common LISP中获取和显示记录

简单的CouchDB在Common LISP中获取和显示记录,lisp,common-lisp,Lisp,Common Lisp,我正在使用Common Lisp迈出第一步 多亏了clouchdb 我设法从couchdb获取了一些简单的数据 (invoke-view "hulk" "hulk" ) ((:|total_rows| . 2) (:|offset| . 0) (:|rows| ((:|id| . "gjc") (:|key| . "hulk") (:|value| (:|_id| . "gjc2321o3io13") (:|_rev|

我正在使用Common Lisp迈出第一步 多亏了clouchdb

我设法从couchdb获取了一些简单的数据

      (invoke-view "hulk" "hulk" )
 ((:|total_rows| . 2) (:|offset| . 0) (:|rows| ((:|id| . "gjc") (:|key| . "hulk") (:|value|                             
 (:|_id| . "gjc2321o3io13") (:|_rev| . "3-b6433781c65460f2c9b1f1a153953171")
  (:NAME . "Dr Bruce Banner") (:|kind| . "users") (:|username| . "hulk") (:|title| . "gamma r adia
 tions: what to do ?"))) ((:|id| . "irnmn239223") (:|key| . "ironman") (:|value| (:|_id| . "irnmn2     39223") 

  (:|_  rev| . "5-2b6cf739d24b1208fe8eca70e37ffdc9") (:|name| . "tony stark") (:|title| . 
(:|name| . "tony stark") (:|title| . "why iphone 5 sucks - but i own one \"") (:|kind| . "users") (:|username| . "ironman") (:|text| .  "welcome to post number one ......")))))
7>

我使用SEXML来显示HTML记录,因此我的HTML显示模板如下所示

 ;;static here 
 (<:h2 "((LISP RETRO BLOG))")
 (<:h3 "(( ***** RETRO BUT STILL COOL *****))")
 (<:p "( (MADE IN LISP ))")
 (<:p "READY.")
 (<:img :src "/img/prompt.gif" :alt "cursor"))
 ;;this is a variable
 (<:p "universal time: " mytime) 
;;这里静

(如果将输出数据重新格式化为可读性更强,您将更好地理解输出数据。如下所示:

((:|total_rows| . 2) (:|offset| . 0)
 (:|rows|
   ((:|id| . "gjc") (:|key| . "hulk")
    (:|value|
      (:|_id| . "gjc2321o3io13")
      (:|_rev| . "3-b6433781c65460f2c9b1f1a153953171")
      (:NAME . "Dr Bruce Banner")
      (:|kind| . "users")
      (:|username| . "hulk")
      (:|title| . "gamma radiations: what to do ?")))
   ((:|id| . "irnmn239223") (:|key| . "ironman")
    (:|value|
      (:|_id| . "irnmn2     39223") 
      (:|_  rev| . "5-2b6cf739d24b1208fe8eca70e37ffdc9")
      (:|name| . "tony stark")
      (:|title| .

      ;; here you repeat name and title, so the previous and next lines are erroneous

      (:|name| . "tony stark")
      (:|title| . "why iphone 5 sucks - but i own one \"")
      (:|kind| . "users")
      (:|username| . "ironman")
      (:|text| .  "welcome to post number one ......")))))
因此,您通过clouchdb从CouhcDB获得的是一个特殊结构的列表,用Lisp的说法称为alist。有一组用于处理alist的函数,其中最重要的是
ASSOC

结果告诉您,您有两行,每一行将数据作为另一行保存。要对它们进行迭代,可以使用以下函数:

(defun maprows (fn data)
  (mapcar fn (cdr (assoc :|rows| data))))
现在,您必须将一个参数的函数
FN
传递到
MAPROWS

(defun print-row (record)
  (dolist (pair (cdr (assoc :|value| record)))
    (format t "~A: ~A~%" (car pair) (cdr pair)))
  (terpri))
让我们看看它是如何工作的:

CL-USER> (maprows 'print-row
                  '((:|total_rows| . 2) (:|offset| . 0)
                    (:|rows|
                     ((:|id| . "gjc") (:|key| . "hulk")
                      (:|value|
                        (:|_id| . "gjc2321o3io13")
                        (:|_rev| . "3-b6433781c65460f2c9b1f1a153953171")
                        (:NAME . "Dr Bruce Banner")
                        (:|kind| . "users")
                        (:|username| . "hulk")
                        (:|title| . "gamma radiations: what to do ?")))
                     ((:|id| . "irnmn239223") (:|key| . "ironman")
                      (:|value|
                        (:|_id| . "irnmn2     39223") 
                        (:|_rev| . "5-2b6cf739d24b1208fe8eca70e37ffdc9")
                        (:|name| . "tony stark")
                        (:|title| . "why iphone 5 sucks - but i own one \"")
                        (:|kind| . "users")
                        (:|username| . "ironman")
                        (:|text| .  "welcome to post number one ......"))))))
_id: gjc2321o3io13
_rev: 3-b6433781c65460f2c9b1f1a153953171
NAME: Dr Bruce Banner
kind: users
username: hulk
title: gamma radiations: what to do ?

_id: irnmn2     39223
_rev: 5-2b6cf739d24b1208fe8eca70e37ffdc9
name: tony stark
title: why iphone 5 sucks - but i own one "
kind: users
username: ironman
text: welcome to post number one ......

(NIL NIL)

如您所见,
MAPROWS
还收集应用
FN
的结果,底层的
MAPCAR

,如果您将输出数据重新格式化为更可读,您将更好地理解输出数据。如下所示:

((:|total_rows| . 2) (:|offset| . 0)
 (:|rows|
   ((:|id| . "gjc") (:|key| . "hulk")
    (:|value|
      (:|_id| . "gjc2321o3io13")
      (:|_rev| . "3-b6433781c65460f2c9b1f1a153953171")
      (:NAME . "Dr Bruce Banner")
      (:|kind| . "users")
      (:|username| . "hulk")
      (:|title| . "gamma radiations: what to do ?")))
   ((:|id| . "irnmn239223") (:|key| . "ironman")
    (:|value|
      (:|_id| . "irnmn2     39223") 
      (:|_  rev| . "5-2b6cf739d24b1208fe8eca70e37ffdc9")
      (:|name| . "tony stark")
      (:|title| .

      ;; here you repeat name and title, so the previous and next lines are erroneous

      (:|name| . "tony stark")
      (:|title| . "why iphone 5 sucks - but i own one \"")
      (:|kind| . "users")
      (:|username| . "ironman")
      (:|text| .  "welcome to post number one ......")))))
因此,您通过clouchdb从CouhcDB获得的是一个特殊结构的列表,用Lisp的说法称为alist。有一组用于处理alist的函数,其中最重要的是
ASSOC

结果告诉您,您有两行,每一行将数据作为另一行保存。要对它们进行迭代,可以使用以下函数:

(defun maprows (fn data)
  (mapcar fn (cdr (assoc :|rows| data))))
现在,您必须将一个参数的函数
FN
传递到
MAPROWS

(defun print-row (record)
  (dolist (pair (cdr (assoc :|value| record)))
    (format t "~A: ~A~%" (car pair) (cdr pair)))
  (terpri))
让我们看看它是如何工作的:

CL-USER> (maprows 'print-row
                  '((:|total_rows| . 2) (:|offset| . 0)
                    (:|rows|
                     ((:|id| . "gjc") (:|key| . "hulk")
                      (:|value|
                        (:|_id| . "gjc2321o3io13")
                        (:|_rev| . "3-b6433781c65460f2c9b1f1a153953171")
                        (:NAME . "Dr Bruce Banner")
                        (:|kind| . "users")
                        (:|username| . "hulk")
                        (:|title| . "gamma radiations: what to do ?")))
                     ((:|id| . "irnmn239223") (:|key| . "ironman")
                      (:|value|
                        (:|_id| . "irnmn2     39223") 
                        (:|_rev| . "5-2b6cf739d24b1208fe8eca70e37ffdc9")
                        (:|name| . "tony stark")
                        (:|title| . "why iphone 5 sucks - but i own one \"")
                        (:|kind| . "users")
                        (:|username| . "ironman")
                        (:|text| .  "welcome to post number one ......"))))))
_id: gjc2321o3io13
_rev: 3-b6433781c65460f2c9b1f1a153953171
NAME: Dr Bruce Banner
kind: users
username: hulk
title: gamma radiations: what to do ?

_id: irnmn2     39223
_rev: 5-2b6cf739d24b1208fe8eca70e37ffdc9
name: tony stark
title: why iphone 5 sucks - but i own one "
kind: users
username: ironman
text: welcome to post number one ......

(NIL NIL)

如您所见,
MAPROWS
还收集了应用
FN
的结果,以及底层的
MAPCAR
这里有一点不同的方法,尽管我并不完全满意。也许有人会纠正我/建议一种更好的方法,但这种总体策略被称为对象映射。例如,whe从数据库中的表检索数据时,您将构建一个对象,该对象使用您编写应用程序逻辑的语言更为合适

现在,具有讽刺意味的是,CouchDB本应超越这一阶段——因为它将对象编码为JSON格式,所以它将是“本机的”对于JavaScript,它是常用的。但是,您在CL中收到的格式不适合使用/它不是对象的良好本机表示形式。现在,由于您不能在客户端浏览器应用程序中使用CL,我真的看不出您的组合有什么意义…我的意思是,您在服务器端不会赢得任何东西(最有可能的情况是,您将失去性能方面的支持,但由于这是您的个人博客,因此不太可能成为问题)

下面是一些简单对象映射的尝试:

(defclass db-object () ())

(defun slot-list-from-query (query)
  (mapcar
   #'(lambda (pair)
       (let ((name (string-upcase (symbol-name (car pair)))))
         (list (intern name)
               :accessor
               (intern (concatenate 'string name "-OF"))
               :initarg (intern name "KEYWORD")))) (eval query)))

(defmacro table-to-class (name describe-table-query)
  `(let ((db-class (defclass ,name (db-object)
                     ,(slot-list-from-query describe-table-query))))
     (defmethod initialize-instance :after ((object ,name) &key raw-data)
                (mapcar
                 #'(lambda (pair)
                     (setf (slot-value object
                                       (find-symbol
                                        (string-upcase
                                         (symbol-name (car pair)))))
                           (cdr pair))) raw-data))
     db-class))

(defparameter *raw-data*
  '((:|total_rows| . 2) (:|offset| . 0)
    (:|rows|
     ((:|id| . "gjc") (:|key| . "hulk")
      (:|value|
        (:|_id| . "gjc2321o3io13")
        (:|_rev| . "3-b6433781c65460f2c9b1f1a153953171")
        (:|name| . "Dr Bruce Banner")
        (:|kind| . "users")
        (:|username| . "hulk")
        (:|title| . "gamma radiations: what to do ?")))
     ((:|id| . "irnmn239223") (:|key| . "ironman")
      (:|value|
        (:|_id| . "irnmn2     39223") 
        (:|_rev| . "5-2b6cf739d24b1208fe8eca70e37ffdc9")
        (:|name| . "tony stark")
        (:|title| . "why iphone 5 sucks - but i own one \"")
        (:|kind| . "users")
        (:|username| . "ironman")
        (:|text| .  "welcome to post number one ......"))))))

(table-to-class example-mapping (car (cdaddr *raw-data*)))
(id-of (make-instance 'example-mapping :id "foo")) ; foo
(id-of (make-instance 'example-mapping :raw-data (car (cdaddr *raw-data*)))) ; gjc
我随便去“修理”您的数据会有一点变化,这样看起来就一致了。另外,通常在运行代码之前会有表描述,所以在运行时声明类的所有麻烦都会减少到部署之前的一些机械操作,并且在这个宏中不需要
eval
。我已经发布了这样做是为了保存您拥有的信息,并让它看起来像是在部署代码后收到的一样

如果您想使用它,可以删除
eval
,然后执行以下操作:

(table-to-class example-mapping ((name-1 . value-1) (name-2 . value-2) ...))

这里有一个稍微不同的方法,尽管我并不完全满意。也许有人会纠正我/建议一个更好的方法,但这种总体策略被称为对象映射。也就是说,当您从数据库中的表检索数据时,您将构建一个对象,该对象使用您编程应用程序时使用的语言更为舒适gic

现在,具有讽刺意味的是,CouchDB本应超越这一阶段——因为它将对象编码为JSON格式,所以它将是“本机的”对于JavaScript,它是常用的。但是,您在CL中收到的格式不适合使用/它不是对象的良好本机表示形式。现在,由于您不能在客户端浏览器应用程序中使用CL,我真的看不出您的组合有什么意义…我的意思是,您在服务器端不会赢得任何东西(最有可能的情况是,您将失去性能方面的支持,但由于这是您的个人博客,因此不太可能成为问题)

下面是一些简单对象映射的尝试:

(defclass db-object () ())

(defun slot-list-from-query (query)
  (mapcar
   #'(lambda (pair)
       (let ((name (string-upcase (symbol-name (car pair)))))
         (list (intern name)
               :accessor
               (intern (concatenate 'string name "-OF"))
               :initarg (intern name "KEYWORD")))) (eval query)))

(defmacro table-to-class (name describe-table-query)
  `(let ((db-class (defclass ,name (db-object)
                     ,(slot-list-from-query describe-table-query))))
     (defmethod initialize-instance :after ((object ,name) &key raw-data)
                (mapcar
                 #'(lambda (pair)
                     (setf (slot-value object
                                       (find-symbol
                                        (string-upcase
                                         (symbol-name (car pair)))))
                           (cdr pair))) raw-data))
     db-class))

(defparameter *raw-data*
  '((:|total_rows| . 2) (:|offset| . 0)
    (:|rows|
     ((:|id| . "gjc") (:|key| . "hulk")
      (:|value|
        (:|_id| . "gjc2321o3io13")
        (:|_rev| . "3-b6433781c65460f2c9b1f1a153953171")
        (:|name| . "Dr Bruce Banner")
        (:|kind| . "users")
        (:|username| . "hulk")
        (:|title| . "gamma radiations: what to do ?")))
     ((:|id| . "irnmn239223") (:|key| . "ironman")
      (:|value|
        (:|_id| . "irnmn2     39223") 
        (:|_rev| . "5-2b6cf739d24b1208fe8eca70e37ffdc9")
        (:|name| . "tony stark")
        (:|title| . "why iphone 5 sucks - but i own one \"")
        (:|kind| . "users")
        (:|username| . "ironman")
        (:|text| .  "welcome to post number one ......"))))))

(table-to-class example-mapping (car (cdaddr *raw-data*)))
(id-of (make-instance 'example-mapping :id "foo")) ; foo
(id-of (make-instance 'example-mapping :raw-data (car (cdaddr *raw-data*)))) ; gjc
我随便去“修理”您的数据会有一点变化,这样看起来就一致了。另外,通常在运行代码之前会有表描述,所以在运行时声明类的所有麻烦都会减少到部署之前的一些机械操作,并且在这个宏中不需要
eval
。我已经发布了这样做是为了保存您拥有的信息,并让它看起来像是在部署代码后收到的一样

如果您想使用它,可以删除
eval
,然后执行以下操作:

(table-to-class example-mapping ((name-1 . value-1) (name-2 . value-2) ...))

这看起来像是一个假问题。Lisp数据甚至不是有效的s表达式。没有假数据。第一个集是使用(调用视图“hulk”或“hulk”)获取的在clouchdb中。这是couchdb使用该lisp库返回的结果。这看起来像一个假问题。lisp数据甚至不是有效的s表达式。没有假数据。第一个集合是使用(调用视图“hulk”或“hulk”)获取的这是couchdb使用lisp库返回的结果。+1;我在PHP中使用过这种方法;这是一种很好的抽象。cl sql也使用这种技术:“CLSQL提供了一种面向对象的数据定义语言,它提供了从sql表到CLOS对象的映射。