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

hasMany through doesn't set the through key value #394

Merged
merged 1 commit into from
Feb 17, 2015
Merged

hasMany through doesn't set the through key value #394

merged 1 commit into from
Feb 17, 2015

Conversation

clarkbw
Copy link
Contributor

@clarkbw clarkbw commented Jan 15, 2015

I created a quick test to describe the hasManyThrough issue I'm seeing. I left in the console.log call so you can easily see the problematic object. (pull in my PR and run npm test)

{ "followerId": 2,
  "date": "Wed Jan 14 2015 22:54:43 GMT-0800 (PST)",
  "id": 1 }

followerId is being set but the followeeId attribute is not set; a critical piece when creating this object. This test demonstrates using the add function though I haven't testing with other relation functions like create.

@slnode
Copy link

slnode commented Jan 15, 2015

Can one of the admins verify this patch? To accept patch and trigger a build add comment ".ok\W+to\W+test."

@clarkbw
Copy link
Contributor Author

clarkbw commented Jan 16, 2015

I did a little digging, I'm guessing the throughKeys function is not correctly returning keys for a hasManyThrough relation. Might be able to look into a fix later but I don't understand all of what the throughKeys function does.
https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/relation-definition.js#L816

Logging the keys returned from within the add function shows the wrong set returned.
https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/relation-definition.js#L979

@raymondfeng
Copy link
Contributor

There seems to be a bug in handling multiple belongsTo relations to the same model, see:
https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/relation-definition.js#L464

@steirico
Copy link

I'm facing this problem (or a related one) with loopback-datasource-juggler 2.14.1, too.

I have the following models:

{
  "name": "Customuser",
  "base": "User",
  "properties": {
.....
  },
  "relations": {
    "supportingUsers": {
      "type": "hasMany",
      "model": "Customuser",
      "through": "SupporterMapping",
      "foreignKey": "supportingUserId"
    },
    "supportedUsers": {
      "type": "hasMany",
      "model": "Customuser",
      "through": "SupporterMapping",
      "foreignKey": "supportedUserId"
    },
    "supportedSites": {
      "type": "hasMany",
      "model": "Site",
      "through": "SupporterMapping",
      "foreignKey": "supportedSiteId"
    },
  },
...
}
{
  "name": "Site",
  "base": "PersistedModel",
  "properties": {
...
  },
  "relations": {
    "customer": {
      "type": "belongsTo",
      "model": "Customuser",
      "foreignKey": "customerId"
    },
    "siteSupporter": {
      "type": "hasMany",
      "model": "Customuser",
      "through": "SupporterMapping",
      "foreignKey": "supportingUserId"
    }
  },
...
}
{
  "name": "SupporterMapping",
  "base": "PersistedModel",
  "properties": {
...
  },
  "relations": {
    "supportingUser": {
      "type": "belongsTo",
      "model": "Customuser"
    },
    "supportedUser": {
      "type": "belongsTo",
      "model": "Customuser"
    },
    "supportedSite": {
      "type": "belongsTo",
      "model": "Site"
    }
  },
...
}

IMHO, relations are set up correctly. Querying the model SupporterMapping using the StrongLoop API Explorer resolves all relations correctly.
GET /SupporterMappings/{id}/supportedSite -> ok
GET /SupporterMappings/{id}/supportedUser -> ok
GET /SupporterMappings/{id}/supportingUser -> ok

But errors arise for the following API methods:
GET /Customuser/{id}/supportedSites

{
  "error": {
    "name": "Error",
    "status": 500,
    "message": "Relation \"site\" is not defined for SupporterMapping model",
    "stack": "Error: Relation \"site\" is not defined for SupporterMapping model\n    at processIncludeItem (c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:144:10)\n    at c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:118:5\n    at c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:125:13\n    at Array.forEach (native)\n    at _each (c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:46:24)\n    at Object.async.each (c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:124:9)\n    at Function.Inclusion.include (c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:117:9)\n    at MySQL.<anonymous> (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:588:33)\n    at releaseConnectionAndCallback (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:149:17)\n    at Query.<anonymous> (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:163:7)"
  }
}

GET /Hoocusers/{id}/supportedUsers

{
  "error": {
    "name": "Error",
    "status": 500,
    "message": "Relation \"customuser\" is not defined for SupporterMapping model",
    "stack": "Error: Relation \"customuser\" is not defined for SupporterMapping model\n    at processIncludeItem (c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:144:10)\n    at c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:118:5\n    at c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:125:13\n    at Array.forEach (native)\n    at _each (c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:46:24)\n    at Object.async.each (c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:124:9)\n    at Function.Inclusion.include (c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:117:9)\n    at MySQL.<anonymous> (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:588:33)\n    at releaseConnectionAndCallback (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:149:17)\n    at Query.<anonymous> (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:163:7)"
  }
}

GET /Hoocusers/{id}/supportingUsers

{
  "error": {
    "name": "Error",
    "status": 500,
    "message": "Relation \"customuser\" is not defined for SupporterMapping model",
    "stack": "Error: Relation \"customuser\" is not defined for SupporterMapping model\n    at processIncludeItem (c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:144:10)\n    at c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:118:5\n    at c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:125:13\n    at Array.forEach (native)\n    at _each (c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:46:24)\n    at Object.async.each (c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:124:9)\n    at Function.Inclusion.include (c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:117:9)\n    at MySQL.<anonymous> (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:588:33)\n    at releaseConnectionAndCallback (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:149:17)\n    at Query.<anonymous> (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:163:7)"
  }
}

GET /Sites/{id}/siteSupporter

{
  "error": {
    "name": "Error",
    "status": 500,
    "message": "Relation \"customuser\" is not defined for SupporterMapping model",
    "stack": "Error: Relation \"customuser\" is not defined for SupporterMapping model\n    at processIncludeItem (c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:144:10)\n    at c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:118:5\n    at c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:125:13\n    at Array.forEach (native)\n    at _each (c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:46:24)\n    at Object.async.each (c:\\hfb_webapp\\node_modules\\async\\lib\\async.js:124:9)\n    at Function.Inclusion.include (c:\\hfb_webapp\\node_modules\\loopback-datasource-juggler\\lib\\include.js:117:9)\n    at MySQL.<anonymous> (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:588:33)\n    at releaseConnectionAndCallback (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:149:17)\n    at Query.<anonymous> (c:\\hfb_webapp\\node_modules\\loopback-connector-mysql\\lib\\mysql.js:163:7)"
  }
}

So far, I was able to track down the problem to this section where relationName is not resolved correctly:
https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/include.js#L129-L140

Hopefully, this is helpful for you. Thanks for your excellent work!

@clarkbw
Copy link
Contributor Author

clarkbw commented Jan 16, 2015

Here's my solution so far: (code not ready for check-in)
Changed the findBelongsTo function so it returns an array of options found. From the throughKeys function you can then test to see what type of relations you're working with and use the appropriate keys returned. This doesn't scale to other cases where you might have multiple belongsTo relations but only want a specific set.

@raymondfeng
Copy link
Contributor

@clarkbw Pls let us know when your patch is ready to merge.

@steirico
Copy link

I just want to let you know that @clarkbw's patch is working for me.

Furthermore, I had to add the "keyThrough" property to the "hasMany" relation definitions. Unfortunately, this property is not documented anywhere and it cost me half a day digging into the code and find this solution. Enhance the docs accordingly, please.

But now, my models work now as expected. Here my modified model definitions. Hopefully, someone can benefit from them:

{
  "name": "Customuser",
  "base": "User",
  "properties": {
.....
  },
  "relations": {
    "supportingUsers": {
      "type": "hasMany",
      "model": "Hoocuser",
      "through": "SupporterMapping",
      "foreignKey": "supportedUserId",
      "keyThrough": "supportingUserId"
    },
    "supportedUsers": {
      "type": "hasMany",
      "model": "Hoocuser",
      "through": "SupporterMapping",
      "foreignKey": "supportingUserId",
      "keyThrough": "supportedUserId"
    },
    "supportedSites": {
      "type": "hasMany",
      "model": "Site",
      "through": "SupporterMapping",
      "foreignKey": "supportingUserId",
      "keyThrough": "supportedSiteId"
    },
  },
...
}
{
  "name": "Site",
  "base": "PersistedModel",
  "properties": {
...
  },
  "relations": {
    "customer": {
      "type": "belongsTo",
      "model": "Customuser",
      "foreignKey": "customerId"
    },
    "siteSupporters": {
      "type": "hasMany",
      "model": "Hoocuser",
      "through": "SupporterMapping",
      "foreignKey": "supportedSiteId",
      "keyThrough": "supportingUserId"
    }
  },
...
}
{
  "name": "SupporterMapping",
  "base": "PersistedModel",
  "properties": {
...
  },
  "relations": {
    "supportingUser": {
      "type": "belongsTo",
      "model": "Customuser"
    },
    "supportedUser": {
      "type": "belongsTo",
      "model": "Customuser"
    },
    "supportedSite": {
      "type": "belongsTo",
      "model": "Site"
    }
  },
...
}

@clarkbw
Copy link
Contributor Author

clarkbw commented Feb 1, 2015

@raymondfeng let me know if including lodash-node is ok. I can make it work either way; I prefer using lodash but I'm not sure what the project guidelines are for module inclusion.

@raymondfeng
Copy link
Contributor

lodash is fine.

@clarkbw
Copy link
Contributor Author

clarkbw commented Feb 2, 2015

@raymondfeng updated, rebased against master and squashed into 1 commit. I'm probably going to continue to be slow on the turnaround time but let me know if you want any changes.

@raymondfeng
Copy link
Contributor

ok to test

@raymondfeng
Copy link
Contributor

@clarkbw
Copy link
Contributor Author

clarkbw commented Feb 10, 2015

Have you signed the CLA - https://cla.strongloop.com/agreements/strongloop/loopback-datasource-juggler?

Sorry. I thought I did, but I'm wondering now if I signed it for loopback and not this module if they are different CLAs. Signed again, I'll look into the build failures.

@raymondfeng
Copy link
Contributor

@clarkbw Any update on the test failures?

@clarkbw
Copy link
Contributor Author

clarkbw commented Feb 16, 2015

@clarkbw Any update on the test failures?

Looking through them now, I wasn't seeing those previously so I'll have to track down the change that made the difference.

@clarkbw
Copy link
Contributor Author

clarkbw commented Feb 17, 2015

@raymondfeng updated and passing.
The change from lodash-node to lodash caused a little confusion, all fixed now. I'm still not totally happy with it, the code isn't any easier to read; it would be nice to do a bit of refactor cleanup that created some sub-modules to separate concerns.

raymondfeng added a commit that referenced this pull request Feb 17, 2015
hasMany through doesn't set the through key value
@raymondfeng raymondfeng merged commit a44e45b into loopbackio:master Feb 17, 2015
@AurelieV
Copy link

AurelieV commented Apr 3, 2015

@steirico I have the same problem as you, but your synthax did not worked for me.
One side works well, but other side has a very strange behavior (returning doublons, even all data).
Which version to you use? Which database?
Thanks for your answer

@clarkbw
Copy link
Contributor Author

clarkbw commented Apr 3, 2015

but other side has a very strange behavior (returning doublons, even all data).

@AurelieV do you have a test case you can share so I can take a look?

I'm using MongoDB and you'll need this module as of 2.18.0 when the fix landed.

@AurelieV
Copy link

AurelieV commented Apr 8, 2015

@clarkbw I use the same configuration than you.
I have finally discovered that the problem only appends when I add some fields filter in my request.
Fetch with only include works fine, but not any more with fields.
I load links between Definitions (same as Supporters in your case) manually from an external source. Then I check the through table, and everythink is ok, but problems happens when I check getting related definitions (referenced and referencing) for a given definition.
(coffee synthax)

(working, defs contains what I want)

app.models.Definition.findOne { where: { external_id: '19A35136542D582B' }, include: ['referencedDefinitions']}, (err, def) ->
  assert.equal err, null
  defs = def.referencedDefinitions()

(not working, defs contains doublons or even all data)

app.models.Definition.findOne { where: { external_id: '19A35136542D582B' }, include: ['referencedDefinitions'], fields:['referencedDefinitions']}, (err, def) ->
  assert.equal err, null
  defs = def.referencedDefinitions()

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

Successfully merging this pull request may close these issues.

None yet

6 participants