Thomas David Kehoe
Thomas David Kehoe

Reputation: 10930

Firebase update() not a function with a query

I'm trying to update a property in a record in Firebase Database, with AngularJS. I can set up a query to find my record:

  firebase.database().ref('en/').orderByChild('word').equalTo('the').once('value')
  .then(function(snapshot) {
    snapshot.forEach(function(childSnapshot) {
      console.log(childSnapshot.val())
    });
  })

I can update my property, if I hardcode in the record's key:

firebase.database().ref('en/-KloeQHDC-mugPjJMAG4').update({ wordFrequency: 111 })

But if I set up a query to find the record and then update it, I get an error message update is not a function:

firebase.database().ref('en/').orderByChild('word').equalTo('the').update({ wordFrequency: 9001 })

Another answer suggests calling update() from inside a forEach loop:

  firebase.database().ref('en/').orderByChild('word').equalTo('the').once('value')
  .then(function(snapshot) {
    snapshot.forEach(function(childSnapshot) {
      console.log(childSnapshot.val()); // this works
      childSnapshot.ref().update({ wordFrequency: 9001 });
    });
  });

That returns an error message TypeError: childSnapshot.ref is not a function. I don't see how childSnapshot is a Firebase ref.

Another answer says

When you call update() on a location, Firebase loops over the data that you pass in (in your case asJson) and for each key performs a ref.child(key).set(value).

If update() loops over the data, why should I call update() from inside a forEach loop? The documentation doesn't show calling update() from inside a forEach loop.

Upvotes: 1

Views: 4862

Answers (2)

Thomas David Kehoe
Thomas David Kehoe

Reputation: 10930

After I posted this question I walked the dog, ate dinner, and then the solution came to me. My new rule is, "The key to Firebase queries is to keep track of the key."

This template is for users to update records in the database. They enter a search term in a form field and click the "Search" button. The $scope.search handler queries the Firebase database and then populates the form fields with the record's properties:

  $scope.search = function() {
    myFirebase_ref.orderByChild('word').equalTo($scope.word).once('value')
    .then(function(snapshot) {
    snapshot.forEach(function(childSnapshot) {
      $scope.wordKey = childSnapshot.key;
      $scope.audioArray = childSnapshot.val().audio;
      $scope.ipaArray = childSnapshot.val().ipa;
      $scope.language = childSnapshot.val().language;
      $scope.longLanguage = childSnapshot.val().longLanguage;
      $scope.phonemeArray = childSnapshot.val().phonemes;
      $scope.translationArray = childSnapshot.val().translations;
      $scope.word = childSnapshot.val().word;
      $scope.wordFrequency = childSnapshot.val().wordFrequency;
      $scope.$apply();
    });
  })
  .catch(function(error) {
    console.error("Authentication failed:", error.message);
  });
};

Note at the top of the property assignments I have $scope.wordKey = childSnapshot.key;. I'm keeping track of the record's key.

The user then updates a field. Each field has a button next to it for "Update". Each button goes to a handler. For example, to update the wordFrequency field I have this handler:

$scope.updateFrequencyRank = function() {
  firebase.database().ref('en/' + $scope.wordKey).update({ wordFrequency: $scope.wordFrequency })
};

One line of code and it works! Even better, I made an onComplete function to tell me if the update succeeded:

$scope.updateFrequencyRank = function() {

  var onComplete = function(error) {
    if (error) {
      console.log('Update failed');
    } else {
      console.log('Update succeeded');
    }
  };

  firebase.database().ref('en/' + $scope.wordKey).update({ wordFrequency: $scope.wordFrequency }, onComplete);
};

Upvotes: 1

Frank van Puffelen
Frank van Puffelen

Reputation: 598708

The Firebase Database SDK provides a Reference.update() method to update data in a single location in a database. Key here is that a Reference is a single location in the database, so it is clear what to update.

My pseudo-code explanation about how multi-path updates work applies to how the database server implements it: given a single location/DatabaseReference it updates each path in the update() call based on that.

A Query can match multiple locations in the database, so it doesn't have an update() method (or set() or remove() for that matter).

To update each location matched by a query, you execute the query and then call update() on each result - either by a child_added listener, or with a value listener and a loop like in your last snippet.

Upvotes: 3

Related Questions