Reputation: 1158
I'm working on a custom adapter in [email protected] which will support associations but I am having trouble getting them working in conjunction with my adapter. My configuration is a one-to-many association between article and stats. My models and adapter are setup like this:
// api/models/article.js
module.exports = {
connection: ['myadapter'],
tableName: 'Knowledge_Base__kav',
attributes: {
KnowledgeArticleId: { type: 'string', primaryKey: true }
stats: {
collection: 'stats',
via: 'parentId'
}
}
// api/models/stats.js
module.exports = {
connection: ['myadapter'],
tableName: 'KnowledgeArticleViewStat',
attributes: {
count: 'integer',
ParentId: {
model: 'article'
}
}
}
// adapter.js
find: function(connectionName, collectionName, options, cb) {
console.dir(options)
// output
// {where: null}
db.query(options, function(err, res)) {
cb(err, res)
}
}
However, when I try to populate using Article.find().populate('stats').exec(console.log())
, my adapter gets {where: null}
as options when I would expect it to receive {where: {parentId: [<some-article-id>]}}
. It will return a list of articles to me but the field which is supposed to be populated from another model (stats) is just an empty list.
I feel like this is related to the fact that my adapter is not getting the proper where param to search for the related model on the primary key. To test this further, I setup a test one-to-many relationship using the the sails-mongo adapter. In this case the adapter did receive params I expected and the association worked fine.
Does anyone have any idea on why .populate('stats')
wouldn't be sending the proper "where" params to my adapter?
So it seems like what happens in associations is that SomeModel.find() will hit the adapter once and then .populate('othermodel') hits the adapter again using the primary key of the first request. Then the results of both are joined together. In my case, the second hit against the adapter isn't happening for some unknown reason.
The original issue was related to an attribute naming error that's mentioned in the comments below. However, there still appears to be some issue with the final population step mentioned by particlebanana:
Final step will: Take all of the query results from all the returned query operations and combine them in-memory to build up a result set you can return in the exec callback.
I'm seeing that all required queries are now firing but they are failing to actually populate the alias. Here's the call with some added debugging output in the form of a gist for easier consumption: https://gist.github.com/jasonsims/9423170
Upvotes: 3
Views: 2370
Reputation: 1158
Just to summarize, there were multiple issues going on here which were causing associations not to populate:
8eff54b
and it should be included in the next rc of waterline ([email protected]).{ foreignKey: [ value ] }
. Since the value was a list, jsforce was incorrectly generating the SOQL query since it expected all list values to be accompanied by either $in
or $nin
operators. I addressed this issue in github/jsforce#9 and it's now included in [email protected]._.uniq(childRows, pk)
. Since the model has defined pk == id
but the actual value returned from Salesforce is pk == Id
, this call to uniq blows away all child records but one. I'm not entirely sure if this should be a waterline bug or not but fixing the capitalization in the model attribute definitions resolved this.Upvotes: 0
Reputation: 2416
It looks like you are on the right track! The way the operation sets get built up, the .find()
on the Article should run with the first log (empty where) and the second query should get run with the parentId criteria in the log. The second query isn't running because it can't build up that parentId
array of primary keys when you don't return anything from the first query.
Short answer: you need to return something in the find
callback to see the second log, which should match your expected criteria.
The query lifecycle looks something like this:
.join()
method, if so you can pass the criteria down and let the adapter handle the joins.Article.find()
)exec
callback.I hope that helps some. Shoot me the url of your repo and I will look through it, if it's able to be open sourced, and can help some more if you come across any issues.
Upvotes: 3