146
146
147
147
import { timingSafeEqual } from "jsr:@std/[email protected] /timing_safe_equal" ;
148
148
149
+ import { BLOB_KEY , BLOB_META_KEY } from "./blob_util.ts" ;
150
+
149
151
function addIfUnique ( set : Set < Deno . KvKeyPart > , item : Uint8Array ) {
150
152
for ( const i of set ) {
151
153
if ( ArrayBuffer . isView ( i ) && timingSafeEqual ( i , item ) ) {
@@ -156,17 +158,29 @@ function addIfUnique(set: Set<Deno.KvKeyPart>, item: Uint8Array) {
156
158
}
157
159
158
160
function addOrIncrement (
159
- map : Map < Deno . KvKeyPart , number > ,
161
+ map : Map < Deno . KvKeyPart , { count : number ; isBlob ?: boolean } > ,
160
162
item : Uint8Array ,
161
- increment : boolean ,
163
+ next : Deno . KvKeyPart | undefined ,
162
164
) {
165
+ let count = 0 ;
166
+ let isBlob = false ;
167
+ if ( next ) {
168
+ if ( next === BLOB_KEY ) {
169
+ isBlob = true ;
170
+ } else if ( next !== BLOB_META_KEY ) {
171
+ count = 1 ;
172
+ }
173
+ }
163
174
for ( const [ k , v ] of map ) {
164
175
if ( ArrayBuffer . isView ( k ) && timingSafeEqual ( k , item ) ) {
165
- map . set ( k , increment ? v + 1 : v ) ;
176
+ if ( isBlob ) {
177
+ v . isBlob = true ;
178
+ }
179
+ v . count = count ;
166
180
return ;
167
181
}
168
182
}
169
- map . set ( item , increment ? 1 : 0 ) ;
183
+ map . set ( item , isBlob ? { count , isBlob } : { count } ) ;
170
184
}
171
185
172
186
/** Determines if one {@linkcode Deno.KvKeyPart} equals another. This is more
@@ -318,6 +332,9 @@ export async function unique(
318
332
throw new TypeError ( `Unexpected key length of ${ key . length } .` ) ;
319
333
}
320
334
const part = key [ prefixLength ] ;
335
+ if ( part === BLOB_KEY || part === BLOB_META_KEY ) {
336
+ continue ;
337
+ }
321
338
if ( ArrayBuffer . isView ( part ) ) {
322
339
addIfUnique ( prefixes , part ) ;
323
340
} else {
@@ -327,6 +344,17 @@ export async function unique(
327
344
return [ ...prefixes ] . map ( ( part ) => [ ...prefix , part ] ) ;
328
345
}
329
346
347
+ /** Elements of an array that gets resolved when calling
348
+ * {@linkcode uniqueCount}. */
349
+ export interface UniqueCountElement {
350
+ /** The key of the element. */
351
+ key : Deno . KvKey ;
352
+ /** The number of sub-keys the key has. */
353
+ count : number ;
354
+ /** Indicates if the value of the key is a kv-toolbox blob value. */
355
+ isBlob ?: boolean ;
356
+ }
357
+
330
358
/** Resolves with an array of unique sub keys/prefixes for the provided prefix
331
359
* along with the number of sub keys that match that prefix. The `count`
332
360
* represents the number of sub keys, a value of `0` indicates that only the
@@ -336,7 +364,11 @@ export async function unique(
336
364
* where you are retrieving a list including counts and you want to know all the
337
365
* unique _descendants_ of a key in order to be able to enumerate them.
338
366
*
339
- * For example if you had the following keys stored in a datastore:
367
+ * If you omit a `prefix`, all unique root keys are resolved.
368
+ *
369
+ * @example
370
+ *
371
+ * If you had the following keys stored in a datastore:
340
372
*
341
373
* ```ts
342
374
* ["a", "b"]
@@ -345,7 +377,7 @@ export async function unique(
345
377
* ["a", "d", "f"]
346
378
* ```
347
379
*
348
- * And you would get the following results when using `unique ()`:
380
+ * And you would get the following results when using `uniqueCount ()`:
349
381
*
350
382
* ```ts
351
383
* import { uniqueCount } from "jsr:@kitsonk/kv-toolbox/keys";
@@ -356,36 +388,46 @@ export async function unique(
356
388
* // { key: ["a", "d"], count: 2 }
357
389
* await kv.close();
358
390
* ```
359
- *
360
- * If you omit a `prefix`, all unique root keys are resolved.
361
391
*/
362
392
export async function uniqueCount (
363
393
kv : Deno . Kv ,
364
394
prefix : Deno . KvKey = [ ] ,
365
395
options ?: Deno . KvListOptions ,
366
- ) : Promise < { key : Deno . KvKey ; count : number } [ ] > {
396
+ ) : Promise < UniqueCountElement [ ] > {
367
397
const list = kv . list ( { prefix } , options ) ;
368
398
const prefixLength = prefix . length ;
369
- const prefixCounts = new Map < Deno . KvKeyPart , number > ( ) ;
399
+ const prefixCounts = new Map <
400
+ Deno . KvKeyPart ,
401
+ { count : number ; isBlob ?: boolean }
402
+ > ( ) ;
370
403
for await ( const { key } of list ) {
371
404
if ( key . length <= prefixLength ) {
372
405
throw new TypeError ( `Unexpected key length of ${ key . length } .` ) ;
373
406
}
374
407
const part = key [ prefixLength ] ;
408
+ if ( part === BLOB_KEY || part === BLOB_META_KEY ) {
409
+ continue ;
410
+ }
411
+ const next = key [ prefixLength + 1 ] ;
375
412
if ( ArrayBuffer . isView ( part ) ) {
376
- addOrIncrement ( prefixCounts , part , key . length > ( prefixLength + 1 ) ) ;
413
+ addOrIncrement ( prefixCounts , part , next ) ;
377
414
} else {
378
415
if ( ! prefixCounts . has ( part ) ) {
379
- prefixCounts . set ( part , 0 ) ;
416
+ prefixCounts . set ( part , { count : 0 } ) ;
380
417
}
381
- if ( key . length > ( prefixLength + 1 ) ) {
382
- prefixCounts . set ( part , prefixCounts . get ( part ) ! + 1 ) ;
418
+ if ( next ) {
419
+ const count = prefixCounts . get ( part ) ! ;
420
+ if ( next === BLOB_KEY ) {
421
+ count . isBlob = true ;
422
+ } else if ( next !== BLOB_META_KEY ) {
423
+ count . count ++ ;
424
+ }
383
425
}
384
426
}
385
427
}
386
428
return [ ...prefixCounts ] . map ( ( [ part , count ] ) => ( {
387
429
key : [ ...prefix , part ] ,
388
- count,
430
+ ... count ,
389
431
} ) ) ;
390
432
}
391
433
0 commit comments