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

setInstanceProperty fails comparison on objects or custom types with objects #517

Closed
kapouer opened this issue Jun 3, 2014 · 6 comments
Closed

Comments

@kapouer
Copy link
Contributor

kapouer commented Jun 3, 2014

Hi,
the comparison if (opts.data[key] !== value) { at https://github.com/dresende/node-orm2/blob/38e76722/lib/Instance.js#L453 will never be true when modifying a property of a column that is an object. The horrible workaround is:

var props = source.prop;
props.file = filename;
source.prop = null; // or anything that is not props
source.prop = props;

i suggest to improve the comparison when the value is an object:

if (opts.data[key] !== value ||
    (typeof value == "object" &&
     propertyToValue(value) !== propertyToValue(opts.data[key])
    )
   ) {../..}

so a thorough comparison is done in case of equality of objects.

@dxg
Copy link
Collaborator

dxg commented Jun 4, 2014

This problem is harder to solve than it seems.
I tried the above as well as a few other things, and nothing worked.

var Place = db.define('place', {
  position: { type: 'object' }
});

...

Place.create({ position: { x: 2, y: 3 } }, ...);

Place.get(1, function (err, item) {
  console.log(item.saved()); // "true"
  item.position.x = 5;
  console.log(item.saved()); // "true"
})

.save() will only persist to the database if saved() returns false.

Unfortunately, in the above example when we set x to 5, the code on line 453 isn't called.
And even if it was (eg, by doing p = item.position; item.position = null; item.position = p) the comparison still wouldn't return true because value points to the same object in memory as opts.data[key] so we'd be comparing it to itself.

The solutions I can see are:

  1. Always assume object properties have changed
  2. When saving, fetch all Object properties from the database and compare to what we have
  3. Keep a shadow copy of all instance data and do a comparison when calling save()
  4. Allow something like item.save({ force: true }, ...) and maybe a global setting for this
  5. Add item.markAsDirty('position')

Number 1 goes against the principle of least surprise.
Number 2 would result in extra queries, negatively impacting speed.
Number 3 would double memory usage - problematic if a property is storing a Buffer() of a large file.
Number 4 & 5 is rather manual.

I'm leaning towards number 4 and/or 5 but it's not exactly a nice solution..

@kapouer
Copy link
Contributor Author

kapouer commented Jun 4, 2014

  1. item.set('position.x', 5)
    (or variants)

@dxg
Copy link
Collaborator

dxg commented Jun 4, 2014

That sounds better than my ideas.

dxg added a commit that referenced this issue Jun 5, 2014
@dxg
Copy link
Collaborator

dxg commented Jun 5, 2014

We now have:

item.set('position.x', 2)
item.set(['position','x'], 2) // in case you have objects like { "a.b" : 4  }
item.markAsDirty('position')

set() will only mark the object as dirty if you are changing the value. If x was 2 and you set it to 2, it won't affect dirty state.

Published as version 2.1.15.

@dxg dxg closed this as completed Jun 5, 2014
@kapouer
Copy link
Contributor Author

kapouer commented Jun 5, 2014

that's wonderful, thank you

@kapouer
Copy link
Contributor Author

kapouer commented Sep 10, 2014

The only missing thing now is a mention of that in the docs...

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

2 participants