Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-path update #435

Closed
expleosoftware opened this issue Aug 15, 2016 · 19 comments
Closed

Multi-path update #435

expleosoftware opened this issue Aug 15, 2016 · 19 comments

Comments

@expleosoftware
Copy link

Hi

Is there a way to perform multi-path update (like described here: https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html) using angularfire2 ?

@davideast
Copy link
Member

It works the same way :)

The .update() operator will work the same way as the .update() method on a reference. It really just calls set anyways.

constructor(af: AngularFire) {
  var updatedUserData = {};
  updatedUserData["user/posts/" + newPostKey] = true;
  updatedUserData["posts/" + newPostKey] = {
    title: "New Post",
    content: "Here is my new post!"
  };
  af.database.object('path').update(updatedUserData);
}

@zefrench
Copy link

Thanks David. You save my time !!

@mikeybyker
Copy link
Contributor

@davideast
Is the multi-path update working as expected? I figured it wasn't - it is not updating, it is setting - but I just saw your comment:

"It really just calls set anyways."

So maybe that is the intention with multi-path? To me the name implies it should be an update rather than a set operation...

eg. if I start off with firebase data of:
test/objectA: {a:1, b:2, c: 3, d: 4, e:5}
test/objectB: {w:1, x:2, y: 3, z: 4}

  constructor(af: AngularFire) {
    let update = {};
    update['test/objectA'] = {a:10, e:50};
    update['test/objectB'] = {x:20};
    af.database.object('').update(update);
  }

Then I end up with:
test/objectA: {a:10, e:50}
test/objectB: {x:20}

rather than the expected (if it was an update)
test/objectA: {a:10, b:2, c: 3, d: 4, e:50}
test/objectB: {w:1, x:20, y: 3, z: 4}

@robodair
Copy link

@mikeybyker @davideast I'm experiencing the same thing. Perhaps Firebase 3 doesn't support this?

When I run this and actually allow the write in my database rules

        // Create an object representing our complete changes
        let databaseUpdate = {
            // Add the company change to the update
            companies: company,          // company object is defined elsewhere
            users: {}
        };

        // Build the user object
        let userCompanies = {};
        userCompanies[companyKey] = employeeKey;

        let user = {
            companies: userCompanies
        };

        // Add the user object to the database tree at the user id's location
        databaseUpdate.users[this.auth.auth.uid] = user;

        return this.af.database.object('').update(databaseUpdate);

It just replaces the whole database

@expleosoftware
Copy link
Author

expleosoftware commented Oct 10, 2016

Works for me.
@robodair - Did you try to do it from the root ('/')?
return this.af.database.object('/').update(databaseUpdate);

@robodair
Copy link

I thought I'd tried everything - thanks @expleosoftware! I don't know how I missed that.

@mikeybyker
Copy link
Contributor

@robodair for me, using update still doesn't work as I had in my example:

  constructor(af: AngularFire) {
    let update = {};
    update['test/objectA'] = {a:10, e:50};
    update['test/objectB'] = {x:20};
    af.database.object('').update(update);
  }

That does a set operation - or at least, overwrites the entire object. Any values in the fb database (eg. objectA.b, objectA.c, objectA.d) not specified in the update object are deleted. Which is a shame - lost a lot of data finding that out!

However, a way round it, and maybe how it is meant to work here, is to go further down the path, so to speak:

  constructor(af: AngularFire) {
    let update = {};
    update['test/objectA/a'] = 10;
    update['test/objectA/e'] = 50;
    update['test/objectB/x'] = 20;
    af.database.object('').update(update);
  }

So update['test/objectA/a'] = 10 rather than update['test/objectA'] = {a:10};
That way, the other values remain intact. Perhaps not quite what your issue was, but maybe helpful to someone trying to update this way.

@mikeybyker
Copy link
Contributor

...although if you look at how firebase themselves say it should work it doesn't appear to be working the same way...

Using the pre-existing atomic update support, we can change the user's age and add a city without updating their name

    {
      "user": {
        "name": "Samantha",
        "age": 25
      }
    }
    var userRef = ref.child("user");
    var newUserData = {
      "age": 30,
      "city": "Provo, UT"
    };
    ref.child("user").update(newUserData);

So, still not sure if an angularfire bug or not...Maybe @davideast could say?

@robodair
Copy link

@mikeybyker

try

  constructor(af: AngularFire) {
    let update = {};
    update['test/objectA'] = {a:10, e:50};
    update['test/objectB'] = {x:20};
    af.database.object('/').update(update);
  }

the / in

af.database.object('/').update(update);

is what made it work for me

@mikeybyker
Copy link
Contributor

@robodair
Cheers - I did try that, and still no go - it still sets the data rather than updates it.

Normal (single) updates work fine - i.e.

this.af.database.object('test/objectA').update({ a: 99 });

Updates 'a' and leaves b,c,d,e alone.

Have a look: https://mikeybyker.github.io/multi-location-updates/
Or maybe download: https://github.com/mikeybyker/multi-location-updates

@robodair
Copy link

robodair commented Oct 11, 2016

@mikeybyker you're right. This is hella confusing. It just so happens that for my application there's nothing that exists at said locations so I didn't notice.

Thanks for the test repo, I made a fork and submitted a pull request with explanations.

Basically update() performs a set() on every immediate child of the update object, no more. Reading the docs after this realisation it all seems clear. Before however, it wasn't. Very confusing.

@mikeybyker
Copy link
Contributor

No problem :-) Yep, a bit confusing is right!

@mikeybyker
Copy link
Contributor

@wceolin - it does perform a set() yup. To me it would be a bug, and it doesn't fit with what firebase have said about how it should work (see quote & link above) - but the description used by @davideast at the top of this issue ("It really just calls set anyways.") sounds like they don't think it is... Which doesn't help much - sorry!

@davideast
Copy link
Member

@wceolin @mikeybyker

It performs an update at the first level, but everything below is treated as a set. To get the effects of an update you'll have to go to the deepest level of the path.

Rather than:

let update = {};
update['test/objectA'] = {a:10, e:50};
update['test/objectB'] = {x:20};

Do this:

let update = {};
update['test/objectA/a'] = 10;
update['test/objectB/x'] = 20;

By specifying deep path level it will only do an update for a and x.

@robodair
Copy link

My guess is that it would be too expensive of an operation on the firebase
end if you were updating a big tree.
On Wed., 16 Nov. 2016 at 06:25, Will Ceolin notifications@github.com
wrote:

@davideast https://github.com/davideast is there a technical limitation
for updating the first level only and not the whole tree? Or is it just a
design choice?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#435 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AH7tF0_beVUTheTkWDtYOdYYTr4ueGUJks5q-gcUgaJpZM4Jkfp1
.

@rubenheymans
Copy link

how do you do this in angularfire2 4.0?

@Unkn0wn0x
Copy link

I hope my solution could be useful for someone:

/** define first path */
var updateUserPath = '/users/' + firebase.auth().currentUser.uid + '/';
/** some data which should be updated via a multi-path update to the first defined path */
var updateUser = {
    'username': 'John Doe',
    'firstname': 'John',
    'lastname': 'Doe',
};

/** define second path */
var updateUserGroupPath = '/groups/' + firebase.auth().currentUser.uid + '/';
/** some data which should be updated via a multi-path update to the second defined path */
var updateUserGroup = {
    'group': 'Firebase',
    'description': 'The awesome firebase group!'
};

/** create new object for storing the multi-path updates */
var updateData = {};

/** loop through the object updateUser and push for each property a new property into updateData with the defined path including the key of the updateUser */
for (var key in updateUser) {
    updateData[updateUserPath + key] = updateUser[key];
};

/** same here for the updateUserGroup */
for (var key in updateUserGroup) {
    updateData[updateUserGroupPath + key] = updateUserGroup[key];
}

/**
 * this console.log() will show a object like this:
 * {
 * "/users/1234567890/username": "John Doe",
 * "/users/1234567890/firstname": "John",
 * "/users/1234567890/lastname": "Doe",
 * "/groups/1234567890/group": "Firebase",
 * "/groups/1234567890/description": "The awesome firebase group!"
 * }
 */
console.log(updateData);

/** update your data without touching anything else */
firebase.database().ref().update(updateData);

It works fine for me.

@paean99
Copy link

paean99 commented Dec 2, 2017

Here is a video about this feature on the Firebase youtube channel:
Data consistency with Multi-path updates - The Firebase Database For SQL Developers #7

This post gives a little more explanations:
Client-side fan-out for data consistency

@MohitKS5
Copy link

If I give a type to document reference, then running update on it with multi-path gives type error. Any idea how to solve that?

export interface Personal{
  tel: number
}
export interface LocalUser{
  personal: Personal
}
public userRef = (id: string): AngularFirestoreDocument<LocalUser> => this.afs.doc(`users/${id}`);
this.usrRef.update({"personal/tel": 237486238746})

this gives ERRORArgument of type '{ "personal/tel": any; }' is not assignable to parameter of type 'Partial<LocalUser>'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants