IDB: openCursor() is not a function - javascript

Well, maybe I do something wrong... But this error is very confusing. I stick with: "Uncaught TypeError: requestChatHistory.openCursor is not a function at IDBOpenDBRequest.dbPromise.onsuccess"
IDBOpenRequest might inherit methods from IDBRequest...
I tried to make request without openCursor() and get the first value from it.
Also tried to re-create database, use different browsers (Chrome, Edge, Firefox)
onUpgrade promise:
dbPromise.onupgradeneeded = function(event) {
let db = event.target.result;
db.createObjectStore('keys', {keyPath: 'userId'});
let chatHistory = db.createObjectStore('messages', { keyPath: "id", autoIncrement:true });
chatHistory.createIndex("chatId", "chatId", { unique: false });
};
There is my function with request:
async function loadSavedMessages(chatId){
let dbPromise = idb.open('clientDB', 3);
dbPromise.onsuccess = function() {
let db = this.result;
let dbTransaction = db.transaction(["messages"]);
let messages = dbTransaction.objectStore("messages");
let index = messages.index('chatId');
let requestChatHistory = index.get(chatId);
requestChatHistory.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
console.log(cursor);
cursor.continue();
}
};
}
}
Function that saves data:
async function saveMessage(chatId, message, userId){
let dbPromise = idb.open('clientDB', 3);
dbPromise.onsuccess = function() {
let db = this.result;
let dbTransaction = db.transaction(["messages"], 'readwrite');
let messages = dbTransaction.objectStore("messages");
let mesObj = {
chatId: chatId,
user: userId,
message: message,
timestamp: Date.now()
};
let save = messages.add(mesObj);
save.onerror = function(event) {
// Handle errors!
console.log("Something went wrong with local DB :(")
};
save.onsuccess = function(event) {
// Do something with the request.result!
console.log(`Message saved, id ${save.result}`);
};
}
}

This is your problem:
let requestChatHistory = index.get(chatId);
requestChatHistory.openCursor().onsuccess = function(event) {
requestChatHistory is an IDBRequest, which has no openCursor method. openCursor is on IDBIndex, like your index variable. So maybe you meant to do something like:
index.openCursor(chatId).onsuccess = function(event) {

Related

Javascript: How to update IndexedDB?

I'm trying to create a chrome extension, but I am having some trouble updating my DB.
In the code below I am using index.get to the the object that contains a certain value. If such an object doesn't exist I will create a new one, which works just fine.
But if the DB contains an object with the specified value, I want to append a new object to an array (allMessages) that is inside the object I searched for. The details doesn't really matter in this case.
What is important is to find out if the way I'm adding this new obj to the array (allMessages) is a valid way of updating the database.
records.forEach((person) => {
console.log("here1");
const index = objectStore.index("urlKeyValue");
let search = index.get(person.urlKeyValue);
search.onsuccess = function (event) {
if (search.result === undefined) {
// no record with that key
let request = objectStore.add(person);
request.onsuccess = function () {
console.log("Added: ", person);
};
} else {
// here I'm iterating an array that is inside the obj I searched for,
// and then checking if the key for that array matches **theUserId**
for (userObj of event.target.result.allMessages) {
if (theUserId == Object.keys(userObj)) {
// is this part correct. Is it possible to update the DB this way?
let objToAdd1 = {
time: person.allMessages[0][theUserId][0].time,
msg: person.allMessages[0][theUserId][0].msg,
};
let currentObj = userObj[theUserId];
let updatedObj = currentObj.push(objToAdd1);
}
}
)}
Using objectStore.openCursor you can update only part of the record.
The following updates only book prices.
const transaction = db.transaction("books", "readwrite");
const objectStore = transaction.objectStore("books");
records = [{ id: "kimetu", price: 600 }];
records.forEach((book) => {
const index = objectStore.index("id");
const search = index.get(book.id);
search.onsuccess = () => {
if (search.result === undefined) {
const request = objectStore.add(book);
request.onsuccess = () => {
console.log("Added: ", book);
};
} else {
const request = objectStore.openCursor(IDBKeyRange.only(book.id));
request.onsuccess = () => {
const cursor = request.result;
if (cursor) {
cursor.value.price = 1000;
const updateRequest = cursor.update(cursor.value);
updateRequest.onsuccess = () => {
console.log("Updated: ", cursor.value.price);
};
cursor.continue();
}
};
}
}
});

How to trigger firebase http function in node.js?

I am trying to trigger an another function in Firebase Cloud function with javascript. But i always getting an error of Can't set headers after they are sent. Please take a look at my code below: ................. ................. ............ ................ ................. ............... ....................... .................. ..............
exports.productIndexShuffleOne = functions.https.onRequest(async (req, res) => {
const interval = req.query.interval;
console.log("interval: "+interval);
const productRef = admin.firestore().collection("Products");
const adminRef = admin.firestore().collection("Admin").doc("totalProd").get();
const dateRef = admin.firestore().collection("Admin").doc("totalProd").collection("indexShuffle").doc("productShuffle").get();
return dateRef.then(documentSnapshot => {
const setDate = documentSnapshot.get('date').seconds;
var nextDay = setDate;
console.log("Date: "+nextDay);
const x = setInterval(function() {
clearInterval(x);
return Promise.all([adminRef]).then(result => {
const totalNum = result[0].data().totalNumber;
console.log("totalNum: "+totalNum);
var numberList = [];
var index = 1;
while(index <= totalNum){
numberList.push(index);
index++;
}
var cidx, ridx, tmp;
cidx = numberList.length;
while (cidx !== 0) {
ridx = Math.floor(Math.random() * cidx);
cidx--;
tmp = numberList[cidx];
numberList[cidx] = numberList[ridx];
numberList[ridx] = tmp;
}
console.log(numberList);
var counter = 0;
return productRef.get().then(snapshot => {
snapshot.forEach(doc => {
const prodID = doc.get('productID');
const index = doc.get('index');
var newIndex = numberList[counter];
counter++;
console.log("oldIndex: "+index);
console.log("newIndex: "+newIndex);
productRef.doc(prodID).update({
index: newIndex
}, {merge: true});
});
return res.redirect('https://us-central1-myfunction-123456.cloudfunctions.net/productIndexShuffleTwo?interval='+interval);
})
.catch(err => {
console.log('Error getting documents', err);
});
});
}, interval);
return res.status(203).send(interval);
}).catch(function(err) {
console.error(err);
});
});
This is because you've sent multiple responses while the rule is that you only allowed sending one response. Please try to look at your code and optimize it in such a way that it contains only one response.
I can see you have multiple responses as below:
1 -> return res.redirect('https://us-central1-myfunction-123456.cloudfunctions.net/productIndexShuffleTwo?interval='+interval);
2 -> return res.status(203).send(interval);
I believe that you can have res.redirect and then res.status.send called one after another. When you writing endpoints there rule of a thumb: always send response and only do that once. Refactor your code so there no way you can make those two calls, but only one of them.

Confirmed populated array of objects returns empty

I have a method that is failing when returning an array of objects. As mentioned in the title - the array is confirmed to be populated but is empty in the response.
Here is the full flow:
The Url:
http://localhost:53000/api/v1/landmarks?lat=40.76959&lng=-73.95136&radius=160
Is routed to the corresponding index:
api.route('/api/v1/landmarks').get(Landmark.list);
The Index Calls a Service:
exports.list = (req, res) => {
const landmark = new LandmarkService();
landmark.getLandmarks(req)
.then(landmarks => {
var response = new Object();
response.startindex = req.query.page;
response.limit = req.query.per_page;
response.landmarks = landmarks;
res.json(response);
})
.catch(err => {
logger.error(err);
res.status(422).send(err.errors);
});
};
The Service Method Uses a Data Access Class to Return the Promise
getLandmarks(req) {
const params = req.params || {};
const query = req.query || {};
const page = parseInt(query.page, 10) || 1;
const perPage = parseInt(query.per_page, 10);
const userLatitude = parseFloat(query.lat);
const userLongitude = parseFloat(query.lng);
const userRadius = parseFloat(query.radius) || 10;
const utils = new Utils();
const data = new DataService();
const landmarkProperties = ['key','building','street','category','closing',
'email','name','opening','phone','postal','timestamp','type','web'];
return data.db_GetAllByLocation(landmarksRef, landmarkLocationsRef,
landmarkProperties, userLatitude, userLongitude, userRadius);
} // getLandmarks
However, the response is always empty.
I am building an array in the called method and populating it with JSON objects. That is what is supposed to be sent back in the response. I can confirm that the attributes array is correctly populated before I hit the return statement. I can log it to the console. I can also send back a test array filled with stub values successfully.
I have a feeling it is how I am setting things up inside the Promise?
Data Access Method That Should Return Array of Objects:
db_GetAllByLocation(ref, ref_locations, properties, user_latitude, user_longitude, user_radius)
{
const landmarkGeoFire = new GeoFire(ref_locations);
var geoQuery = landmarkGeoFire.query({
center: [user_latitude, user_longitude],
radius: user_radius
});
var locations = [];
var onKeyEnteredRegistration = geoQuery.on("key_entered", function (key, coordinates, distance) {
var location = {};
location.key = key;
location.latitude = coordinates[0];
location.longitude = coordinates[1];
location.distance = distance;
locations.push(location);
});
var attributes = [];
var onReadyRegistration = geoQuery.on("ready", function() {
ref.on('value', function (refsSnap) {
refsSnap.forEach((refSnap) => {
var list = refSnap;
locations.forEach(function(locationSnap)
{
//console.log(refSnap.key, '==', locationSnap.key);
if (refSnap.key == locationSnap.key)
{
var attribute = {};
for(var i=0; i<=properties.length-1; i++)
{
if(properties[i] == 'key') {
attribute[properties[i]] = refSnap.key;
continue;
}
attribute[properties[i]] = list.child(properties[i]).val();
}
attribute['latitude'] = locationSnap.latitude;
attribute['longitude'] = locationSnap.longitude;
attribute['distance'] = locationSnap.distance;
attributes.push(attribute);
} // refSnap.key == locationSnap.key
}); // locations.forEach
}); // refsSnap.forEach
return Promise.resolve(attributes); <-- does not resolve (throws 'cannot read property .then')
//geoQuery.cancel();
}); // ref.on
}); // onreadyregistration
return Promise.resolve(attributes); <-- comes back empty
}
It seems that data.db_GetAllByLocation is an asynchronous function, therefore the call resolve(landmarks); is getting called before the execution of the async function is finished. If the data.db_GetAllByLocation returns a promise then call the resolve(landmarks) inside the promise.
data.db_GetAllByLocation().then(function() {
resolve();
})
Also try the following modified db_GetAllByLocation()
db_GetAllByLocation(ref, ref_locations, properties, user_latitude, user_longitude, user_radius)
{
return new Promise(function(resolve, reject){
const landmarkGeoFire = new GeoFire(ref_locations);
var geoQuery = landmarkGeoFire.query({
center: [user_latitude, user_longitude],
radius: user_radius
});
var locations = [{}];
var onKeyEnteredRegistration = geoQuery.on("key_entered", function (key, coordinates, distance) {
var location = {};
location.key = key;
location.latitude = coordinates[0];
location.longitude = coordinates[1];
location.distance = distance;
locations.push(location);
});
var attributes = [{}];
var onReadyRegistration = geoQuery.on("ready", function() {
ref.on('value', function (refsSnap) {
refsSnap.forEach((refSnap) => {
var list = refSnap;
locations.forEach(function(locationSnap)
{
if (refSnap.key == locationSnap.key)
{
var attribute = {};
for(var i=0; i<=properties.length-1; i++)
{
if(properties[i] == 'key') {
attribute[properties[i]] = refSnap.key;
continue;
}
attribute[properties[i]] = list.child(properties[i]).val();
}
attribute['latitude'] = locationSnap.latitude;
attribute['longitude'] = locationSnap.longitude;
attribute['distance'] = locationSnap.distance;
attributes.push(attribute);
} // refSnap.key == locationSnap.key
}); // locations.forEach
}); // refsSnap.forEach
// return JSON.stringify(attributes);
return resolve(attributes);
}); // ref.on
}); // onreadyregistration
});
}
OK, I sorted this by removing all my code and writing some test logic (I should have done this before I posted my question).
The below flow works for me, and, applied back to my code, gave me the results I was looking for. No need to re-post the code, but maybe the below flow will be helpful to somebody.
route
api.route('/api/v1/landmarks').get(Landmark.test);
index
exports.test = (req, res) => {
const landmark = new LandmarkService();
landmark.getLandmarksTest(req)
.then(landmarks => {
var final = {};
final.attr1 = 'attr1';
final.attr2 = 'attr2';
final.landmarks = landmarks;
res.json(final);
})
.catch(err => {
logger.error(err);
res.status(422).send(err.errors);
});
};
service method
getLandmarksTest(req)
{
const data = new DataService();
data.db_PromiseTest().then(results => {
return Promise.resolve(results);
}).catch(err => {
return Promise.reject(err.errors);
});
}
data layer method
db_PromiseTest()
{
var stub = {
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
};
return Promise.resolve(stub);
}

Why array.length is not working while array is filled by a async function in javascript

I'm learing WebAudio API. I'm facing a problem with it. Basically things are asynchronous here...so im getting a bit confused. Please help.Here is my code:-
//"use strict";
var sources = new Array();
var actx;
var songs = ['src1.mp3', 'src2.mp3'];
async function start() {
console.log("WELCOME!!");
try {
actx = new AudioContext();
} catch (e) {
console.log('WebAudio api is not supported!!');
}
await getBuffers(actx, songs);
console.log(sources);
console.log(sources.length);
}
function load_song(url) {
let promise = new Promise((resolve, reject) => {
let request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = () => {
let audioData = request.response;
resolve(audioData);
}
request.onerror = () => {
reject(new error("Could not load the song:- " + url));
}
request.send();
});
return promise;
}
//creats buffers
async function getBuffers(actx, songs) {
// let buffer_list = new Array();
for (let x = 0; x < songs.length; x++) {
let temp = actx.createBufferSource();
await load_song(songs[x]).then((audioData) => {
actx.decodeAudioData(audioData).then((decodedAudioData) => {
temp.buffer = decodedAudioData;
sources.push(temp);
}).catch((error) => {
console.error(error);
});
});
}
//console.log(buffers.length);
}
async function play() {
//start();
sources[0].start(0);
//sources[1].start(0);
}
function stop() {
sources[0].stop(0);
//sources[1].stop(0);
}
Here in the two lines console.log(sources) and console.log(sources.length). Here the results are. Why console.log(sources.length) is 0?
Please help me........Thank you.
You need to return
actx.decodeAudioData(audioData).then((decodedAudioData) => {
As you are not returning it, you dont await it. Therefore the log appears before the array gets filled, however console.log(sources) is live, so you see the latest change.
Such mistakes are more unlikely to happen when you use await everywhere, and thats also easier to read IMO:
async function getBuffers(actx, songs) {
const sources = []; //mutating a global variable isnt good, so lets make it local
for (const song of songs) { //for...of is so beautiful, why dont you use it?
try { //this is similar to .catch
let temp = actx.createBufferSource();
const audioData = await load_song(song);
const decodedAudioData = await actx.decodeAudioData(audioData);
temp.buffer = decodedAudioData;
sources.push(temp);
} catch(e){ console.error(e); } //okay that does not count as an error handler...
}
return sources; //getBuffers implies that it returns sth
}
You should change your getBuffers code for something like this
async function getBuffers(actx, songs) {
try {
for (let song of songs) {
let temp = actx.createBufferSource();
var audioData = await load_song(songs[x])
var decodedAudioData = await actx.decodeAudioData(audioData)
temp.buffer = decodedAudioData;
sources.push(temp);
}
} catch (e) { console.log(e) }
}

IndexDB cursor .onsucess as a promise

First of all, I can't find a suitable title for this question - please feel free to edit.
I have the following function that reads objects from indexDb,
loadNeededParcels = property => {
const dbResults = [];
var readTransaction = this.db
.transaction("parcelData")
.objectStore("parcelData");
readTransaction.openCursor().onerror = e => {
console.log("open cursor error ", e);
};
readTransaction.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
dbResults.push(cursor.value);
cursor.continue();
} else {
return dbResults;
}
};
};
Now when I call this function with a simple function call, for example:
console.log(loadNeededParcels('hasData'))
The console log is undefined. I am guessing this happens because the function does not wait for the cursor to finish and return the dbResults variable?
So my question is this - how can I re-write this function as a promise, or rather to wait for the readTransaction.openCursor().onsucess to trigger?
So the expected result is for the function to actually return the values read from the database before exiting.
I am using cursors since the .getAll() the method is not supported in IE.
A simple solution that I ended up using:
loadNeededParcels = property => {
return new Promise((resolve, reject) => {
var readTransaction = this.db
.transaction("parcelData")
.objectStore("parcelData");
readTransaction.openCursor().onerror = e => {
reject(e);
};
const dbResults = [];
readTransaction.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
dbResults.push(cursor.value);
cursor.continue();
} else {
resolve(dbResults);
}
};
});
};
Try something like this. Do not call openCursor twice, that creates two requests.
function loadNeededParcels(db, property) {
return new Promise(function(resolve, reject) {
var results = [];
var tx = db.transaction('parcelData');
tx.onerror = function(event) {
reject(tx.error);
};
var store = tx.objectStore('parcelData');
// Open a cursor over all items
var request = store.openCursor();
request.onsuccess = function(event) {
var cursor = request.result;
if(cursor) {
var value = cursor.value;
if(value) {
// Only append defined values to the array
results.push(cursor.value);
}
cursor.continue();
} else {
resolve(results);
}
};
});
}
loadNeededParcels(db, 'hasData').then(function(results) {
console.log('Results', results);
}).catch(function(error) {
console.error(error);
});

Categories