I want to perform a batch transaction in firestore. I am storing last key in other collection.
i need to get the last key then increase by 1, then create two documents using this key. How can i do this?
let lastDealKeyRef = this.db.collection('counters').doc('dealCounter')
let dealsRef = this.db.collection('deals').doc(id)
let lastDealKey = batch.get(lastDealKeyRef) // here is the problem..
batch.set(dealsRef, dealData)
let contentRef = this.db.collection('contents').doc('deal' + id)
batch.set(contentRef, {'html': '<p>Hello World</p>' + lastDealKey })
batch.commit().then(function () {
console.log('done') })
If you want to read/write data in a single operation you should be using a transaction.
// Set up all references
let lastDealKeyRef = this.db.collection('counters').doc('dealCounter');
let dealsRef = this.db.collection('deals').doc(id);
let contentRef = this.db.collection('contents').doc('deal' + id);
// Begin a transaction
db.runTransaction(function(transaction) {
// Get the data you want to read
return transaction.get(lastDealKeyRef).then(function(lastDealDoc) {
let lastDealData = lastDealDoc.data();
// Set all data
let setDeals = transaction.set(dealsRef, dealData);
let setContent = transaction.set(contentRef, {'html': '<p>Hello World</p>' + lastDealKey });
// Return a promise
return Promise.all([setDeals, setContent]);
});
}).then(function() {
console.log("Transaction success.");
}).catch(function(err) {
console.error("Transaction failure: " + err);
});
You can read more about transactions and batches here:
https://firebase.google.com/docs/firestore/manage-data/transactions
Related
For example, I can remove document x from collection x, then remove document y from collection y, but if something goes wrong, rollback everything. Based on the documentation, DocumentationReference.delete() is basically the only way to delete a document.
There is a Transaction.delete(DocumentReference) method to delete a document in a transaction.
So to transactionally delete all cities with a population < 100K (modified from the example in the documentation):
var citiesRef = db.collection("cities");
db.runTransaction(function(transaction) {
var count = 0;
return transaction.get(citiesRef).then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
if (doc.data().population <= 1000000) {
transaction.delete(doc.ref);
count = count + 1;
}
});
});
}).then(function(count) {
console.log("Deleted cities ", count);
}).catch(function(err) {
console.error(err);
});
I use the batch method, as in:
let batch = db.batch()
batch.deleteDocument(documentXref)
batch.deleteDocument(documentYref)
batch.commit() { error in
if let error = error {
print(error.localizedDescription)
}
}
Note that this works even when offline. Thus if you want the operation to fail in that case, you should use the transaction approach.
I'm trying to remove an entry from my Firebase database, but am not able to delete it.
Here's my database structure:
Here's what I've tried:
let subRefs = firebase.database().ref('subscriptions/' + userId).once('value').then(function(snapshot){
let objs = snapshot.val();
for (let key in objs){
if (objs[key].uid == memberId){
console.log('found'); // I see this in the console
//remove the subscription
let ref = firebase.database().ref('subscription/' + userId + '/' + key);
ref.remove().then(function() {
console.log("Remove succeeded");
}).catch(function(error) {
console.log("Remove failed");
});
}
}
});
I've also tried the following approach as suggested here in the docs, but that didn't work either.
let update = {};
update['subscription/' + userId + '/' + key] = null;
firebase.database().ref().update(update).then(function(){
console.log('remove success');
}).catch(function(err){
console.log('remove failed');
});
In both cases, I see the "remove success" log, but when I check the database, it's not actually deleted.
The following code executed when something is written at a certain location in the database.
What I expect from this code : If the number of coins are => 500 to subtract only once 500 coins from the current coins value and to add one ticket to the existing ticket value.
What I am getting in reality: The code recursively subtracts 500 coins until the coin value is lower than 500; It adds more tickets then it should.
Please , can somebody modify my code to work as expected ?
I do not know what I am doing wrong
exports.TransformCoinsIntoTickets1 = functions.database.ref('/AddTickets/{userid}').onWrite(event => {
var user = event.params.userid;
/// first get coins to see if we can lower coin value
var usercoinRef1 = admin.database().ref('coins').child(user);
usercoinRef1.on("value", function(snapshot) {
var numberofcoins = snapshot.val();
console.log("We are in snapshot of coins -> He has coins = " + numberofcoins);
if (numberofcoins >= 500 )
return usercoinRef1.set(numberofcoins-500).then(() => {
var ticketRef1 = admin.database().ref('tickets').child(user);
ticketRef1.on("value", function(snap123) {
var numberoftickets = snap123.val();
return ticketRef1.set(numberoftickets+1).then(() => {
console.log('Tickets Write succeeded!');
});
}, function (error) {
console.log("Error Tickets: " + error.code);
});
console.log('Coins Write succeeded!');
});
}, function (error) {
console.log("Error Coins: " + error.code);
});
//then we write the new coin value if we need to
});
I just realized that instead of
on
i should use
once
So, replacing
.on("value",
with
.once("value",
resolved the problem.
I use Ionic 2 with Angular 2 in my project. In the root component you can click a "Add" button to add a new Report via a complex form and a lot of preprovided data (there are some selects that are feeded with data fetched from sqlite database)
Now in my "CreateReportComponent" i have the following constructor to load the data and assign it to local array variable:
selectEmployeeOptions: Employee[];
constructor(private dbService: DatabaseService) {
dbService.getAllEmployees().then(employees => {
this.selectEmployeeOptions = employees;
});
// load more data like tasks etc.
});
But when I want to modify this data in my component, the array is empty. I tried to do it in ngOnInit() but this seems to be to early as well.
I want to to something like this, before the component gets displayed:
dbService.getAllEmployees().then(employees => {
this.selectEmployeeOptions = employees;
// modify data
this.selectEmployeeTitleOptions = employees.map((item) => {
return item.title;
});
console.log(JSON.stringify(this.selectEmployeeTitleOptions)) // --> empty
});
But selectEmployeeTitleOptions is empty...
The function in the databaseService looks like this:
getAllEmployees(): Promise<Emplyoee[]> {
let query = "SELECT * FROM employees";
let employeeList = [];
this.database.executeSql(query, []).then((data) => {
if(data.rows.length > 0) {
let e = new Employee();
e.id = data.rows.item(i).id;
e.firstname = data.rows.item(i).firstname;
e.lastname = data.rows.item(i).lastname;
employeeList.push(e);
}
}, (error) => {
// handle error
});
return Promise.resolve(employeeList);
}
I read that there is the Resolve pattern (https://blog.thoughtram.io/angular/2016/10/10/resolving-route-data-in-angular-2.html) But I need to make multiple calls and not only for contacts as in the example.
So the question: How to wait for multiple calls to database?
i think something go wrong here
getAllEmployees(): Promise<Emplyoee[]> {
let query = "SELECT * FROM employees";
let employeeList = [];
this.database.executeSql(query, []).then((data) => {
if(data.rows.length > 0) {
let e = new Employee();
e.id = data.rows.item(i).id;
e.firstname = data.rows.item(i).firstname;
e.lastname = data.rows.item(i).lastname;
employeeList.push(e);
}
}, (error) => {
// handle error
});
return Promise.resolve(employeeList);
}
first return Promise.resolve(employeeList); will return empty array, because it is async process.
you need loop through data.rows, then format return data like this.
getAllEmployees(): Promise<Employee[]> {
let query = "SELECT * FROM employees";
return this.database.executeSql(query, []).then((data) => {
let arr = [];
for(let i = ; i < data.rows.length; ++i) {
let emp = data.rows.item(i);
let e = new Employee();
e.id = emp.id;
e.firstname = emp.firstname;
e.lastname = emp.lastname;
arr.push(e);
}
return arr;
});
}
note that .then() return a promise object.
What you are looking for is forkJoin method that returns Observable that you should switch to instead of using Promises, for reference about why you should do this check here.
Short information about fork join from its GitHub page:
Runs all observable sequences in parallel and collect their last elements.
This way you can safely make parallel requests to your API.
For more information regarding forkJoin go here.
Additionally you should call services using ngOnInit as you mentioned before. For more information about Angular 2 lifecycle hooks see the docs.
You can use Promise.all
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
You push all promises to an array, and then go
let foo : [Promise<Emplyoee[]>,Promise<void>] = [getAllEmployees(), method2()];
Promise.all(foo).then((results:any[]) => {
let employeearray: any = results[0];
/* and on an on */
});
I have this firebase:
users: {
userId: {
notifications: {
notificationId: "Notification"
}
}
}
When given "Notification", I'm trying to find its notificationId (which is generated from the push() method) so I can eventually delete it. According to the docs, the $indexFor() method should do this for me. Here's my code:
var ref = new Firebase('https://url.firebaseio.com/');
$scope.dismissNotification = function(notification) {
var notificationRef = ref.child('users/' + $scope.currentUser.id + '/notifications');
var notifications = $firebaseArray(notificationRef);
notifications.$loaded().then(function(data) {
console.log(data);
console.log(data.$indexFor(notification));
}).catch(function(error) {
console.log('Error: ' + error);
});
};
The first log is the correct object with the notification string inside that I'm looking for, but the second log returns -1, when I want it to return the notificationId associated with it.
Not sure what you're trying to accomplish, but this is the simplest way to find the key for a given value:
var notificationRef = ref.child('users/' + $scope.currentUser.id + '/notifications');
var query = notificationRef.orderByValue().equalTo(notification);
query.once('child_added', function(snapshot) {
console.log(snapshot.key());
});