redis 之 clisp 接口庫

修正 redis-lrange bug. 2020.02.22

;;;; redis-lib.pkg
(in-package :cl-user)
(defpackage :whj-redis
	(:nicknames :redis :redis-lib)
	(:use
		:common-lisp
		:ext
		:socket)
	(:export
	
		:*redis-conn*
		
		:redis-connect
		:redis-close
		
		:with-redis
		
		:redis-send ;for debug
		:redis-command
		
		:redis-ttl
		:redis-exists
		:redis-expire
		:redis-keys
		:redis-type
		
		:redis-sort
		
		:redis-set
		:redis-get
		:redis-set-encode
		:redis-get-encode
		
		:redis-lpush
		:redis-lpop
		:redis-rpush
		:redis-rpop
		:redis-lrange
		:redis-llen
		:redis-lindex
		:redis-lset
		:redis-linsert
		:redis-rpoplpush
		:redis-ltrim
		:redis-lrem

		:redis-smembers
		:redis-sismember
		:redis-sadd
		:redis-srem
		:redis-scard
		:redis-sdiff
		:redis-sinter
		:redis-sunion
		:redis-sdiffstore
		:redis-sinterstore
		:redis-sunionstore
		:redis-srandmember
		:redis-spop
		
		:redis-zrange
		:redis-zrevrange
		:redis-zadd
		:redis-zscore
		:redis-zrangebyscore
		:redis-zrevrangebyscore
		:redis-zincrby
		:redis-zcard
		:redis-zrem
		:redis-zcount
		:redis-zremrangebyrank
		:redis-zremrangebyscore
		:redis-zrank
		:redis-zrevrank
		:redis-zinterstore
		:redis-zunionstore

		:redis-hset
		:redis-hget
		:redis-hgetall
		:redis-hkeys
		:redis-hlen))
;;;; redis-lib.lsp
(in-package :redis-lib)
;------------------------------------------------------------------------------------------
(defvar *redis-conn* nil)
(defvar *redis-charset* charset:utf-8)
(defvar *redis-cmd-end-flag* (coerce (list #\return #\newline) 'string))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;實現統一請求協議
;;;; 助手宏
(defmacro redis-read-byte ()
	'(read-byte *redis-conn* nil))
(defmacro redis-send-bytes (sequence)
	`(write-byte-sequence ,sequence *redis-conn*))
(defmacro redis-send-string (string)
	`(redis-send-bytes (convert-string-to-bytes ,string *redis-charset*)))
(defmacro redis-cmd-end ()
	'(redis-send-string *redis-cmd-end-flag*))

(defmacro redis-command (&rest args)
	`(progn
		(redis-send-cmd-length ,(length args))
		(redis-send-item-list ,@args)
		(redis-read-to-end)))
		
(defun to-string (o)
	(format nil "~a" o))

(defun redis-mode (mode)
	(setf (stream-element-type *redis-conn*) (if (eq mode :char) 'character  '(unsigned-byte 8))))

(defun redis-send-cmd-length (n)
	(redis-send-string (format nil "*~a" n))
	(redis-cmd-end))
(defun redis-send-item-length (n)
	(redis-send-string (format nil "$~a" n))
	(redis-cmd-end))

;${len}\r\n{str-item-byte-list}\r\n
(defun redis-send-item (string-item)
	(let ((v (convert-string-to-bytes string-item *redis-charset*)))
		(redis-send-item-length (length v))
		(redis-send-bytes v)
		(redis-cmd-end)))
		
(defun redis-send-item-list (&rest args)
	(dolist (item args)
		(redis-send-item (to-string item))))

(defun redis-read-line ()
	(let ((lst (loop for x = (redis-read-byte) while (not (= x 13)) collect x)))
		 (redis-read-byte)
		 (convert-string-from-bytes (coerce lst 'vector) *redis-charset*)))

(defun redis-read-integer ()
	(let ((line (redis-read-line)))
        (when (string/= line "")
            (parse-integer line 
                        :start (if (member (elt line 0) (list #\* #\$)) 1 0)))))
	
(defun redis-read-nbyte (n)
	(if (<= n 0)
        ""
		(let ((lst (loop for i from 1 to n collect (redis-read-byte))))
			(redis-read-byte)
			(redis-read-byte)
			(convert-string-from-bytes (coerce lst 'vector) *redis-charset*))))
		 
(defun redis-read-to-end ()
	(let ((char-flag (code-char (redis-read-byte))))
		(cond
			((char= char-flag #\$);單字符串
				(let ((n (redis-read-integer)))
					(values (redis-read-nbyte n) n)))
			((char= char-flag #\*)
				(let ((n (redis-read-integer)))
					(values 
                            (let ((lst nil))
                                 (do ((i 0))
                                     ((>= i n))
                                     (let ((xb (redis-read-integer)))
                                        (when xb 
                                            (incf i)
                                            (push (redis-read-nbyte xb) lst))))
                                 (nreverse lst))
						n)))
			((char= char-flag #\:);整數回覆
				(parse-integer (redis-read-line)))
			((char= char-flag #\+);狀態
				(redis-read-line))
			((char= char-flag #\-);錯誤
				(redis-read-line)))))
			
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;------------------------------------------------------------------------------------------
;連接redis server	
(defun redis-connect (&optional (ip "127.0.0.1") (port 6379))
	(setf *redis-conn* (socket-connect port ip :element-type '(unsigned-byte 8) :external-format :dos)))
;關閉redis socket 連接
(defun redis-close nil
	(when *redis-conn*
		(close *redis-conn*)))
;打開連接,執行命令後關閉連接	
(defmacro with-redis ((ip port) &body body)
	`(let ((*redis-conn* (redis-connect ,ip ,port)))
		(unwind-protect
			(progn
				,@body)
			(redis-close))))
;------------------------------------------------------------------------------------------
;redis ttl
(defun redis-ttl (key)
	(redis-command "ttl" key))
;redis exists
(defun redis-exists (&rest key-list)
	(redis-send-cmd-length (1+ (length key-list)))
	(apply #'redis-send-item-list "exists" key-list)
	(redis-read-to-end))
;redis expire
(defun redis-expire (key seconds)
	(redis-command "expire" key seconds))
;redis type
(defun redis-type (key)
	(redis-command "type" key))
;redis keys
(defun redis-keys (&optional (pattern "*"))
	(redis-command "keys" pattern))
;redis get
(defun redis-get (key)
	(redis-command "get" key))
;redis set
(defun redis-set (key value &optional (expire-seconds -1))
	(let ((n (+ 3
				(if (>= expire-seconds 0)
					2
					0))))
		(redis-send-cmd-length n)
		(redis-send-item-list "set" key value)
		(when (>= expire-seconds 0)
			(redis-send-item-list "ex" expire-seconds)))
	(redis-read-to-end))
;redis sort [BY pattern] [LIMIT offset count] [GET pattern [GET pattern]] ASC|DESC				
(defun redis-sort (key &key (BY NIL) (LIMIT NIL) (GET NIL) (MODE 'ASC))
	(let ((n (+ 2
				(if by 2 0)
				(if limit 3 0)
				(if get 2 0)
				(if mode 1 0))))
		(redis-send-cmd-length n)
		(redis-send-item-list "sort" key)
		(when BY
			(redis-send-item-list "BY" BY))
		(when LIMIT
			(redis-send-item-list 
				"LIMIT" 
				(car limit)
				(cadr limit)))
		(when GET
			(redis-send-item-list 
				"GET" 
				GET))
		(when MODE
			(redis-send-item (to-string MODE)))
	(redis-read-to-end)))
;------------------------------------------------------------------------------------------
;redis lpush
;返回 添加後的新長度
(defun redis-lpush (key &rest value-list)
	(redis-send-cmd-length (+ 2 (length value-list)))
	(apply #'redis-send-item-list "lpush" key value-list)
	(redis-read-to-end))
;redis lpop
(defun redis-lpop (key)
	(redis-command "lpop" key))

;redis rpush
(defun redis-rpush (key &rest value-list)
	(redis-send-cmd-length (+ 2 (length value-list)))
	(apply #'redis-send-item-list "rpush" key value-list)
	(redis-read-to-end))
;redis rpop
(defun redis-rpop (key)
	(redis-command "rpop" key))
;redis lrange	
(defun redis-lrange (key &optional (start 0) (end -1))
	(redis-command "lrange" key start end))
;redis-llen
(defun redis-llen (key)
	(redis-command "llen" key))
;redis lindex
(defun redis-lindex (key index)
	(redis-command "lindex" key index))
;redis lset	
(defun redis-lset (key index value)
	(redis-command "lset" key index value))
;redis linsert
(defun redis-linsert (key value pivot-value &optional (after-p t))
	(redis-command
		"linsert" 
		key 
		(if after-p "after" "before") 
		pivot-value 
		value))
;redis rpoplpush
(defun redis-rpoplpush (src dest)
	(redis-command "rpoplpush" src dest))
;redis ltrim ;僅保留 start end 之間的數據
(defun redis-ltrim (key start end)
	(redis-command "ltrim" key start end))
;redis lrem
(defun redis-lrem (key count value)
	(redis-command "lrem" key count value))
;------------------------------------------------------------------------------------------	
;redis semembers	
(defun redis-smembers (key)
	(redis-command "smembers" key))
;redis sismember
(defun redis-sismember (key member)
	(redis-command "sismember" key member))
;redis sadd
(defun redis-sadd (key &rest member-list)
	(redis-send-cmd-length (+ 2 (length member-list)))
	(apply #'redis-send-item-list "sadd" key member-list)
	(redis-read-to-end))
;redis srem
(defun redis-srem (key &rest member-list)
	(redis-send-cmd-length (+ 2 (length member-list)))
	(apply #'redis-send-item-list "srem" key member-list)
	(redis-read-to-end))
;redis scard
(defun redis-scard (key)
	(redis-command "scard" key))
;redis sdiff
(defun redis-sdiff (&rest key-list)
	(redis-send-cmd-length (1+ (length key-list)))
	(apply #'redis-send-item-list "sdiff" key-list)
	(redis-read-to-end))
;redis sinter
(defun redis-sinter (&rest key-list)
	(redis-send-cmd-length (1+ (length key-list)))
	(apply #'redis-send-item-list "sinter" key-list)
	(redis-read-to-end))
;redis sunion
(defun redis-sunion (&rest key-list)
	(redis-send-cmd-length (1+ (length key-list)))
	(apply #'redis-send-item-list "sunion" key-list)
	(redis-read-to-end))
;redis sdiffstore
(defun redis-sdiffstore (dst-key &rest key-list)
	(redis-send-cmd-length (+ 2 (length key-list)))
	(apply #'redis-send-item-list "sdiffstore" dst-key key-list)
	(redis-read-to-end))
;redis sinterstore
(defun redis-sinterstore (dst-key &rest key-list)
	(redis-send-cmd-length (+ 2 (length key-list)))
	(apply #'redis-send-item-list "sinterstore" dst-key key-list)
	(redis-read-to-end))
;redis sunionstore
(defun redis-sunionstore (dst-key &rest key-list)
	(redis-send-cmd-length (+ 2 (length key-list)))
	(apply #'redis-send-item-list "sunionstore" dst-key key-list)
	(redis-read-to-end))
;redis srandmember
(defun redis-srandmember (key count)
	(redis-command "srandmember" key count))
;redis spop
(defun redis-spop (key &optional (count 1))
	(redis-command "spop" key count))
;------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------
;redis zadd	
(defun redis-zadd (key &rest score-value-list)
	(redis-send-cmd-length (+ 2 (length score-value-list)))
	(apply #'redis-send-item-list "zadd" key score-value-list)
	(redis-read-to-end))
;redis zrange	
(defun redis-zrange (key &optional (withscores-p nil) (start 0) (end -1) )
	(redis-send-cmd-length (+ 4 (if withscores-p 1 0)))
	(redis-send-item-list "zrange" key start end)
	(when withscores-p
		(redis-send-item "withscores"))
	(redis-read-to-end))
;redis zrevrange	
(defun redis-zrevrange (key &optional (withscores-p nil) (start 0) (end -1) )
	(redis-send-cmd-length (+ 4 (if withscores-p 1 0)))
	(redis-send-item-list "zrevrange" key start end)
	(when withscores-p
		(redis-send-item "withscores"))
	(redis-read-to-end))
;redis zscore
(defun redis-zscore (key member)
	(redis-command "zscore" key member))
;redis zrangebyscore key min max [withscores] [limit offset count]
;exap: (redis-zrangebyscore "z1" "(10" 20 t '(0 1))
(defun redis-zrangebyscore (key min max &optional (withscores nil) (limit '(0 -1)))
	(redis-send-cmd-length (+ 7 (if withscores 1 0)))
	(redis-send-item-list "zrangebyscore" key min max "limit" (car limit) (cadr limit))
	(when withscores
		(redis-send-item "withscores"))
	(redis-read-to-end))
;redis zrevrangebyscore key max min [withscores] [limit offset count]
(defun redis-zrevrangebyscore (key max min &optional (withscores nil) (limit '(0 -1)))
	(redis-send-cmd-length (+ 7 (if withscores 1 0)))
	(redis-send-item-list "zrevrangebyscore" key max min "limit" (car limit) (cadr limit))
	(when withscores
		(redis-send-item "withscores"))
	(redis-read-to-end))
	
;redis-zincrby
(defun redis-zincrby (key increment member)
	(redis-command "zincrby" key increment member))
;redis-zcard
(defun redis-zcard (key)
	(redis-command "zcard" key))
;redis zrem
(defun redis-zrem (key &rest member-list)
	(redis-send-cmd-length (+ 2 (length member-list)))
	(apply #'redis-send-item-list "zrem" key member-list)
	(redis-read-to-end))
;redis zcount
(defun redis-zcount (key min max)
	(redis-command "zcount" key min max))
	
;redis-zremrangebyrank
(defun redis-zremrangebyrank (key start stop)
	(redis-command "zremrangebyrank" key start stop))
;:redis-zremrangebyscore
(defun redis-zremrangebyscore (key min max)
	(redis-command "zremrangebyscore" key min max))
;:redis-zrank: zrank key member
(defun redis-zrank (key member)
	(redis-command "zrank" key member))
;:redis-zrevrank: zrevrank key member
(defun redis-zrevrank (key member)
	(redis-command "zrevrank" key member))
;redis-zinterstore: zinterstore numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE min|max|sum]
; exap: (redis-zinterstore "z-dst" '("z1" 1 "z2" 2) 'sum)
(defun redis-zinterstore (dst-key key-weight-list aggregate-type)
	(let ((kv-cnt (length key-weight-list)))
		(redis-send-cmd-length (+ 6 kv-cnt))
		(apply #'redis-send-item-list
								`("zinterstore" 
								,dst-key
								,(/ kv-cnt 2) 
								,@(loop for x in key-weight-list by #'cddr collect x) 
								"WEIGHTS"
								,@(loop for x on key-weight-list by #'cddr collect (second x))
								"AGGREGATE"
								,aggregate-type)))
		(redis-read-to-end))
;:redis-zunionstore: zunionstore numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE min|max|sum]
(defun redis-zunionstore (dst-key key-weight-list aggregate-type)
	(let ((kv-cnt (length key-weight-list)))
		(redis-send-cmd-length (+ 6 kv-cnt))
		(apply #'redis-send-item-list 
								`("zunionstore" 
								,dst-key
								,(/ kv-cnt 2)
								,@(loop for x in key-weight-list by #'cddr collect x) 
								"WEIGHTS"
								,@(loop for x on key-weight-list by #'cddr collect (second x))
								"AGGREGATE"
								,aggregate-type)))
		(redis-read-to-end))
	
;------------------------------------------------------------------------------------------
;hash API
;redis hset
(defun redis-hset (key field value)
	(redis-command "hset" key field value))
;redis hget
(defun redis-hget (key field)
	(redis-command "hget" key field))
	
;redis hgetall
(defun redis-hgetall (key)
	(redis-command "hgetall" key))
;redis hkeys
(defun redis-hkeys (key)
	(redis-command "hkeys" key))
;redis hlen
(defun redis-hlen (key)
	(redis-command "hlen" key))
;;;; redis-lib-test.lsp

(in-package :cl-user)

(defun reload nil
    (load "h:/lisptool/rediscli/redis-lib.pkg")
    (load "h:/lisptool/rediscli/redis-lib.lsp")
    (load "h:/lisptool/rediscli/redis-lib-test.lsp"))

(use-package :redis-lib)
(defparameter *ip* "127.0.0.1")
(defparameter *port* 6379)


;for test
(defun redis-init nil
	(redis-close)
    (redis-connect *ip* *port*))
	
(defun test-1 nil
	(dotimes (i 10000)
		(redis-set (format nil "key~4,'0d" i)
			(format nil "it is a test No.~a !" (1+ i)))))

	
(defun test-2 nil
	(with-redis (*ip* *port*)
		(print (redis-zrange "z1"))
		(print (redis-lrange "lst1"))))
		
(defun test-3 nil
	(with-redis (*ip* *port*)
		(loop for x = (redis-rpop "lst1")
			while x 
				do (print x))))
				
(defun test-4 nil
	(with-redis (*ip* *port*)
		(dotimes (i 100)
			(redis-lpush "lst1" "ok test1" 1 2 5 "end flag"))
		(loop for x = (redis-rpop "lst1")
			while x 
				do (print x))))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章