kabugh
kabugh

Reputation: 315

Changing a path of an element in Firebase

I would like to change a location of an element in database. I have tried to firstly remove the element from one location, then push it to another. Here's a method:

firebase.database().ref('completed').on('value', (snapshot) => {
  snapshot.forEach((snapchild) => {
    if(snapchild.val().title === item.title && snapchild.val().id === item.id) {
      firebase.database().ref('todos').push(snapchild.val()).then(() => {
        firebase.database().ref(`anotherlocation/${snapchild.key}`).remove()
      }
    }
  })
})

Unfortunately it doesn't work, the element gets deleted but it's not being added to another location. Is it the right way to do it?

Edit: firebase structure added.db

Upvotes: 1

Views: 674

Answers (2)

Frank van Puffelen
Frank van Puffelen

Reputation: 599836

There are a few problems with the code:

  1. You're calling push in the new location, which means the item is added with a new key. You'll probably want the item to have the same key in its new location as it had in its old location.
  2. You're performing two database operations, which means that the first one may succeed while the other fails. By using a single multi-location update you can ensure that either both writes succeeds, or neither of them happens.
  3. You're reading more items than needed. Since you know the id of the item(s) you want to move, you can limit your read operation to that.

In code:

var query = firebase.database().ref('completed').orderByChild('id').equalTo(item.id);
query.on('value', (snapshot) => {
  snapshot.forEach((snapchild) => {
    if(snapchild.val().title === item.title) {
      var updates = {};
      updates['/todos/'+snapchild.key] = snapchild.val();
      updates['/completed/'+snapchild.key] = null;
      firebase.database().ref().update(updates);
    }
  })
})

This moves the completed item with item.id back to todos. I'm not sure if that is the exact move you want to perform, since that wasn't clear from your question. If not, you'll have to update the paths in updates.

Upvotes: 3

Martin Zeitler
Martin Zeitler

Reputation: 76809

I've once wrote a method, which first creates a copy and then removes the source node. this only would need to be ported from Java to JavaScript, which should be a fairly simple task. the order of operations at least is proven to be working. generally, you should replace .on('value') with .once('value'), because there won't be any further events - and the deletion should be caught by a ChildEventListener on the parent node (or however this is called in JavaScript). if wanting to script this real proper, the encapsulation in a transaction would make sense for the given scenario.

/** relocates a record from one to another path (v2). */
protected void move(final DatabaseReference source, final DatabaseReference target) {
    source.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            target.setValue(snapshot.getValue(), new DatabaseReference.CompletionListener() {

                @Override
                public void onComplete(DatabaseError databaseError, @NonNull DatabaseReference databaseReference) {

                    /* if not the operation has failed */
                    if (databaseError != null) {
                        if(mDebug) {Log.w(LOG_TAG, databaseError.getMessage());}
                    } else {

                        /* remove the source path */
                        source.removeValue(new DatabaseReference.CompletionListener(){
                            @Override
                            public void onComplete(DatabaseError databaseError, @NonNull DatabaseReference databaseReference) {
                                if(mDebug) {Log.d(LOG_TAG, "moved " + source.toString() + " to " + target.toString());}
                            }
                        });
                    }
                }
            });
        }
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            if(mDebug) {Log.w(LOG_TAG, databaseError.getMessage());}
        }
    });
}

Upvotes: 1

Related Questions