Last active
June 15, 2023 08:08
-
-
Save martpet/264575855a40a51ad83dbcc7fcf42dd7 to your computer and use it in GitHub Desktop.
Unique and non-unique indexes with Deno KV
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const kv = await Deno.openKv(); | |
interface Choice { | |
id: string; | |
poll: string; | |
title: string; | |
} | |
export async function setChoice(choice: Choice) { | |
const primaryKey = ["choices", choice.id]; | |
const byTitleKey = ["choices_by_title", choice.title]; | |
const byPollKey = ["choices_by_poll", choice.poll, choice.id]; | |
const { duplicateFields, keysForRemoval } = await checkIndexes< | |
Choice | |
>({ | |
kv, | |
primaryKey, | |
uniqueIndexes: { title: byTitleKey }, | |
nonUniqueIndexes: { poll: byPollKey }, | |
}); | |
if (duplicateFields) { | |
return { duplicateFields }; | |
} | |
const operation = kv.atomic(); | |
if (keysForRemoval) { | |
keysForRemoval.forEach((key) => operation.delete(key)); | |
} | |
operation | |
.set(primaryKey, choice) | |
.set(byTitleKey, choice) | |
.set(byPollKey, choice); | |
await operation.commit(); | |
} | |
interface CheckIndexesArg<T> { | |
kv: Deno.Kv; | |
primaryKey: Deno.KvKey; | |
uniqueIndexes?: Partial<Record<keyof T, Deno.KvKey>>; | |
nonUniqueIndexes?: Partial<Record<keyof T, Deno.KvKey>>; | |
} | |
async function checkIndexes<T>(arg: CheckIndexesArg<T>) { | |
const { kv, primaryKey, uniqueIndexes, nonUniqueIndexes } = arg; | |
const allIndexes = { ...uniqueIndexes, ...nonUniqueIndexes }; | |
const allFields = Object.keys(allIndexes) as (keyof T)[]; | |
const uniqueIndexesFields = Object.keys(uniqueIndexes || {}); | |
const secondaryKeys = Object.values(allIndexes) as Deno.KvKey[]; | |
const entries = await kv.getMany<T[]>([primaryKey, ...secondaryKeys]); | |
const [primaryEntry, ...secondaryEntries] = entries; | |
const primaryItem = primaryEntry.value; | |
const duplicateFields: (keyof T)[] = []; | |
const keysForRemoval: Deno.KvKey[] = []; | |
allFields.forEach((fieldName, i) => { | |
const secondaryKey = secondaryKeys[i]; | |
const isUniqueIndex = uniqueIndexesFields.includes(fieldName as string); | |
const positionOfValueInKey = secondaryKey.length - (isUniqueIndex ? 1 : 2); | |
const currentVal = primaryItem?.[fieldName]; | |
const newVal = secondaryKey[positionOfValueInKey]; | |
const secondaryItem = secondaryEntries[i].value; | |
if (newVal !== currentVal) { | |
if (!secondaryItem && oldVal !== undefined) { | |
const key = [...secondaryKey]; | |
key[positionOfValueInKey] = currentVal as string; | |
keysForRemoval.push(key); | |
} else if (secondaryItem && isUniqueIndex) { | |
duplicateFields.push(fieldName); | |
} | |
} | |
}); | |
if (duplicateFields.length) { | |
return { duplicateFields }; | |
} | |
return { keysForRemoval }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment