github.com/nutsdb/nutsdb@v1.0.4/tx_list.go (about) 1 // Copyright 2019 The nutsdb Author. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nutsdb 16 17 import ( 18 "bytes" 19 "sort" 20 "strings" 21 "time" 22 23 "github.com/pkg/errors" 24 "github.com/xujiajun/utils/strconv2" 25 ) 26 27 var bucketKeySeqMap map[string]*HeadTailSeq 28 29 // ErrSeparatorForListKey returns when list key contains the SeparatorForListKey. 30 var ErrSeparatorForListKey = errors.Errorf("contain separator (%s) for List key", SeparatorForListKey) 31 32 // SeparatorForListKey represents separator for listKey 33 const SeparatorForListKey = "|" 34 35 // RPop removes and returns the last element of the list stored in the bucket at given bucket and key. 36 func (tx *Tx) RPop(bucket string, key []byte) (item []byte, err error) { 37 item, err = tx.RPeek(bucket, key) 38 if err != nil { 39 return 40 } 41 42 return item, tx.push(bucket, key, DataRPopFlag, item) 43 } 44 45 // RPeek returns the last element of the list stored in the bucket at given bucket and key. 46 func (tx *Tx) RPeek(bucket string, key []byte) ([]byte, error) { 47 if err := tx.checkTxIsClosed(); err != nil { 48 return nil, err 49 } 50 51 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 52 if err != nil { 53 return nil, err 54 } 55 var ( 56 bucketId = b.Id 57 l *List 58 exist bool 59 ) 60 61 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 62 return nil, ErrBucket 63 } 64 65 if tx.CheckExpire(bucket, key) { 66 return nil, ErrListNotFound 67 } 68 69 item, err := l.RPeek(string(key)) 70 if err != nil { 71 return nil, err 72 } 73 74 v, err := tx.db.getValueByRecord(item.record) 75 if err != nil { 76 return nil, err 77 } 78 79 return v, nil 80 } 81 82 // push sets values for list stored in the bucket at given bucket, key, flag and values. 83 func (tx *Tx) push(bucket string, key []byte, flag uint16, values ...[]byte) error { 84 for _, value := range values { 85 err := tx.put(bucket, key, value, Persistent, flag, uint64(time.Now().Unix()), DataStructureList) 86 if err != nil { 87 return err 88 } 89 } 90 91 return nil 92 } 93 94 func (tx *Tx) getListNewKey(bucket string, key []byte, isLeft bool) []byte { 95 if bucketKeySeqMap == nil { 96 bucketKeySeqMap = make(map[string]*HeadTailSeq) 97 } 98 99 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 100 if err != nil { 101 return nil 102 } 103 bucketId := b.Id 104 105 bucketKey := bucket + string(key) 106 if _, ok := bucketKeySeqMap[bucketKey]; !ok { 107 bucketKeySeqMap[bucketKey] = tx.getListHeadTailSeq(bucketId, string(key)) 108 } 109 110 seq := generateSeq(bucketKeySeqMap[bucketKey], isLeft) 111 return encodeListKey(key, seq) 112 } 113 114 // RPush inserts the values at the tail of the list stored in the bucket at given bucket,key and values. 115 func (tx *Tx) RPush(bucket string, key []byte, values ...[]byte) error { 116 if err := tx.isKeyValid(bucket, key); err != nil { 117 return err 118 } 119 120 if strings.Contains(string(key), SeparatorForListKey) { 121 return ErrSeparatorForListKey 122 } 123 124 for _, value := range values { 125 newKey := tx.getListNewKey(bucket, key, false) 126 err := tx.push(bucket, newKey, DataLPushFlag, value) 127 if err != nil { 128 return err 129 } 130 } 131 132 return nil 133 } 134 135 // LPush inserts the values at the head of the list stored in the bucket at given bucket,key and values. 136 func (tx *Tx) LPush(bucket string, key []byte, values ...[]byte) error { 137 if err := tx.isKeyValid(bucket, key); err != nil { 138 return err 139 } 140 141 if strings.Contains(string(key), SeparatorForListKey) { 142 return ErrSeparatorForListKey 143 } 144 145 for _, value := range values { 146 newKey := tx.getListNewKey(bucket, key, true) 147 err := tx.push(bucket, newKey, DataLPushFlag, value) 148 if err != nil { 149 return err 150 } 151 } 152 153 return nil 154 } 155 156 func (tx *Tx) isKeyValid(bucket string, key []byte) error { 157 if err := tx.checkTxIsClosed(); err != nil { 158 return err 159 } 160 161 if tx.CheckExpire(bucket, key) { 162 return ErrListNotFound 163 } 164 165 return nil 166 } 167 168 func (tx *Tx) LPushRaw(bucket string, key []byte, values ...[]byte) error { 169 if err := tx.isKeyValid(bucket, key); err != nil { 170 return err 171 } 172 173 return tx.push(bucket, key, DataLPushFlag, values...) 174 } 175 176 func (tx *Tx) RPushRaw(bucket string, key []byte, values ...[]byte) error { 177 if err := tx.isKeyValid(bucket, key); err != nil { 178 return err 179 } 180 181 return tx.push(bucket, key, DataRPushFlag, values...) 182 } 183 184 // LPop removes and returns the first element of the list stored in the bucket at given bucket and key. 185 func (tx *Tx) LPop(bucket string, key []byte) (item []byte, err error) { 186 item, err = tx.LPeek(bucket, key) 187 if err != nil { 188 return 189 } 190 191 return item, tx.push(bucket, key, DataLPopFlag, item) 192 } 193 194 // LPeek returns the first element of the list stored in the bucket at given bucket and key. 195 func (tx *Tx) LPeek(bucket string, key []byte) (item []byte, err error) { 196 if err := tx.checkTxIsClosed(); err != nil { 197 return nil, err 198 } 199 200 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 201 if err != nil { 202 return nil, err 203 } 204 var ( 205 bucketId = b.Id 206 l *List 207 exist bool 208 ) 209 210 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 211 return nil, ErrBucket 212 } 213 if tx.CheckExpire(bucket, key) { 214 return nil, ErrListNotFound 215 } 216 r, err := l.LPeek(string(key)) 217 if err != nil { 218 return nil, err 219 } 220 221 v, err := tx.db.getValueByRecord(r.record) 222 if err != nil { 223 return nil, err 224 } 225 226 return v, nil 227 } 228 229 // LSize returns the size of key in the bucket in the bucket at given bucket and key. 230 func (tx *Tx) LSize(bucket string, key []byte) (int, error) { 231 if err := tx.checkTxIsClosed(); err != nil { 232 return 0, err 233 } 234 235 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 236 if err != nil { 237 return 0, err 238 } 239 240 var ( 241 bucketId = b.Id 242 l *List 243 exist bool 244 ) 245 246 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 247 return 0, ErrBucket 248 } 249 if tx.CheckExpire(bucket, key) { 250 return 0, ErrListNotFound 251 } 252 return l.Size(string(key)) 253 } 254 255 // LRange returns the specified elements of the list stored in the bucket at given bucket,key, start and end. 256 // The offsets start and stop are zero-based indexes 0 being the first element of the list (the head of the list), 257 // 1 being the next element and so on. 258 // Start and end can also be negative numbers indicating offsets from the end of the list, 259 // where -1 is the last element of the list, -2 the penultimate element and so on. 260 func (tx *Tx) LRange(bucket string, key []byte, start, end int) ([][]byte, error) { 261 if err := tx.checkTxIsClosed(); err != nil { 262 return nil, err 263 } 264 265 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 266 if err != nil { 267 return nil, err 268 } 269 var ( 270 bucketId = b.Id 271 l *List 272 exist bool 273 ) 274 275 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 276 return nil, ErrBucket 277 } 278 if tx.CheckExpire(bucket, key) { 279 return nil, ErrListNotFound 280 } 281 282 records, err := l.LRange(string(key), start, end) 283 if err != nil { 284 return nil, err 285 } 286 287 values := make([][]byte, len(records)) 288 289 for i, r := range records { 290 value, err := tx.db.getValueByRecord(r) 291 if err != nil { 292 return nil, err 293 } 294 values[i] = value 295 } 296 297 return values, nil 298 } 299 300 // LRem removes the first count occurrences of elements equal to value from the list stored in the bucket at given bucket,key,count. 301 // The count argument influences the operation in the following ways: 302 // count > 0: Remove elements equal to value moving from head to tail. 303 // count < 0: Remove elements equal to value moving from tail to head. 304 // count = 0: Remove all elements equal to value. 305 func (tx *Tx) LRem(bucket string, key []byte, count int, value []byte) error { 306 var ( 307 buffer bytes.Buffer 308 size int 309 ) 310 size, err := tx.LSize(bucket, key) 311 if err != nil { 312 return err 313 } 314 315 if count > size || count < -size { 316 return ErrCount 317 } 318 319 buffer.Write([]byte(strconv2.IntToStr(count))) 320 buffer.Write([]byte(SeparatorForListKey)) 321 buffer.Write(value) 322 newValue := buffer.Bytes() 323 324 err = tx.push(bucket, key, DataLRemFlag, newValue) 325 if err != nil { 326 return err 327 } 328 329 return nil 330 } 331 332 // LTrim trims an existing list so that it will contain only the specified range of elements specified. 333 // the offsets start and stop are zero-based indexes 0 being the first element of the list (the head of the list), 334 // 1 being the next element and so on. 335 // start and end can also be negative numbers indicating offsets from the end of the list, 336 // where -1 is the last element of the list, -2 the penultimate element and so on. 337 func (tx *Tx) LTrim(bucket string, key []byte, start, end int) error { 338 var ( 339 err error 340 buffer bytes.Buffer 341 ) 342 343 if err = tx.checkTxIsClosed(); err != nil { 344 return err 345 } 346 347 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 348 if err != nil { 349 return err 350 } 351 352 var ( 353 bucketId = b.Id 354 l *List 355 exist bool 356 ) 357 358 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 359 return ErrBucket 360 } 361 362 if tx.CheckExpire(bucket, key) { 363 return ErrListNotFound 364 } 365 if _, ok := l.Items[string(key)]; !ok { 366 return ErrListNotFound 367 } 368 369 if _, err := tx.LRange(bucket, key, start, end); err != nil { 370 return err 371 } 372 373 buffer.Write(key) 374 buffer.Write([]byte(SeparatorForListKey)) 375 buffer.Write([]byte(strconv2.IntToStr(start))) 376 newKey := buffer.Bytes() 377 378 return tx.push(bucket, newKey, DataLTrimFlag, []byte(strconv2.IntToStr(end))) 379 } 380 381 // LRemByIndex remove the list element at specified index 382 func (tx *Tx) LRemByIndex(bucket string, key []byte, indexes ...int) error { 383 if err := tx.checkTxIsClosed(); err != nil { 384 return err 385 } 386 387 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 388 if err != nil { 389 return err 390 } 391 bucketId := b.Id 392 if _, ok := tx.db.Index.list.exist(bucketId); !ok { 393 return ErrListNotFound 394 } 395 396 if tx.CheckExpire(bucket, key) { 397 return ErrListNotFound 398 } 399 400 if len(indexes) == 0 { 401 return nil 402 } 403 404 sort.Ints(indexes) 405 data, err := MarshalInts(indexes) 406 if err != nil { 407 return err 408 } 409 410 err = tx.push(bucket, key, DataLRemByIndex, data) 411 if err != nil { 412 return err 413 } 414 415 return nil 416 } 417 418 // LKeys find all keys matching a given pattern 419 func (tx *Tx) LKeys(bucket, pattern string, f func(key string) bool) error { 420 if err := tx.checkTxIsClosed(); err != nil { 421 return err 422 } 423 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 424 if err != nil { 425 return err 426 } 427 var ( 428 bucketId = b.Id 429 l *List 430 exist bool 431 ) 432 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 433 return ErrBucket 434 } 435 436 for key := range l.Items { 437 if tx.CheckExpire(bucket, []byte(key)) { 438 continue 439 } 440 if end, err := MatchForRange(pattern, key, f); end || err != nil { 441 return err 442 } 443 } 444 return nil 445 } 446 447 func (tx *Tx) ExpireList(bucket string, key []byte, ttl uint32) error { 448 if err := tx.checkTxIsClosed(); err != nil { 449 return err 450 } 451 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 452 if err != nil { 453 return err 454 } 455 456 var ( 457 bucketId = b.Id 458 l *List 459 exist bool 460 ) 461 462 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 463 return ErrBucket 464 } 465 466 l.TTL[string(key)] = ttl 467 l.TimeStamp[string(key)] = uint64(time.Now().Unix()) 468 ttls := strconv2.Int64ToStr(int64(ttl)) 469 err = tx.push(bucket, key, DataExpireListFlag, []byte(ttls)) 470 if err != nil { 471 return err 472 } 473 return nil 474 } 475 476 func (tx *Tx) CheckExpire(bucket string, key []byte) bool { 477 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 478 if err != nil { 479 return false 480 } 481 482 var ( 483 bucketId = b.Id 484 l *List 485 exist bool 486 ) 487 488 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 489 return false 490 } 491 492 if l.IsExpire(string(key)) { 493 _ = tx.push(bucket, key, DataDeleteFlag) 494 return true 495 } 496 return false 497 } 498 499 func (tx *Tx) GetListTTL(bucket string, key []byte) (uint32, error) { 500 if err := tx.checkTxIsClosed(); err != nil { 501 return 0, err 502 } 503 b, err := tx.db.bm.GetBucket(DataStructureList, bucket) 504 if err != nil { 505 return 0, err 506 } 507 508 var ( 509 bucketId = b.Id 510 l *List 511 exist bool 512 ) 513 if l, exist = tx.db.Index.list.exist(bucketId); !exist { 514 return 0, ErrBucket 515 } 516 return l.GetListTTL(string(key)) 517 }