Skip to content

Commit

Permalink
Optimise has many notifications (#4850)
Browse files Browse the repository at this point in the history
* optimise has-many notifications

* bring in line with master

* bring in line with master

* exit flushCanonical early if not alive

* add test for non contiguous change

* niggles

* jshint fix
  • Loading branch information
BryanCrotaz authored and stefanpenner committed Mar 10, 2017
1 parent c53b8f8 commit 03440c1
Show file tree
Hide file tree
Showing 4 changed files with 1,417 additions and 21 deletions.
58 changes: 58 additions & 0 deletions addon/-private/system/diff-array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
@namespace
@method diff-array
@for DS
@param {Array} oldArray the old array
@param {Array} newArray the new array
@return {hash} {
firstChangeIndex: <integer>, // null if no change
addedCount: <integer>, // 0 if no change
removedCount: <integer> // 0 if no change
}
*/
export default function diffArray(oldArray, newArray) {
const oldLength = oldArray.length;
const newLength = newArray.length;

const shortestLength = Math.min(oldLength, newLength);
let firstChangeIndex = null; // null signifies no changes

// find the first change
for (let i=0; i<shortestLength; i++) {
// compare each item in the array
if (oldArray[i] !== newArray[i]) {
firstChangeIndex = i;
break;
}
}

if (firstChangeIndex === null && newLength !== oldLength) {
// no change found in the overlapping block
// and array lengths differ,
// so change starts at end of overlap
firstChangeIndex = shortestLength;
}

let addedCount = 0;
let removedCount = 0;
if (firstChangeIndex !== null) {
// we found a change, find the end of the change
let unchangedEndBlockLength = shortestLength - firstChangeIndex;
// walk back from the end of both arrays until we find a change
for (let i=1; i<=shortestLength; i++) {
// compare each item in the array
if (oldArray[oldLength-i] !== newArray[newLength-i]) {
unchangedEndBlockLength = i-1;
break;
}
}
addedCount = newLength - unchangedEndBlockLength - firstChangeIndex;
removedCount = oldLength - unchangedEndBlockLength - firstChangeIndex;
}

return {
firstChangeIndex,
addedCount,
removedCount
};
}
37 changes: 22 additions & 15 deletions addon/-private/system/many-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Ember from 'ember';
import { assert } from "ember-data/-private/debug";
import { PromiseArray } from "./promise-proxies";
import { _objectIsAlive } from "./store/common";
import diffArray from './diff-array';

const { get, set } = Ember;

Expand Down Expand Up @@ -141,6 +142,10 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, {
},

flushCanonical(isInitialized = true) {
// It’s possible the parent side of the relationship may have been unloaded by this point
if (!_objectIsAlive(this)) {
return;
}
let toSet = this.canonicalState;

//a hack for not removing new records
Expand All @@ -152,18 +157,21 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, {
(internalModel) => internalModel.isNew() && toSet.indexOf(internalModel) === -1
);
toSet = toSet.concat(newRecords);
let oldLength = this.length;
this.arrayContentWillChange(0, this.length, toSet.length);
// It’s possible the parent side of the relationship may have been unloaded by this point
if (_objectIsAlive(this)) {
this.set('length', toSet.length);
}
this.currentState = toSet;
this.arrayContentDidChange(0, oldLength, this.length);

if (isInitialized) {
//TODO Figure out to notify only on additions and maybe only if unloaded
this.relationship.notifyHasManyChanged();
// diff to find changes
let diff = diffArray(this.currentState, toSet);

if (diff.firstChangeIndex !== null) { // it's null if no change found
// we found a change
this.arrayContentWillChange(diff.firstChangeIndex, diff.removedCount, diff.addedCount);
this.set('length', toSet.length);
this.currentState = toSet;
this.arrayContentDidChange(diff.firstChangeIndex, diff.removedCount, diff.addedCount);
if (isInitialized && diff.addedCount > 0) {
//notify only on additions
//TODO only notify if unloaded
this.relationship.notifyHasManyChanged();
}
}
},

Expand Down Expand Up @@ -289,12 +297,11 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, {
@return {DS.Model} record
*/
createRecord(hash) {
let store = get(this, 'store');
let type = get(this, 'type');
let record;
const store = get(this, 'store');
const type = get(this, 'type');

assert(`You cannot add '${type.modelName}' records to this polymorphic relationship.`, !get(this, 'isPolymorphic'));
record = store.createRecord(type.modelName, hash);
let record = store.createRecord(type.modelName, hash);
this.pushObject(record);

return record;
Expand Down
Loading

0 comments on commit 03440c1

Please sign in to comment.