Deletion in FireStore (Latest Snip) - reactjs

I have a Data Table i want to delete every document inside collection before invoke loadCheckOut.
How can i dow that with latest JS Syntax.
I am using React JS, and it initilize DB from getDb() method so methods like db.collection() not work on it i want a complete moduler solution
const loadCheckout = async (priceId) => {
//before adding we must delete existing collection
const docRef_x = collection(db, `customers/${user.uid}/checkout_sessions`);
const snapshot = await getDocs(docRef_x);
const x = await deleteDoc(snapshot);
const docRef = await addDoc(
collection(db, `customers/${user.uid}/checkout_sessions`),
{
price: priceId,
success_url: window.location.origin,
cancel_url: window.location.origin,
}
);
const ref = collection(db, `customers/${user.uid}/checkout_sessions`);
const snap = onSnapshot(
ref,
{ includeMetadataChanges: true },
async (doc) => {
var error = null,
sessionId = null;
var first = true;
doc.forEach((ele) => {
if (first) {
error = ele.data().error;
sessionId = ele.data().sessionId;
first = false;
}
});
console.log(sessionId);
if (error) {
alert(error);
}
if (sessionId) {
const stripe = await loadStripe(stripe_public_key);
stripe.redirectToCheckout({ sessionId });
}
}
);
};

This won't work:
const snapshot = await getDocs(docRef_x);
const x = await deleteDoc(snapshot);
The deleteDoc function requires a single DocumentReference, while your snapshot is a QuerySnapshot. This has very little to do with the change in syntax, as snapshot.delete() also wouldn't have worked in v8 of the SDK and earlier.
To delete the documents in the query snapshot, loop over the results and delete them one by one:
snapshot.forEach((doc) => {
deleteDoc(doc.ref);
});

Related

State Not Finished Setting before being used in useEffect

I am hosting a react app in aws amplify using the aws-serverless version of express as the REST API, which sits inside of a lambda function. A big problem that I am facing is that asynchronous jobs in aws-serverless express cause the lambda function to complete before the promises resolve. Leaving me with no data and no error handling. This caused me to bring a lot of the asynchronous work to the front end of the application.
The problem here is that I need to bring a large amount of data into state. Right now, I am using a delay workaround (shown below) but instead need a programatic way to make sure state is finished updating before being used in the second useEffect hook (dependent on odds & failedTries props) instead of using the delay functionality.
Any help would be greatly appreciated.
const App = ({ signOut }) => {
const [odds, setOdds] = useState([]);
const [updateTime,setUpdateTime] = useState(0);
const [failedTries,setFailedTries] = useState(0);
useEffect(() => {
const setNflOdds = async () => {
let response = await updateNflOdds();
let data = response;
setOdds(data);
};
setNflOdds();
setUpdateTime(1);
const interval = setInterval(() => {
setNflOdds();
setUpdateTime(updateTime => updateTime +1);
}, 100000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const s3Push = (() => {
if(!odds.length) {
setFailedTries(failedTries => failedTries + 1);
} else {
const delay = ms => new Promise(res => setTimeout(res, ms));
const nflOddsRefDelay = async() => {
*//This is the current workaround, wait ten seconds before pushing odds state up to the s3 bucket*
await delay(10000);
oddsS3Helper(odds);
};
nflOddsRefDelay()
}
});
s3Push();
}, [odds, failedTries]);
With the above indicated delay workaround this works for my use case (13k records inside of the array) but the data size is highly variable and I want to figure out a way that no matter the data size it brings the entire call up to the s3 bucket.
below is the content of the functions being called in the useEffect hook
const pushToS3 = async ( file, key ) => {
const creds = await Auth.currentCredentials()
const REGION = {region};
const s3Client = new S3Client({
credentials: Auth.essentialCredentials(creds),
region: REGION
});
const params = {
Bucket: {s3 bucket name}
Key: key,
Body: file,
};
s3Client.send(new PutObjectCommand(params));
console.log("file is sent");
};
const oddsS3Helper = (async (odds) => {
console.log("inside s3 helper: ",odds);
let csv = '';
let headers = Object.keys(odds[0]).join(',');
let values = odds.map(odd => Object.values(odd).join(',')).join('\n');
csv += headers + '\n' + values;
const buffedFile = csv;
const key = 'nflprops.csv'
const delay = ms => new Promise(res => setTimeout(res, ms));
const propRefDelay = async() => {
await delay(5000);
await postNflOdds();
};
pushToS3( buffedFile, key );
await propRefDelay();
});
async function getNflGames() {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {} // OPTIONAL
};
const data = await API.get(apiName, path, init);
return data;
};
async function getNflOdds(gameId) {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {}, // OPTIONAL
body: { gameId }
};
const data = await API.post(apiName, path, init);
return data;
};
async function updateNflOdds() {
const ojNflGames = await getNflGames();
const nflGameProps = [];
const nflOddsPush = ( async () => {
try {
await ojNflGames.data.map( async (game) => {
const ojNflOdds = await getNflOdds(game.id)
await ojNflOdds.data[0].odds.map((line) => {
nflGameProps.push(
{
gameId: game.id,
oddsId: line.id,
sports_book_name: line.sports_book_name,
name: line.name,
price: line.price,
checked_date: line.checked_date,
bet_points: line.bet_points,
is_main: line.is_main,
is_live: line.is_live,
market_name: line.market_name,
home_rotation_number: line.home_rotation_number,
away_rotation_number: line.away_rotation_number,
deep_link_url: line.deep_link_url,
player_id: line.player_id,
}
);
});
});
} catch (err) {
console.log("there was an error", err);
}
});
try {
await nflOddsPush();
} catch(err) {
console.log("odds push errored: ", err);
}
console.log("inside of updateNflOdds function: ",nflGameProps);
return nflGameProps;
};

useEffect didnt run

So i have this function that i want to run once when the app start. This function task is to create userId then i will run another function to fetch data from firebase with the userId that created before. But the fetch function didn't start or it didnt do the task well, there is no sign of error, that's what make it more confusing. If i press the fetch function by button it work correctly.
the state
const [task, setTask] = useState(); // bisa di sebut sebagai controller text input
const [taskItems, setTaskItems] = useState([]); // state untuk list task
const [userId, setUserId] = useState();
const [isLoading, setIsLoading] = useState(true);
const baseUrl =
'https://react-http-post-RANDOM_KEY-default-rtdb.firebaseio.com/task/' + userId;
this is function to create userId function on init app
const handleCreateUser = async () => {
setIsLoading(true);
try {
const value = await AsyncStorage.getItem('userId');
if (value !== null) {
setUserId(value);
} else {
const uniqueId = makeid(6);
await AsyncStorage.setItem('userId', 'user' + uniqueId);
setUserId('user' + uniqueId);
}
await fetchDatabase();
} catch (error) {
console.log('errorrr AsyncStorage' + error);
}
setIsLoading(false);
};
this is function to fetch data from firebase
const fetchDatabase = async () => {
console.log('infinite looping');
try {
const response = await fetch(baseUrl + '.json');
if (!response.ok) {
throw new Error('Something went wrong!');
}
const data = await response.json();
// looping Map/Object dengan key sebagai indexnya
const loadedTask = [];
for (var id in data) {
loadedTask.push({
key: id,
text: data[id].text,
isComplete: data[id].isComplete,
});
}
setTaskItems(loadedTask);
} catch (error) {
setError(error.message);
}
};
this is how i call the useEffect
useEffect(() => {
handleCreateUser();
}, []);
The first thing I see is that you are not using await correctly. It should be before fetchDatabase(); function that is inside handleCreateUser like so:
await fetchDatabase();
The word await is there when you have to call an asynchronous function and you have to wait for this function to be completed.
Edit
To use only one useEffect you can check if your fetch function received your data by:
// or whatever statusCode you get when the data are present
if(reponse.statusCode === 200) {
// the await is not needed because it is present for the reponse abov
const data = response.json();
// looping Map/Object dengan key sebagai indexnya
const loadedTask = [];
for (var id in data) {
loadedTask.push({
key: id,
text: data[id].text,
isComplete: data[id].isComplete,
});
}
setTaskItems(loadedTask);
}
i got the answer, by using 2 useEffect
useEffect(() => {
handleCreateUser();
}, []);
useEffect(() => {
fetchDatabase();
}, [userId]);

Trouble updating Doc in Firestore with React

Im trying to edit a document in my firestore db. I get an error i cant figure out.
Uncaught (in promise) FirebaseError: Expected type 'va', but it was: a custom Fh object
Im passing in an Object to the updateDoc function using the spread operator.
const saveEvent = React.useCallback(() => {
console.log(isChecked)
const checked = [];
isChecked.map((item, index) => {
if (item === true) {
checked.push(index + 1)
}
})
console.log(checked)
const newEvent = {
id: tempEvent.id,
title: popupEventTitle,
description: popupEventDescription,
start: popupEventDate[0].toString(),
end: popupEventDate[1].toString(),
allDay: popupEventAllDay,
status: popupEventStatus,
color: selectedColor,
resource: checked
};
if (isEdit) {
// update the event in the list
const index = myEvents.findIndex(x => x.id === tempEvent.id);
const newEventList = [...myEvents];
newEventList.splice(index, 1, newEvent);
console.log(newEventList)
setMyEvents(newEventList);
// ISSUE IS IN THE UpdateEvent function
const UpdateEvent = async () => {
const userRef = collection(database, 'events');
const q = query(userRef, where('id', '==', `${tempEvent.id}`));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(newEvent)
updateDoc(doc, {
...newEvent,
});
})
}
UpdateEvent()
} else {
// add the new event to the list
setMyEvents([...myEvents, newEvent]);
const getEvents = async () => {
try {
const docRef = await addDoc(collection(database, "events"), {
id: tempEvent.id,
title: popupEventTitle,
description: popupEventDescription,
start: new Date(popupEventDate[0]),
end: new Date(popupEventDate[1]),
allDay: popupEventAllDay,
status: popupEventStatus,
color: selectedColor,
resource: checked
});
console.log("Document written with ID: ", docRef.id);
} catch (e) {
console.error("Error adding document: ", e);
}
}
//console.log(newEvent)
getEvents()
}
setSelectedDate(popupEventDate[0]);
setOpen(false);
}, [isEdit, myEvents, popupEventAllDay, popupEventDate, popupEventDescription, popupEventStatus, popupEventTitle, tempEvent, selectedColor, isChecked]);
Im not sure whats wrong, and googling the issue gives me little to work with. I cant find anything about Expected type 'va', but it was: a custom Fh object anywhere. Not even in the documentation..
Any help greatly appreciated.
EDIT:
Ater logging doc.query i noticed a small Va on the top of the document. Also a small "$h" when logging doc Anyone know anything more about that?
Screenshots:
This occurs when you're updating a document with incorrect document reference. You should use ref property to get the document reference to properly update the document on your foreach loop. See snippet below:
const UpdateEvent = async () => {
const userRef = collection(database, 'events');
const q = query(userRef, where('id', '==', `${tempEvent.id}`));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(newEvent)
// Here. You shouldn't use the doc object itself.
// You must use the `reference` property to get the document reference to update to.
updateDoc(doc.ref, {
...newEvent,
});
})
}
UpdateEvent()
For more information, you may checkout this documentation.
// ISSUE IS IN THE UpdateEvent function
const UpdateEvent = async () => {
const userRef = collection(database, 'events');
const q = query(userRef, where('id', '==',${tempEvent.id}));
if you are using 'where' claouse then you will always get one doc
const querySnapshot = await getDocs(q);
const doc = await getDocs(q); try this
querySnapshot.forEach((doc) => {
console.log(newEvent)
updateDoc(doc, {
...newEvent,
});
})
}
UpdateEvent()
Try this
// ISSUE IS IN THE UpdateEvent function
const UpdateEvent = async () => {
const userRef = collection(database, 'events');
const q = query(userRef, where('id', '==', `${tempEvent.id}`));
const doc = await getDocs(q);
//querySnapshot.forEach((doc) => {
console.log(newEvent)
updateDoc(doc, {
...newEvent,
//});
})
}
UpdateEvent()

RedisError: Cannot perform full-text search operations like .match on field of type 'string'

I'm new to Redis and can't find any good docs on querying database for specific value and then sort it.
I'm trying to get data from a database, I'm using NextJs & Redis-om
I tried doing this in my /lib/redis.js
import { Client, Entity, Repository, Schema } from "redis-om";
const client = new Client();
const connect = async () => {
if (!client.isOpen()) {
await client.open(process.env.NEXT_PUBLIC_REDIS_URL);
}
};
class Score extends Entity {}
const schema = new Schema(
Score,
{
address: { type: "string" },
score: { type: "string", textSearch: true },
},
{ dataStructure: "JSON" }
);
export const createScore = async (data) => {
await connect();
const repository = client?.fetchRepository(schema, client);
const score = repository.createEntity();
score.score = data.score;
score.address = data.address;
const id = await repository.save(score);
return id;
};
export const createIndex = async () => {
await connect();
const repository = client.fetchRepository(schema, client);
await repository.createIndex();
};
export const searchScore = async (query) => {
await connect();
const repository = client.fetchRepository(schema, client);
const scores = await repository.search().where("address").matches(query);
return scores;
};
And fetching data as
const router = useRouter();
const data = router.query;
const query = Object.keys(data)[0];
const fetchScores = async () => {
const res = await fetch("/api/search?" + query);
const results = await res.json();
// console.log(results);
// setScore(results[])
};
fetchScores();
To simplify this, I'm passing an address that was meant to check database and if any address is found matching to that query address, return it.
The issue is that you've modeled your address incorrectly.
When you set: address: { type: "string" } you set address up as a TAG field type in RediSearch, which does not allow full-text searches, instead you want to set address to a text type address: { type: "text" } which will support the full-text search capabilities of RediSearch.

Infinite loop inserting docs in firebase after query in react

I am trying to get how many documents there are in the firestore to get a correlative document of the day and then save a new document with React. For some reason when I execute the function to save the new document that causes that firestore to add the new record on an infinite loop. Probably I am doing something wrong. Can someone point me out with the error?
this is my function that saves the new document.
import { db } from '../utils/init-firebase'
import { collection, query, onSnapshot, Timestamp, addDoc, where } from "firebase/firestore"
export interface ProjectState {
projectId: string,
name: string;
}
const saveProject = async (project: ProjectState) => {
try {
const today = Timestamp.fromDate(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate()));
const q = query(collection(db, 'projects'), where('created', '>=', today));
let count = 0
const unsubscribe = onSnapshot(q, (querySnapshot) => {
count = querySnapshot.docs.length + 1;
const date = new Date();
const month = twoDigitDate(date.getMonth() + 1);
const day = twoDigitDate(date.getDate());
const year = date.getFullYear().toString().slice(-2)
const projectId = `${month}${day}${year}${count > 0 ? '-' + count : ''}`;
const projectDoc = collection(db, 'projects');
return addDoc(projectDoc, {
projectId,
name: project.name
created: Timestamp.now(),
});
})
} catch (err) {
console.log('Error creating project:', err)
}
}
const twoDigitDate = (date: number) => {
return date < 10 ? `0${date}` : date;
}
export default saveProject;
You have infinite loop because of onSnapshot() is a listener listen all time collection and when new document appear triggers again with all documents in snapshoot + one extra you added. Insted of onSnapshot() use getDocs() function.
import { collection, query, where, getDocs } from "firebase/firestore";
const q = query(collection(db, "cities"), where("capital", "==", true));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
Your function should lool like this ?
const saveProject = async (project: ProjectState) => {
try {
const today = Timestamp.fromDate(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate()));
const q = query(collection(db, 'projects'), where('created', '>=', today));
let count = 0
const querySnapshot = await getDocs(q)
querySnapshot.forEach(doc => {
// your code what ever you want to do.
})
} catch (err) {
console.log('Error creating project:', err)
}
}
I don't have any clue why you wanna make new docs in same collection according to old documents in this collection. You need to remember using only query you using now you will get max 100 documents.

Resources