github.com/trustbloc/kms-go@v1.1.2/spi/storage/storage.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package storage 8 9 import ( 10 "errors" 11 ) 12 13 // MultiError represents the errors that occurred during a bulk operation. 14 type MultiError interface { 15 error 16 Errors() []error // Errors returns the error objects for all operations. 17 } 18 19 var ( 20 // ErrStoreNotFound is returned when a store is not found. 21 ErrStoreNotFound = errors.New("store not found") 22 // ErrDataNotFound is returned when data is not found. 23 ErrDataNotFound = errors.New("data not found") 24 // ErrDuplicateKey is returned when a call is made to Store.Batch using the IsNewKey PutOption with a key that 25 // already exists in the database. 26 ErrDuplicateKey = errors.New("duplicate key") 27 ) 28 29 // StoreConfiguration represents the configuration of a store. 30 // Currently, it's only used for creating indexes in underlying storage databases. 31 type StoreConfiguration struct { 32 // TagNames is a list of Tag names to create indexes on. 33 // Tag names cannot contain any ':' characters. 34 TagNames []string `json:"tagNames,omitempty"` 35 } 36 37 // SortOrder specifies the sort order of query results. 38 type SortOrder int 39 40 const ( 41 // SortAscending indicates that the query results must be sorted in ascending order. 42 SortAscending SortOrder = iota 43 // SortDescending indicates that the query results must be sorted in descending order. 44 SortDescending 45 ) 46 47 // SortOptions sets the order that results from an Iterator will be returned in. Sorting is based on the tag values 48 // associated with the TagName chosen below. The TagName you use below can be the same as the one you're querying on, 49 // or it can be a different one. Depending on the storage implementation, you may need to ensure that the TagName set 50 // below is in the Store's StoreConfiguration before trying to use it for sorting (or it may be optional, 51 // but recommended). If tag value strings are decimal numbers, then the sorting must be based on their numerical value 52 // instead of the string representations of those numbers (i.e. numerical sorting, not lexicographic). 53 // TagName cannot be blank. 54 type SortOptions struct { 55 Order SortOrder 56 TagName string 57 } 58 59 // QueryOptions represents various options for Query calls in a store. 60 type QueryOptions struct { 61 // PageSize sets the page size used by the Store.Query method. 62 PageSize int 63 // InitialPageNum sets the page for the iterator returned from Store.Query to start from. 64 // InitialPageNum=0 means start from the first page. 65 InitialPageNum int 66 // SortOptions defines the sort order. 67 SortOptions *SortOptions 68 } 69 70 // QueryOption represents an option for a Store.Query call. 71 type QueryOption func(opts *QueryOptions) 72 73 // WithPageSize sets the maximum page size for data retrievals done within the Iterator returned by the Query call. 74 // Paging is handled internally by the Iterator. Higher values may reduce CPU time and the number of database calls at 75 // the expense of higher memory usage. 76 func WithPageSize(size int) QueryOption { 77 return func(opts *QueryOptions) { 78 opts.PageSize = size 79 } 80 } 81 82 // WithInitialPageNum sets the page number for an Iterator to start from. If this option isn't used, 83 // then the Iterator will start from the first page. 84 // Page number counting starts from 0 (i.e. initialPageNum=0 means that the iterator will start from the first page). 85 func WithInitialPageNum(initialPageNum int) QueryOption { 86 return func(opts *QueryOptions) { 87 opts.InitialPageNum = initialPageNum 88 } 89 } 90 91 // WithSortOrder sets the sort order used by a Store.Query call. See SortOptions for more information. 92 // If this option isn't used, then the result order from the Iterator will be determined (perhaps arbitrarily) by the 93 // underlying database implementation. 94 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 95 func WithSortOrder(sortOptions *SortOptions) QueryOption { 96 return func(opts *QueryOptions) { 97 opts.SortOptions = sortOptions 98 } 99 } 100 101 // Tag represents a Name + Value pair that can be associated with a key + value pair for querying later. 102 type Tag struct { 103 // Name can be used to tag a given key + value pair as belonging to some sort of common 104 // group. Example: Identifying a key+value pair as being a Verifiable Credential by storing it 105 // with a tag Name called "VC". When used with the optional Value (see below), tag Name + Value can be used to 106 // specify metadata for a given key + value pair. Example: Identifying a Verifiable Credential (stored as a 107 // key+value pair) as belonging to a user account by using a tag Name called "UserAccount" and a tag Value called 108 // "bob@example.com". Tag Names are intended to be static values that the store is configured with in order to build 109 // indexes for queries (see TagNames in StoreConfiguration). 110 // Tag Names cannot contain any ':' characters. 111 Name string `json:"name,omitempty"` 112 // Value can optionally be used to indicate some metadata associated with a tag name for a given key + value pair. 113 // See Name above for an example of how this can be used. 114 // Tag Values are dynamic and are not specified in a StoreConfiguration. 115 // Tag Values cannot contain any ':' characters. 116 Value string `json:"value,omitempty"` 117 } 118 119 // PutOptions represents options for a Put Operation. 120 type PutOptions struct { 121 // This is an optimization for a Put Operation. Some storage providers may be able to store data faster if they 122 // know beforehand that this key does not currently exist in the database. Unexpected behaviour may occur if 123 // this is set to true and the key already exists. See the documentation for the specific storage provider to 124 // see if and how this option is used. 125 IsNewKey bool `json:"isNewKey,omitempty"` 126 } 127 128 // Operation represents an operation to be performed in the Batch method. 129 type Operation struct { 130 Key string `json:"key,omitempty"` 131 Value []byte `json:"value,omitempty"` // A nil value will result in a delete operation. 132 Tags []Tag `json:"tags,omitempty"` // Optional. 133 PutOptions *PutOptions `json:"putOptions,omitempty"` // Optional. Only used for Put Operations. 134 } 135 136 // Provider represents a storage provider. 137 type Provider interface { 138 // OpenStore opens a Store with the given name and returns it. 139 // Depending on the store implementation, this may or may not create an underlying database. 140 // The store implementation may defer creating the underlying database until SetStoreConfig is called or 141 // data is inserted using Store.Put or Store.Batch. 142 // Store names are not case-sensitive. If name is blank, then an error will be returned. 143 OpenStore(name string) (Store, error) 144 145 // SetStoreConfig sets the configuration on a Store. It's recommended calling this method at some point before 146 // calling Store.Query if your store contains a large amount of data. The underlying database will use this to 147 // create indexes to make querying via the Store.Query method faster. If you don't need to use Store.Query, then 148 // you don't need to call this method. OpenStore must be called first before calling this method. If not, then an 149 // error wrapping ErrStoreNotFound will be returned. This method will not open the store automatically. 150 // If name is blank, then an error will be returned. 151 SetStoreConfig(name string, config StoreConfiguration) error 152 153 // GetStoreConfig gets the current Store configuration. 154 // This method operates a bit differently in that it directly checks the underlying storage implementation to see 155 // if the underlying database exists for the given name, rather than checking the currently known list of 156 // open stores in memory. If no underlying database can be found, then an error wrapping ErrStoreNotFound will be 157 // returned. This means that this method can be used to determine whether an underlying database for a Store 158 // already exists or not. This method will not create the database automatically. 159 // If name is blank, then an error will be returned. 160 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 161 GetStoreConfig(name string) (StoreConfiguration, error) 162 163 // GetOpenStores returns all Stores that are currently open in memory from calling OpenStore. 164 // It does not check for all databases that have been created before. They have to have been opened in this Provider 165 // object's lifetime from a call to OpenStore. 166 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 167 GetOpenStores() []Store 168 169 // Close closes all open Stores in this Provider 170 // For persistent Store implementations, this does not delete any data in the underlying databases. 171 Close() error 172 } 173 174 // Store represents a storage database. 175 type Store interface { 176 // Put stores the key + value pair along with the (optional) tags. If the key already exists in the database, 177 // then the value and tags will be overwritten silently. 178 // If value is a JSON-formatted object, then an underlying storage implementation may store it in a way that 179 // does not preserve the order of the fields. Therefore, you should avoid doing direct byte-for-byte comparisons 180 // with data put in and data retrieved, as the marshalled representation may be different - always unmarshal data 181 // first before comparing. 182 // If key is empty or value is nil, then an error will be returned. 183 // A single key-value pair cannot have multiple tags that share the same tag name. 184 Put(key string, value []byte, tags ...Tag) error 185 186 // Get fetches the value associated with the given key. 187 // If key cannot be found, then an error wrapping ErrDataNotFound will be returned. 188 // If key is empty, then an error will be returned. 189 Get(key string) ([]byte, error) 190 191 // GetTags fetches all tags associated with the given key. 192 // If key cannot be found, then an error wrapping ErrDataNotFound will be returned. 193 // If key is empty, then an error will be returned. 194 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 195 GetTags(key string) ([]Tag, error) 196 197 // GetBulk fetches the values associated with the given keys. 198 // If no data exists under a given key, then a nil []byte is returned for that value. It is not considered an error. 199 // Depending on the implementation, this method may be faster than calling Get for each key individually. 200 // If any of the given keys are empty, then an error will be returned. 201 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 202 GetBulk(keys ...string) ([][]byte, error) 203 204 // Query returns all data that satisfies the expression. Basic expression format: TagName:TagValue. 205 // If TagValue is not provided, then all data associated with the TagName will be returned, regardless of their 206 // tag values. 207 // At a minimum, a store implementation must be able to support querying with a single basic expression, but a 208 // store implementation may also support a more advanced expression format. 209 // Advanced expression format: [Criterion1][Operator][Criterion2][Operator]...[CriterionN]. Square brackets are 210 // used here for visual clarity. Omit them from the actual expression string. 211 // Each Criterion follows the rules for the basic expression format described above. 212 // Each operator must be either "&&" or "||" (without quotes). "&&" indicates an AND operator while "||" 213 // indicates an OR operator. The order of operations are ANDs followed by ORs. 214 // This method also supports a number of QueryOptions. If none are provided, then defaults will be used. 215 // If your store contains a large amount of data, then it's recommended calling Provider.SetStoreConfig at some 216 // point before calling this method in order to create indexes which will speed up queries. 217 Query(expression string, options ...QueryOption) (Iterator, error) 218 219 // Delete deletes the key + value pair (and all tags) associated with key. 220 // If key is empty, then an error will be returned. 221 Delete(key string) error 222 223 // Batch performs multiple Put and/or Delete operations in order. The Puts and Deletes here follow the same rules 224 // as described in the Put and Delete method documentation. The only exception is if the operation makes use of 225 // the PutOptions.IsNewKey optimization, in which case an error wrapping an ErrDuplicateKey may be returned if it's 226 // enabled and a key is used that already exists in the database. 227 // Depending on the implementation, this method may be faster than repeated Put and/or Delete calls. 228 // If any of the given keys are empty, or the operations slice is empty or nil, then an error will be returned. 229 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 230 Batch(operations []Operation) error 231 232 // Flush forces any queued up Put and/or Delete operations to execute. 233 // If the Store implementation doesn't queue up operations, then this method is a no-op. 234 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 235 Flush() error 236 237 // Close closes this store object, freeing resources. For persistent store implementations, this does not delete 238 // any data in the underlying databases. 239 // Close can be called repeatedly on the same store multiple times without causing an error. 240 Close() error 241 } 242 243 // Iterator allows for iteration over a collection of entries in a store. 244 type Iterator interface { 245 // Next moves the pointer to the next entry in the iterator. 246 // Note that it must be called before accessing the first entry. 247 // It returns false if the iterator is exhausted - this is not considered an error. 248 Next() (bool, error) 249 250 // Key returns the key of the current entry. 251 Key() (string, error) 252 253 // Value returns the value of the current entry. 254 Value() ([]byte, error) 255 256 // Tags returns the tags associated with the key of the current entry. 257 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 258 Tags() ([]Tag, error) 259 260 // TotalItems returns a count of the number of entries (key + value + tags triplets) matched by the query 261 // that generated this Iterator. This count is not affected by the page settings used (i.e. the count is of all 262 // results as if you queried starting from the first page and with an unlimited page size). 263 // Depending on the storage implementation, you may need to ensure that the TagName used in the query is in the 264 // Store's StoreConfiguration before trying to call this method (or it may be optional, but recommended). 265 // As of writing, aries-framework-go code does not use this, but it may be useful for custom solutions. 266 TotalItems() (int, error) 267 268 // Close closes this iterator object, freeing resources. 269 Close() error 270 }