Skip to content

Instantly share code, notes, and snippets.

@martpet
Last active June 15, 2023 08:08
Show Gist options
  • Save martpet/264575855a40a51ad83dbcc7fcf42dd7 to your computer and use it in GitHub Desktop.
Save martpet/264575855a40a51ad83dbcc7fcf42dd7 to your computer and use it in GitHub Desktop.
Unique and non-unique indexes with Deno KV
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