IndexedDB getting all data with keys - javascript

Using the IndexedDB API we have these 2 methods: getAll() and getAllKeys() with an usage example below:
let transaction = this.db.transaction(["table"]);
let object_store = transaction.objectStore("table");
request = object_store.getAll(); /* or getAllKeys() */
request.onerror = (event) => {
console.err("error fetching data");
};
request.onsuccess = (event) => {
console.log(request.result);
};
The problem is getAll() seems to retrieve only the data in an array format, and getAllKeys() gets all the keys without the data. I could not find a method to get both keys and values.
Isn't there a better way of getting the data and the keys with one call, like it is stored?
If not, is there a nicer way I could do this without making the code too confusing with multiple asynchronous calls happening?

I was able to retrieve all values with their keys with one callback function using an IDBCursor like this:
transaction = this.db.transaction(["table"]);
object_store = transaction.objectStore("table");
request = object_store.openCursor();
request.onerror = function(event) {
console.err("error fetching data");
};
request.onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
let key = cursor.primaryKey;
let value = cursor.value;
console.log(key, value);
cursor.continue();
}
else {
// no more results
}
};

Alternatively you can use getAllKeys, followed by a transaction to fetch the values for each key.
const getAll = (db, store) => new Promise((res, rej) => {
// Fetch keys
const keysTr = db.transaction(store).objectStore(store).getAllKeys()
keysTr.onsuccess = (event) => {
const keys = event.target.result
if (keys?.length) {
// Start a new transaction for final result
const valuesTr = db.transaction(store)
const objStore = valuesTr.objectStore(store)
const result = [] // { key, value }[]
// Iterate over keys
keys.forEach(key => {
const tr = objStore.get(key)
tr.onsuccess = e => {
result.push({
key,
value: e.target.result
})
}
})
// Resolve `getAll` with final { key, value }[] result
valuesTr.oncomplete = (event) => {
res(result)
}
valuesTr.onerror = (event) => {
rej(event)
}
}
else
res([])
}
keysTr.onerror = (event) => {
rej(event)
}
})

Related

Building an Object from fetch statement

I have some code that when you console.log it, it looks like the image below:
The code I am running is as follows:
onClick={() => {
const stream = fetch(
'https://lichess.org/api/games/user/neio',
{ headers: { Accept: 'application/x-ndjson' } }
);
const onMessage = obj => {
console.log('test', obj);
};
const onComplete = () =>
console.log('The stream has completed');
stream.then(readStream(onMessage)).then(onComplete);
}}
export const readStream = processLine => response => {
const stream = response.body.getReader();
const matcher = /\r?\n/;
const decoder = new TextDecoder();
let buf = '';
const loop = () =>
stream.read().then(({ done, value }) => {
if (done) {
if (buf.length > 0) processLine(JSON.parse(buf));
} else {
const chunk = decoder.decode(value, {
stream: true,
});
buf += chunk;
const parts = buf.split(matcher);
buf = parts.pop();
for (const i of parts) processLine(JSON.parse(i));
return loop();
}
});
return loop();
};
export default readStream;
What I am trying to do is build a parent object that contains all these individual rows of data.
I'm new at promises and fetch etc. So currently, I have no idea on how to build this parent object that contains each individual row.
Any suggestions?
Can't you have a global array and add items to it like:
var arrCollection = [];
...
const onMessage = obj => {
arrCollection.push(obj);
};
You can have an object with those items doing like:
var objCollection = { items: arrCollection };

How do I get user details in Firebase Storage?

I'm a new programmer and very new to firebase and I'm trying to get the current user files info to display on the screen, it seems that my problem is that I can get the URL and the metadata separately, how do I combine them? how can I take everything at once?
I need to show the file name, date, time, link to download.
const getUserFiles = async () => {
if (!userUID) {
return null;
}
let listRef = storageRef.child(userUID);
listRef.listAll().then(res => {
// res.prefixes.forEach((item) => {
// });
res.items.forEach(item => {
item.getMetadata().then(item => {
var file = {
name: item.name.toString(),
timeCreated: item.timeCreated.toString(),
link: '',
};
myFiles.push(file);
});
});
res.items.forEach(item => {
let counter = 0;
item.getDownloadURL().then(url => {
myFiles[counter].link = url.toString();
});
});
});
console.log(myFiles);
};
the current method don't work! and notice that the userUID its only the uid without the user (local state)
Thanks!
The problem is with the asynchronous calls. You're making an async call in forEach and forEach expects a synchronous function.
You can change the logic to use for-of instead.
See below:
const getUserFiles = async () => {
if (!userUID) {
return null;
}
let listRef = storageRef.child(userUID);
const res = await listRef.listAll();
for (const itemRef of res.items) {
const itemMetadata = await itemRef.getMetadata();
const url = await itemRef.getDownloadUrl();
var file = {
name: itemMetadata.name.toString(),
timeCreated: itemMetadata.timeCreated.toString(),
link: url,
};
myFiles.push(file);
}
console.log(myFiles);
}

Why the default value variable change as the changed variable value, Vuejs

as you see the code, on the handleUpdateFilter function the second "if" some how defaultCourseData is filtered as filteredData of the first "if". Thank you for helping me!
setup() {
const course = ref();
const defaultCourseData = null
const gettingCourse = async () => {
const { data } = await getCourse();
defaultCourseData = data
course.value = data;
};
const handleUpdateFilter = (data) => {
// data is filtering value
if (data.value.view) {
const filteredData = defaultCourseData.sort((a, b) => b.luotXem - a.luotXem);
course.value = filteredData;
}
if (!data.value.view) {
course.value = defaultCourseData // This case some how defaultCourseData filtered too
}
};
onMounted(() => {
gettingCourse();
});
return {
course,
handleUpdateFilter,
defaultCourseData
};
},
Your defaultCourseData variable isn't reactive.
Therefore it should be evaluated as null at every call.
Try this
defineComponent({
setup() {
const course = ref([]);
const defaultCourseData = ref([]);
const gettingCourse = async () => {
const { data } = await getCourse();
defaultCourseData.value = data
course.value = data;
};
const handleUpdateFilter = (data) => {
// data is filtering value
if (data.value.view) {
course.value = defaultCourseData.value.sort((a, b) => b.luotXem - a.luotXem);
}
if (!data.value.view) {
course.value = defaultCourseData.value // This case some how defaultCourseData filtered too
}
};
onMounted(async () => {
await gettingCourse();
});
return {
course,
handleUpdateFilter,
defaultCourseData
};
})
Edit: The actual issue here was, that the defaultCourseData always returned a sorted array as Array.prototype.sort() mutates the Array.
So making a copy solves the issue.
if (data.value.view) { course.value = [...defaultCourseData.value].sort((a, b) => b.luotXem - a.luotXem); }

Sending list of images as response using Javascript

I am making an API that gets a list of image names, then it has to download them one by one from S3 bucket and then send them all as a response.
The issue is that my images are being uploaded but it seems that when I put them in a list as base64 and then try to send the list then the list just comes up empty.
const getImagesById = async (req, res) => {
const { id } = req.params;
const imagesSet = new Map();
try {
const documentFromDB = await document.findOne({ id });
documentFromDB.devices.forEach((device) => {
const images = new Set();
device.images.forEach(item => images.add(downloadFromS3(item)))
imagesSet.set(device.name, JSON.stringify(mapToObj(images))) // tried adding just images also but neither works
});
res.status(200).json(JSON.stringify(mapToObj(imagesSet)));
} catch (e) {
console.log(`An error occurred : ${e.message}`);
res.status(500)
.send(e.message);
}
};
function mapToObj(inputMap) {
let obj = {};
inputMap.forEach(function(value, key){
obj[key] = value
});
return obj;
}
And this is how I get images from S3:
const downloadFromS3 = async (imageName) => {
try {
const image = await S3Utils.downloadFile(BUCKET_NAME, imageName);
if (image.stack) {
return null;
}
const imageBase64 = image.Body.toString('base64');
return imageBase64;
} catch (e) {
console.log(`An error occurred while downloading : ${e.message}`);
throw e;
}
};
This is the response I am getting at the moment:
"{\"{ name: 'Martin'}\":\"{\\\"[object Promise]\\\":{}}\"}"
What I am trying to do is get a lits of device names, map them in a Map as key with value as the base64 list of images and then send it all in a response to the UI to show the images with the names.
What am I doing wrong here?
You just need to add await before call the downloadFromS3 function, consequently changing all the above functions.
const getImagesById = async (req, res) => {
const { id } = req.params;
const imagesSet = new Map();
try {
const documentFromDB = await document.findOne({ id });
await Promise.all(documentFromDB.devices.map(async (device) => {
const images = new Set();
await Promise.all(device.images.map(async item => images.add(await downloadFromS3(item))))
imagesSet.set(device.name, JSON.stringify(mapToObj(images))) // tried adding just images also but neither works
}));
res.status(200).json(JSON.stringify(mapToObj(imagesSet)));
} catch (e) {
console.log(`An error occurred : ${e.message}`);
res.status(500)
.send(e.message);
}
};
function mapToObj(inputMap) {
let obj = {};
inputMap.forEach(function(value, key){
obj[key] = value
});
return obj;
}

Angular | Subscribe to multiple observables

I'm trying to create a list of events that a user is going to. First I get event keys and then what I would like to do is subscribe to each event and listen for changes. Currently only the last event works because this.eventRef is being changed in the for loop.
eventRef: AngularFireObject<any>
getEvents() {
const eventsGuestsLookup = this.db.object(`eventsGuestsLookup/${this.uid}`).valueChanges()
this.eventsGuestsLookupSub = eventsGuestsLookup
.subscribe(eventKeys => {
if (eventKeys) {
console.log(eventKeys)
for (const k in eventKeys) {
if (eventKey.hasOwnProperty(k)) {
this.eventRef = this.db.object(`events/${k}`)
console.log(this.eventRef)
this.eventRef.snapshotChanges().subscribe(action => {
const key = action.payload.key
const event = { key, ...action.payload.val() }
this.makeEvents(event)
})
}
}
}
})
}
What I do next is get the user's response and for each status I want to display certain information. I don't know any other way of doing this, so I check both lists attending and notAttending and if there is a response from the user I change the event properties.
makeEvents(event) {
console.log(event)
event.goingText = "RSVP"
event.setGoing = 'rsvp'
event.setColor = "rsvp-color"
const attending = this.db.object(`attendingLookup/${this.uid}/${event.key}`).valueChanges()
this.attendingLookupSub = attending
.subscribe(data => {
console.log('attending', data)
if (data) {
event.goingText = "ATTENDING"
event.setGoing = 'thumbs-up'
event.setColor = 'attending-color'
}
})
const notAttending = this.db.object(`not_attendingLookup/${this.uid}/${event.key}`).valueChanges()
this.notAttendingLookupSub = notAttending
.subscribe(data => {
console.log('not attending', data)
if (data) {
event.goingText = "NOT ATTENDING"
event.setGoing = 'thumbs-down'
event.setColor = 'not-attending-color'
}
})
this.events.push(event)
}
*** Edit
const eventsGuestsLookup = this.db.object(`eventsGuestsLookup/${this.uid}`).valueChanges()
eventsGuestsLookup.subscribe(keys => {
of(keys).pipe(
mergeMap(keys => {
Object.keys(keys).map(k => {
console.log(k)
})
return merge(Object.keys(keys).map(k => this.db.object(`events/${k}`)))
})
).subscribe(data => console.log('data', data))
})
what you want to acheive is flat your observables collection. to acheive it you can do something like this :
//Dummy eventKeys observable.
const obs1$ = new BehaviorSubject({key:1, action: 'lorem'});
const obs2$ = new BehaviorSubject({key:2, action: 'lorem'});
const obs3$ = new BehaviorSubject({key:3, action: 'lorem'});
const eventKeys = {
obs1$,
obs2$,
obs3$
};
// Dummy eventsGuestsLookup observable.
of(eventKeys)
.pipe(
//eventsGuestsLookup dispatch collection of obserbable, we want to flat it.
mergeMap(ev => {
// We merge all observables in new one.
return merge(...Object.keys(ev).map(k => ev[k]));
}),
).subscribe(console.log);
inportant note : ev[k] is an Observable object. On your case you should do something like :
.map(k => this.db.object(`events/${k}`)) // will return observable.
live demo

Categories

Resources