package ledis import ( "errors" "github.com/siddontang/ledisdb/store" "regexp" ) var errDataType = errors.New("error data type") var errMetaKey = errors.New("error meta key") //fif inclusive is true, scan range [cursor, inf) else (cursor, inf) func (db *DB) Scan(dataType DataType, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) { storeDataType, err := getDataStoreType(dataType) if err != nil { return nil, err } return db.scanGeneric(storeDataType, cursor, count, inclusive, match, false) } //if inclusive is true, revscan range (-inf, cursor] else (inf, cursor) func (db *DB) RevScan(dataType DataType, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) { storeDataType, err := getDataStoreType(dataType) if err != nil { return nil, err } return db.scanGeneric(storeDataType, cursor, count, inclusive, match, true) } func getDataStoreType(dataType DataType) (byte, error) { var storeDataType byte switch dataType { case KV: storeDataType = KVType case LIST: storeDataType = LMetaType case HASH: storeDataType = HSizeType case SET: storeDataType = SSizeType case ZSET: storeDataType = ZSizeType default: return 0, errDataType } return storeDataType, nil } func buildMatchRegexp(match string) (*regexp.Regexp, error) { var err error var r *regexp.Regexp = nil if len(match) > 0 { if r, err = regexp.Compile(match); err != nil { return nil, err } } return r, nil } func (db *DB) buildScanIterator(minKey []byte, maxKey []byte, inclusive bool, reverse bool) *store.RangeLimitIterator { tp := store.RangeOpen if !reverse { if inclusive { tp = store.RangeROpen } } else { if inclusive { tp = store.RangeLOpen } } var it *store.RangeLimitIterator if !reverse { it = db.bucket.RangeIterator(minKey, maxKey, tp) } else { it = db.bucket.RevRangeIterator(minKey, maxKey, tp) } return it } func (db *DB) buildScanKeyRange(storeDataType byte, key []byte, reverse bool) (minKey []byte, maxKey []byte, err error) { if !reverse { if minKey, err = db.encodeScanMinKey(storeDataType, key); err != nil { return } if maxKey, err = db.encodeScanMaxKey(storeDataType, nil); err != nil { return } } else { if minKey, err = db.encodeScanMinKey(storeDataType, nil); err != nil { return } if maxKey, err = db.encodeScanMaxKey(storeDataType, key); err != nil { return } } return } func checkScanCount(count int) int { if count <= 0 { count = defaultScanCount } return count } func (db *DB) scanGeneric(storeDataType byte, key []byte, count int, inclusive bool, match string, reverse bool) ([][]byte, error) { r, err := buildMatchRegexp(match) if err != nil { return nil, err } minKey, maxKey, err := db.buildScanKeyRange(storeDataType, key, reverse) if err != nil { return nil, err } count = checkScanCount(count) it := db.buildScanIterator(minKey, maxKey, inclusive, reverse) v := make([][]byte, 0, count) for i := 0; it.Valid() && i < count; it.Next() { if k, err := db.decodeScanKey(storeDataType, it.Key()); err != nil { continue } else if r != nil && !r.Match(k) { continue } else { v = append(v, k) i++ } } it.Close() return v, nil } func (db *DB) encodeScanMinKey(storeDataType byte, key []byte) ([]byte, error) { return db.encodeScanKey(storeDataType, key) } func (db *DB) encodeScanMaxKey(storeDataType byte, key []byte) ([]byte, error) { if len(key) > 0 { return db.encodeScanKey(storeDataType, key) } k, err := db.encodeScanKey(storeDataType, nil) if err != nil { return nil, err } k[len(k)-1] = storeDataType + 1 return k, nil } func (db *DB) encodeScanKey(storeDataType byte, key []byte) ([]byte, error) { switch storeDataType { case KVType: return db.encodeKVKey(key), nil case LMetaType: return db.lEncodeMetaKey(key), nil case HSizeType: return db.hEncodeSizeKey(key), nil case ZSizeType: return db.zEncodeSizeKey(key), nil case SSizeType: return db.sEncodeSizeKey(key), nil default: return nil, errDataType } } func (db *DB) decodeScanKey(storeDataType byte, ek []byte) (key []byte, err error) { switch storeDataType { case KVType: key, err = db.decodeKVKey(ek) case LMetaType: key, err = db.lDecodeMetaKey(ek) case HSizeType: key, err = db.hDecodeSizeKey(ek) case ZSizeType: key, err = db.zDecodeSizeKey(ek) case SSizeType: key, err = db.sDecodeSizeKey(ek) default: err = errDataType } return } // for specail data scan func (db *DB) buildDataScanKeyRange(storeDataType byte, key []byte, cursor []byte, reverse bool) (minKey []byte, maxKey []byte, err error) { if !reverse { if minKey, err = db.encodeDataScanMinKey(storeDataType, key, cursor); err != nil { return } if maxKey, err = db.encodeDataScanMaxKey(storeDataType, key, nil); err != nil { return } } else { if minKey, err = db.encodeDataScanMinKey(storeDataType, key, nil); err != nil { return } if maxKey, err = db.encodeDataScanMaxKey(storeDataType, key, cursor); err != nil { return } } return } func (db *DB) encodeDataScanMinKey(storeDataType byte, key []byte, cursor []byte) ([]byte, error) { return db.encodeDataScanKey(storeDataType, key, cursor) } func (db *DB) encodeDataScanMaxKey(storeDataType byte, key []byte, cursor []byte) ([]byte, error) { if len(cursor) > 0 { return db.encodeDataScanKey(storeDataType, key, cursor) } k, err := db.encodeDataScanKey(storeDataType, key, nil) if err != nil { return nil, err } // here, the last byte is the start seperator, set it to stop seperator k[len(k)-1] = k[len(k)-1] + 1 return k, nil } func (db *DB) encodeDataScanKey(storeDataType byte, key []byte, cursor []byte) ([]byte, error) { switch storeDataType { case HashType: return db.hEncodeHashKey(key, cursor), nil case ZSetType: return db.zEncodeSetKey(key, cursor), nil case SetType: return db.sEncodeSetKey(key, cursor), nil default: return nil, errDataType } } func (db *DB) buildDataScanIterator(storeDataType byte, key []byte, cursor []byte, count int, inclusive bool, reverse bool) (*store.RangeLimitIterator, error) { if err := checkKeySize(key); err != nil { return nil, err } minKey, maxKey, err := db.buildDataScanKeyRange(storeDataType, key, cursor, reverse) if err != nil { return nil, err } it := db.buildScanIterator(minKey, maxKey, inclusive, reverse) return it, nil } func (db *DB) hScanGeneric(key []byte, cursor []byte, count int, inclusive bool, match string, reverse bool) ([]FVPair, error) { count = checkScanCount(count) r, err := buildMatchRegexp(match) if err != nil { return nil, err } v := make([]FVPair, 0, count) it, err := db.buildDataScanIterator(HashType, key, cursor, count, inclusive, reverse) if err != nil { return nil, err } defer it.Close() for i := 0; it.Valid() && i < count; it.Next() { _, f, err := db.hDecodeHashKey(it.Key()) if err != nil { return nil, err } else if r != nil && !r.Match(f) { continue } v = append(v, FVPair{Field: f, Value: it.Value()}) i++ } return v, nil } func (db *DB) HScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]FVPair, error) { return db.hScanGeneric(key, cursor, count, inclusive, match, false) } func (db *DB) HRevScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]FVPair, error) { return db.hScanGeneric(key, cursor, count, inclusive, match, true) } func (db *DB) sScanGeneric(key []byte, cursor []byte, count int, inclusive bool, match string, reverse bool) ([][]byte, error) { count = checkScanCount(count) r, err := buildMatchRegexp(match) if err != nil { return nil, err } v := make([][]byte, 0, count) it, err := db.buildDataScanIterator(SetType, key, cursor, count, inclusive, reverse) if err != nil { return nil, err } defer it.Close() for i := 0; it.Valid() && i < count; it.Next() { _, m, err := db.sDecodeSetKey(it.Key()) if err != nil { return nil, err } else if r != nil && !r.Match(m) { continue } v = append(v, m) i++ } return v, nil } func (db *DB) SScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) { return db.sScanGeneric(key, cursor, count, inclusive, match, false) } func (db *DB) SRevScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) { return db.sScanGeneric(key, cursor, count, inclusive, match, true) } func (db *DB) zScanGeneric(key []byte, cursor []byte, count int, inclusive bool, match string, reverse bool) ([]ScorePair, error) { count = checkScanCount(count) r, err := buildMatchRegexp(match) if err != nil { return nil, err } v := make([]ScorePair, 0, count) it, err := db.buildDataScanIterator(ZSetType, key, cursor, count, inclusive, reverse) if err != nil { return nil, err } defer it.Close() for i := 0; it.Valid() && i < count; it.Next() { _, m, err := db.zDecodeSetKey(it.Key()) if err != nil { return nil, err } else if r != nil && !r.Match(m) { continue } score, err := Int64(it.Value(), nil) if err != nil { return nil, err } v = append(v, ScorePair{Score: score, Member: m}) i++ } return v, nil } func (db *DB) ZScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]ScorePair, error) { return db.zScanGeneric(key, cursor, count, inclusive, match, false) } func (db *DB) ZRevScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]ScorePair, error) { return db.zScanGeneric(key, cursor, count, inclusive, match, true) }