Is it possible to rename a key in the Firebase Realtime Database?

I was wondering, is there a way to update the key value?

Let´s use the following data:

my data

I am using set() to write the data. Now, I want the user to edit their bookTitle and it needs to change on both places. I tried using update() but I can´t seem to make it work. I can only edit the bookTitle in bookInfo NOT on books.

Moving is not an option because it will erase the bookData. I also tried writing using push() but then, I can´t search properly because I don´t have the pushID (I need the search because users can't have two books with the same name)

So, is there a way to update the key value? or, is there a better approach to this? I accept suggestions. Thank you!

Update: This is what I´m currently using to update the book title inside bookInfo

var bookName = document.getElementById('bookName').value;

firebase.database().ref('books/' + bookName + '/bookInfo').update({
    bookTitle : bookName
});

Solution 1:

I think I see what you're trying to do. Firebase doesn't have the concept of "renaming" a part of the path via update. Instead you will have to completely remove the existing node and recreate it. You can do that like so:

var booksRef = firebase.database().ref('books');
booksRef.child(oldTitle).once('value').then(function(snap) {
  var data = snap.val();
  data.bookInfo.bookTitle = newTitle;
  var update = {};
  update[oldTitle] = null;
  update[newTitle] = data;
  return booksRef.update(update);
});

This will remove the info from books/oldTitle and re-populate it with a new title in books/newTitle.

Caveat: This relies on reading the data and then performing a second async update. If you are likely to have multiple users operating on the same data at the same time this could cause issues. You could use a transaction to do this atomically but if /books is a top-level resource with many nodes that may cause performance problems.

If one person is likely to edit the data at a time, the above solution is fine. If not, you may want to consider using a non-user-controlled identifier such as a push id.

Solution 2:

You can export all the database as JSON, rename it in your favorite editor import it again to firebase via import/export option on the top right in the console windowenter image description here

Solution 3:

I would still use push(), that's really the proper way to store multiple child objects under category-node in firebase I think. For searching, have you thought about doing this on the client side? For example you can save all your book objects under books node, and retrieve those to search locally when you need to.

Also as ZenPylon mentioned, push() gives you a unique id that you can also assign as a prop to each book object so each book has a reference to where it is stored in the database.

Solution 4:

One thing worth noting is that you can grab the unique key from the push() function.

var booksRef = firebaseRef.database().ref('books');
var newBookRef = booksRef.push(myBookData);
var bookKey = newBookRef.key;

Source: Firebase Docs - push()

I can't tell from your screenshot where the second instance of bookTitle is (is it the root element in the screenshot?), but if you don't want to use push(), you could grab the data at that location, remove(), and then call set() again, this time with the updated book title. Something like:

var bookRef = firebaseRef.database().ref('path/to/books/book1');

bookRef.on('value', function(snapshot) {
    var bookData = snapshot.val();
    var newData = {};
    var newTitle = 'NewTitle';

    bookData.bookInfo.bookTitle = newTitle;
    newData[newTitle] = bookData;
    firebaseRef.database().ref('path/to/books/' + newTitle).set(newData);

});

Solution 5:

I liked a lot Michael Bleigh's answer BUT...

It was restricting me the scope of deepness when moving keys from one level to another, so I better made this function which even allows you to duplicate (0) or move (1) the complete key by simply changing the third argument:

function mvFBkey(rtOrgn,rtDstn,rem){
  firebase.database().ref(rtOrgn).once('value').then(function(snap) {
    return firebase.database().ref(rtDstn).set(snap.val(),function(){
        if(rem)firebase.database().ref(rtOrgn).set(null);
    });
  });
}