How to implement transaction in Loopback 4 - loopback

I am making two repository calls to update the data in two different tables in db. I want to implement a transaction. Need help on how to perform this.
https://loopback.io/doc/en/lb4/Using-database-transactions.html
From the documentation it looks like transaction can be performed in only one repository not between two repositories.
const created = await repo.create({title: 'Groceries'}, {transaction: tx});
const updated = await repo.update(
{title: 'Errands', id: created.id},
{transaction: tx},
);
// commit the transaction to persist the changes
await tx.commit();
But I want
const created = await repo1.create({title: 'Groceries'}, {transaction: tx});
const updated = await repo2.update(
{title: 'Errands', id: created.id},
{transaction: tx},
);
// commit the transaction to persist the changes
await tx.commit();
Anyone has any idea how to do this.

const created = await repo1.create({title: 'Groceries'}, {transaction: tx});
const updated = await repo2.update( {title: 'Errands', id: created.id},{transaction: tx}, );
// commit the transaction to persist the changes await tx.commit();
Yes it is possible, if your repo1 and repo2 belongs to same datasource.
more information here

Related

update single record among multiple records by UserId in Back4App flutter

I am trying to update records in my Back4App database. The problem is that ..objectId = currentUser.objectId takes only the userid and there can be multiple records for the same id. Thus, I am unable to update a single record of that userid. I am trying to filter by te unique id of records "concatid" but nothing gets updated.
Future<void> updateTodo(String description, String dogName) async {
ParseUser? currentUser = await ParseUser.currentUser() as ParseUser?;
var concatid = currentUser!.objectId! + dogName;
await Future.delayed(Duration(seconds: 1), () {});
final todo = ParseObject('Todo')
..objectId = currentUser.objectId
..set('concatId', concatid)
..set('DogDescription', description);
await todo.save();
}
Here's the documentation about update methods, but it only takes into account a general approach where you can only set new data matching by objectId and assuming this is unique (which is not my case).

How to add new data to firebase

How to add new data to firebase, in the picture on the second column there are users, and on the last one there is my note. This note whas created when user created account, and it whas updated when user logged in, before whas "bad location" etc. My problem is to add new note like this, not update it, kepp it, and at the same time, in the same column have some kind of "new collection" with the same 3 strings, but with different data.
class DataService {
final String uid;
DataService({required this.uid});
final CollectionReference notesCollection =
FirebaseFirestore.instance.collection('Notes');
Future createUserData(String notes, String localisation, String title) async {
return await notesCollection.doc(uid).set({
'notes': notes,
'title': title,
'localisation': localisation,
});
}
Future addData(String notes, String localisation, String title) async {
return await notesCollection.doc(uid).set({
'notes': notes,
'title': title,
'localisation': localisation,
});
}
}
This class shows my createUserData, when my user creates account or loggs in, but how to change "addData" in order to have logic as I described above?
if I understand correctly you want to create a new collection for each documents as history. try this:
notesCollection.doc(uid).collection("historic").add({
'notes': notes,
'title': title,
'localisation': localisation,});

Query child model with mongoose

I'm updating the mongoose version from 5 to 6 and found a change in the behavior.
This is a small example:
We have two models
const MessageSchema = new mongoose.Schema({ text: String });
const Message = mongoose.model('Message', MessageSchema);
const Notification = Message.discriminator(
'Notification',
new mongoose.Schema({important: Boolean})
);
And let's say, we create two documents:
const m = new Message({text: 'Some text 1'});
await m.save();
const n = new Notification({important: true, text: 'Some text'});
await n.save();
And we run this query:
await Message.find({important: true})
In mongoose v5 the query will use important in spite of it not being declared in the Message schema. And the result will be just one document.
However, in the new version, it will ignore important if you query Messages, and will return all the documents, because without important query is just {}.
My question is, how can I achieve the same behavior with the new version?
I know that I could use the Notification model instead of Messages, but image if there were several models like Notification (children of Message), that had important in them, and I wanted to search in all of them.

Designing many to many model with map

I am new to firestore and am wondering if anyone could tell me whether this solution is viable for a many-to-many relationship. I have a collection of Rosters and collection of Students which are related Many-to-Many. As the information I most frequently need about a student is just their name, would it be viable to have a map of students like {<StudentID> : "Student Name"} stored in rosters, and so if I want to retrieve more detailed information about students in a roster, I retrieve the map's keys and iterate through them to retrieve each student's document individually?
I am basing my solution off of this answer.
I'd greatly appreciate any advice! Thank you
Update to this, it is working fine. Here is my code for the cloud function to update athlete names if anyone in the future needs:
export const onUserUpdate =
functions.firestore.document("users/{user}/athletes/{athlete}").onUpdate(
async (change) => {
const after = change.after.data();
const before = change.before.data();
const bid = change.before.id;
console.log("BID: ");
console.log(bid);
const userId: any = change.before.ref.parent.parent?.id;
console.log(`users/${userId}/rosters`);
if (after.athleteName != before.athleteName) {
console.log("Change name detected");
const snapshot =
await db.collection(
`users/${userId}/rosters`).where(
`athletes.${bid}`, ">=", "").get();
const updatePromises : Array<Promise<any>> = [];
snapshot.forEach((doc) => {
console.log(doc.id);
updatePromises.push(db.collection(`users/${userId}/rosters`)
.doc(doc.id).update(`athletes.${bid}`, after.athleteName)
);
});
await Promise.all(updatePromises);
}
});

Firestore - batch.add is not a function

The documentation for Firestore batch writes lists only set(), update() and delete() as permitted operations.
Is there no way to add an add() operation to the batch? I need a document to be created with an auto-generated id.
You can do this in two steps:
// Create a ref with auto-generated ID
var newCityRef = db.collection('cities').doc();
// ...
// Add it in the batch
batch.set(newCityRef, { name: 'New York City' });
// Commit at the end
await batch.commit();
The .doc() method does not write anything to the network or disk, it just makes a reference with an auto-generated ID you can use later.
In my case, using AngularFire2, I had to use the batch.set() method, passing as first parameter the document reference with an ID previously created, and the reference attribute:
import { AngularFirestore } from '#angular/fire/firestore';
...
private afs: AngularFirestore
...
batch.set(
this.afs.collection('estados').doc(this.afs.createId()).ref,
er.getData()
);
I'll offer an answer for Firebase 9 in which the syntax differs from Firebase 8.
For Firebase 9, the equivalent of add() is addDoc() as explained at https://firebase.google.com/docs/firestore/manage-data/add-data#web-version-9_6 . It is for when you're not using batch nor transaction. As per the original problem posted, there is no equivalent of addDoc() on batch nor transaction on Firebase 9 either.
I found a way to achieve the equivalent of addDoc() for a batch on Firebase 9 by following the answer https://stackoverflow.com/a/69859144/2848676 as follows:
const batch = writeBatch(db);
const docADocRef = doc(collection(db, "DocA"));
batch.set(docADocRef, {
fieldA: "This is field of an instance of DocA"
});
const docBDocRef = doc(collection(db, "DocB"));
batch.set(docBDocRef, {
docAID: docADocRef.id
});
batch.commit();
In this example, instances of DocA and DocB are created and DocB receives a pointers to the DocA instance.
According to the docs
Behind the scenes, .add(...) and .doc().set(...) are completely equivalent, so you can use whichever is more convenient.
Perhaps this applies to batches as well?
For PHP you can try :
$batch = $db->batch();
$newCityRef = $db->collection('cities')->newDocument();
$batch->set($newCityRef , [ 'name'=>'New York City' ]);
To create a document with auto-generated ID with firestore batch, you cannot use the addDoc(). You have to use batch.set() with a reference to the document to be created as below
const db = getFirestore();
// Create a transaction to update both the product stock value and add the new stock data
const batch = writeBatch(db);
const prodRef = doc(db, `products/${productId}`);
const stockRef = doc(collection(db, `stocks`);
// newDocId = stockRef.id;
batch.set(stockRef, stock, {merge: true}); //create new document with autoId
batch.update(prodRef, {available : increment(quantity), stock: increment(quantity)});
batch.commit()
Create the reference to the collection in which you are going to add the batch data
We loop over the req.body using forEach and set the each data to be added in to the collection using the set method
We commit the data and save the data to the collection using the commit method and on success ,send a success response.
cloud firestore
Lets assume that you have list of cities and you want to write them in batch.
final CityList = FirebaseFirestore.instance.collection('cities')
WriteBatch batch = FirebaseFirestore.instance.batch();
for(CityList city in cities) {
final newShoppingItem = ShoppingList.doc();
batch.set(newShoppingItem, {
'name': city.name,
'createdAt': DateTime
.now()
.millisecondsSinceEpoch
});
}
batch.commit();
Sam Stern's answer is the correct way to do it, although if you are using AngularFire, .doc() cannot be used withouth a parameter to generate a new docId (see https://github.com/angular/angularfire/issues/1974).
The AngularFire way of doing this would be:
// Create a ref with auto-generated ID
const id = this.db.createId();
const newCityRef= this.db.collection("cities").doc(id);
// ...
// Add it in the batch
batch.set(newCityRef, { name: 'New York City' });
This worked for me and it is mentioned in the docs for PHP
$batch = $db->batch();
# Set the data for NYC
$nycRef = $db->collection('samples/php/cities')->document('NYC');
$batch->set($nycRef, [
'name' => 'New York City'
]);
# Update the population for SF
$sfRef = $db->collection('samples/php/cities')->document('SF');
$batch->update($sfRef, [
['path' => 'population', 'value' => 1000000]
]);
# Delete LA
$laRef = $db->collection('samples/php/cities')->document('LA');
$batch->delete($laRef);
# Commit the batch
$batch->commit();