Repository: js-data/js-data (js)

Issue:
Child records not found when idAttribute is different from parent in hasMany relation
## Description

If the parent model in a `hasMany` relationship with `localKeys` has an `idAttribute` different from the `idAttribute` of the child model, then the related children are not added to the model instance.

When the `relatedCollection` is filtered in `HasManyRelation.findExistingLinksByLocalKeys`, the `where` clause looks for records that have `this.mapper.idAttribute` in the given array. So if `this.relatedCollection.mapper` has a different `idAttribute`, no records (or perhaps worse, the wrong records) will be found.

## Steps to reproduce

Start with the following definitions:
```js
store.defineMapper('foo', {
  relations: {
    hasMany: {
      bar: {
        localField: 'bars',
        localKeys: 'barIds'
      }
    }
  },
  idAttribute: 'fooId'
}

store.defineMapper('bar', {
  relations: {
    hasMany: {
      foo: {
        localField: 'foos',
        foreignKeys: 'barIds'
      }
    }
  }
})
```

Add some data to the store:
```js
const bars = store.add('bar', [
  { id: 0, name: 'A' },
  { id: 1, name: 'B' },
  { id: 2, name: 'C' }
])
const foo = store.add('foo', {
  fooId: 9,
  name: 'Z',
  barIds: [0, 2]
})
```

We expect that `foo.bars` will contain the two given entries, but because that property was built by filtering `bars` using `where: { fooId: { in: [0, 2] } }` instead of `where: { id: { in: [0, 2] } }`, what actually happens is that `foo.bars === []`.

As far as I can tell, the fix is super simple: just change `this.mapper.idAttribute` to `this.relatedCollection.mapper.idAttribute` in the `findExistingLinksByLocalKeys` filter. I'll put together a failing test and fix for it, and submit a PR.

Thanks!


Pull request:
Filter related children by their own idAttribute, instead of that of the parent
Fixes #498.

- [X] - `npm test` succeeds
- [X] - Code coverage does not decrease (if any source code was changed)


Patch:
diff --git a/src/Relation/HasMany.js b/src/Relation/HasMany.js
index 08196ab..3ff0280 100644
--- a/src/Relation/HasMany.js
+++ b/src/Relation/HasMany.js
@@ -62,7 +62,7 @@ export const HasManyRelation = Relation.extend({
   findExistingLinksByLocalKeys (ids) {
     return this.relatedCollection.filter({
       where: {
-        [this.mapper.idAttribute]: {
+        [this.relatedCollection.mapper.idAttribute]: {
           'in': ids
         }
       }


Test patch:
diff --git a/test/integration/datastore.test.js b/test/integration/datastore.test.js
index 8f06aa0..558a7c5 100644
--- a/test/integration/datastore.test.js
+++ b/test/integration/datastore.test.js
@@ -653,4 +653,54 @@ describe('DataStore integration tests', function () {
     assert.equal(nodes[4].parent, nodes[3])
     assert.deepEqual(nodes[4].children, [])
   })
+
+  it('should find hasMany foreignKeys with custom idAttribute', function () {
+    const store = new JSData.DataStore()
+
+    store.defineMapper('foo', {
+      relations: {
+        hasMany: {
+          bar: {
+            localField: 'bars',
+            localKeys: 'barIds'
+          }
+        }
+      },
+      idAttribute: 'fooId'
+    })
+    store.defineMapper('bar', {
+      relations: {
+        hasMany: {
+          foo: {
+            localField: 'foos',
+            foreignKeys: 'barIds'
+          }
+        }
+      }
+    })
+
+    const bars = store.add('bar', [
+      {
+        id: 0,
+        name: 'A'
+      },
+      {
+        id: 1,
+        name: 'B'
+      },
+      {
+        id: 2,
+        name: 'C'
+      }
+    ])
+
+    const foo = store.add('foo', {
+      fooId: 9,
+      name: 'Z',
+      barIds: [0, 2]
+    })
+
+    assert.isOk(foo.bars)
+    assert.deepEqual(foo.bars, [bars[0], bars[2]])
+  })
 })