Files
dandelion/src/db.ts
2024-09-20 20:54:54 -07:00

279 lines
9.6 KiB
TypeScript

// interface MyJsonObject {
// id: string;
// name: string;
// email: string;
// }
// Efficiently storing data in indexdb: https://stackoverflow.com/a/62975917
const postStoreName: string = "posts";
let keyBase = "dandelion_posts_v1_"
let key = "";
interface IDBRequestEvent<T = any> extends Event {
target: IDBRequest<T>;
}
// IndexedDB uses DOMException, so let's use it for error typing
type DBError = Event & {
target: { errorCode: DOMException };
};
export function openDatabase(userID: string): Promise<IDBDatabase> {
const dbName = `user_${userID}`
return new Promise((resolve, reject) => {
const request: IDBOpenDBRequest = indexedDB.open(dbName, 1);
request.onerror = (event: Event) => {
// Use a type assertion to access the specific properties of IDBRequest error event
const errorEvent = event as IDBRequestEvent;
reject(`Database error: ${errorEvent.target.error?.message}`);
};
request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(postStoreName)) {
let store = db.createObjectStore(postStoreName, { keyPath: "id", autoIncrement: true });
store.createIndex("datetimeIndex", "post_timestamp", { unique: false });
store.createIndex("postIDIndex", "data.post_id", { unique: true });
}
};
request.onsuccess = (event: Event) => {
const db: IDBDatabase = (event.target as IDBOpenDBRequest).result;
resolve(db);
};
});
}
export async function addData(userID: string, data: any): Promise<void> {
try {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readwrite");
const store = transaction.objectStore(postStoreName);
const addRequest = store.add({ post_timestamp: data.post_timestamp, data: data });
addRequest.onsuccess = (e: Event) => {
// console.log('Data has been added:', (e.target as IDBRequest).result);
};
addRequest.onerror = (event: Event) => {
// Use a type assertion to access the specific properties of IDBRequest error event
const errorEvent = event as IDBRequestEvent;
console.error('Error in adding data:', errorEvent.target.error?.message);
};
} catch (error) {
console.error('Error in opening database:', error);
}
}
export async function deleteData(userID: string, postID: string) {
try {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readwrite");
const store = transaction.objectStore(postStoreName);
const index = store.index("postIDIndex");
const getRequest = index.getKey(postID);
getRequest.onerror = e => console.log((e.target as IDBRequest).error)
getRequest.onsuccess = e => {
const key = (e.target as IDBRequest).result;
if (key === undefined) {
console.error("Post not found");
return null;
}
const deleteRequest = store.delete(key);
deleteRequest.onerror = e => { console.error((e.target as IDBRequest).error); return false };
deleteRequest.onsuccess = () => true;
}
} catch (error) {
console.error('Error in opening database:', error);
}
}
export async function clearData(userID: string) {
try {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readwrite");
const store = transaction.objectStore(postStoreName);
const clearRequest = store.clear();
clearRequest.onsuccess = (e: Event) => {
// console.log('Data has been added:', (e.target as IDBRequest).result);
};
clearRequest.onerror = (event: Event) => {
// Use a type assertion to access the specific properties of IDBRequest error event
const errorEvent = event as IDBRequestEvent;
console.error('Error in clearing data:', errorEvent.target.error?.message);
};
} catch (error) {
console.error('Error in opening database:', error);
}
}
export async function addDataArray(userID: string, array: any[]): Promise<void> {
try {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readwrite");
const store = transaction.objectStore(postStoreName);
let count = 0;
array.reverse();
for (let data of array) {
const addRequest = store.add({ post_timestamp: data.post_timestamp, data: data });
addRequest.onsuccess = (e: Event) => {
// console.log('Data has been added:', (e.target as IDBRequest).result);
};
addRequest.onerror = (event: Event) => {
// Use a type assertion to access the specific properties of IDBRequest error event
const errorEvent = event as IDBRequestEvent;
console.error('Error in adding data:', errorEvent.target.error?.message);
};
count++;
// if (count % 100 === 0) {
// console.log(`Added ${count} posts...`);
// }
}
} catch (error) {
console.error('Error in opening database:', error);
}
}
export async function mergeDataArray(userID: string, array:any[]): Promise<void> {
try {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readwrite");
const store = transaction.objectStore(postStoreName);
const index = store.index("postIDIndex");
transaction.oncomplete = () => {
console.log("Transaction completed successfully");
db.close();
};
transaction.onerror = (event) => {
console.error("Transaction error:", (event.target as any).error);
db.close();
};
let postsToWrite:any = [];
for (let post of array) {
try {
let havePost = await new Promise<boolean>((resolve, reject) => {
const getRequest = index.getKey(post.post_id);
getRequest.onerror = (e) => {
console.log((e.target as IDBRequest).error);
reject(e);
};
getRequest.onsuccess = async (e) => {
const key = (e.target as IDBRequest).result;
resolve(key !== undefined)
};
});
// console.log(post.post_id, havePost);
if (!havePost ) {
postsToWrite.push(post);
}
} catch (error) {
console.error("Error processing post:", error);
}
}
console.log(`Writing ${postsToWrite.length} posts`);
await addDataArray(userID, postsToWrite);
} catch (error) {
console.error("Error in opening database:", error);
}
}
export async function getData(userID: string, lowerID: Date, upperID: Date): Promise<any | undefined> {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readonly");
const store = transaction.objectStore(postStoreName);
return new Promise((resolve, reject) => {
const keyRangeValue = IDBKeyRange.bound(lowerID, upperID);
const records: any[] = [];
const index = store.index("datetimeIndex");
const cursorRequest = index.openCursor(keyRangeValue);
cursorRequest.onsuccess = (event: Event) => {
const cursor = (event.target as IDBRequest).result as IDBCursorWithValue;
if (cursor) {
records.push(cursor.value.data); // Collect the record
cursor.continue(); // Move to the next item in the range
} else {
// No more entries in the range
resolve(records);
}
};
cursorRequest.onerror = (event: Event) => {
// Use a type assertion to access the specific properties of IDBRequest error event
const errorEvent = event as IDBRequestEvent;
console.error('Transaction failed:', errorEvent.target.error?.message);
reject(errorEvent.target.error); // Reject the promise if there's an error
};
});
}
export async function getAllData(userID: string): Promise<any | undefined> {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readonly");
const store = transaction.objectStore(postStoreName);
return new Promise((resolve, reject) => {
const getRequest = store.getAll();
getRequest.onsuccess = () => {
if (getRequest.result) {
// console.log('Retrieved data:', getRequest.result.jsonData);
// resolve(getRequest.result.jsonData as any);
resolve(getRequest.result);
} else {
console.log('No data record found for key', key);
resolve(undefined); // explicitly resolve with undefined when no data is found
}
};
getRequest.onerror = (event: Event) => {
// Use a type assertion to access the specific properties of IDBRequest error event
const errorEvent = event as IDBRequestEvent;
console.error('Transaction failed:', errorEvent.target.error?.message);
reject(errorEvent.target.error); // reject the promise if there's an error
};
});
}