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  }