Skip to content

Map raw field names to attributes.#3995

Merged
mickhansen merged 1 commit intosequelize:masterfrom
sankethkatta:master
Sep 15, 2015
Merged

Map raw field names to attributes.#3995
mickhansen merged 1 commit intosequelize:masterfrom
sankethkatta:master

Conversation

@sankethkatta
Copy link
Copy Markdown
Contributor

Add feature #3714.

This also allows the returning: true option for update in postgres to work properly. Currently it generates out RETURNING *, which faces the same problem as described with raw queries in #3714.

@mickhansen
Copy link
Copy Markdown
Contributor

I think it would be better if it happened at a lower level than build so it could be reused for bulkCreate etc.

@sankethkatta
Copy link
Copy Markdown
Contributor Author

Any suggestions where a better place for it would be? Maybe in the Instance constructor or initValues?

Also, not sure if you have any insight into the error the build system is throwing, I am not able to reproduce the error locally (and it seems unrelated to my changes):

 Error: Expected 0 running queries. 1 queries still running in [POSTGRES-NATIVE] Continuation local storage bluebird shims then rejected

@mickhansen
Copy link
Copy Markdown
Contributor

Probably in post query result parsing, let the caller pass a field to attribute map that the query result parser can use perhaps. It's something we need a generalized approach to.

@sankethkatta
Copy link
Copy Markdown
Contributor Author

I attempted to generalize this a bit more and moved the aliasing into handleSelectQuery. It uses an option called aliasMap which is passed into Sequelize.prototype.query.
Let me know what you think.

@sankethkatta sankethkatta force-pushed the master branch 2 times, most recently from d78c926 to bbdea2a Compare June 30, 2015 18:12
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result[field] = undefined is a bit quicker, can you check if it produces the same result?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still passes the tests, however, it leaves remnants in the dataValues object.
For example, this is from one of the tests, email_address was aliased to emailAddress.

dataValues: { 
  id: 1,
  username: 'john',
  email_address: undefined,
  createdAt: Sun Jan 01 2012 02:10:10 GMT-0800 (PST),
  updatedAt: Sun Jan 01 2012 02:10:10 GMT-0800 (PST),
  emailAddress: 'john@gmail.com' 
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah of course, we don't want that.

@mickhansen
Copy link
Copy Markdown
Contributor

Looking good. Although i'm not sure about the aliasMap name.

@sankethkatta
Copy link
Copy Markdown
Contributor Author

Looks like the other parameter names for .query() are single words like replacements. How about aliases or mappings?

@mickhansen
Copy link
Copy Markdown
Contributor

That's even less descriptive though :)

@sankethkatta
Copy link
Copy Markdown
Contributor Author

I see, more descriptive! How about rawFieldMap?

@mickhansen
Copy link
Copy Markdown
Contributor

Maybe just fieldMap? I think that would be okay.

@sankethkatta
Copy link
Copy Markdown
Contributor Author

That sounds good to me, updating it now.

@sankethkatta sankethkatta force-pushed the master branch 2 times, most recently from 6ffd7ab to 027743f Compare July 2, 2015 18:57
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be written as

result.field !== undefined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BridgeAR in causes de-opt? And it would be result[field]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This depends on if any field name in result is somehow special or not. If it is e.g. Strange / field it'll deopt (there was an issue with someone using slashing in his field names :D ). Otherwise it should not but checking for undefined will be faster.

@BridgeAR
Copy link
Copy Markdown
Contributor

BridgeAR commented Jul 6, 2015

LGTM (besides one minor comment) even tough I think it would be nice to improve this at some point to only include the fields needed from the beginning on. But this is only internal stuff.

@mickhansen
Copy link
Copy Markdown
Contributor

@BridgeAR From the beggining? I'm not quite sure i understand? You mean parse and rewrite a raw query to alias the fields?

@BridgeAR
Copy link
Copy Markdown
Contributor

BridgeAR commented Jul 6, 2015

Yes

@sankethkatta
Copy link
Copy Markdown
Contributor Author

Just pushed the minor change, checking if the field existed in the result using undefined as suggested.

@mickhansen
Copy link
Copy Markdown
Contributor

@BridgeAR That does not sound feasible at all, don't think we'll ever do that ;p Rewriting raw queries would be a bit too magic i think. (Why is the input not my actual input?)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably put this under the raw if so it isn't run on all selects, no need to encur the extra loop when it's not needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the fix also works for postgres returning: true on update. We'd have to put it behind this.options.raw || this.options.type === QueryTypes.BULKUPDATE.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a bug, update should not result in a handleSelectQuery call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed it did this by design as handling a UPDATE ... RETURNING * is similar to a select. Here is the call to handleSelectQuery in postgres/query.js.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, yeah maybe you're right. I'm afraid we can't incur that perf overhead for all select queries since they are already perf sensitive and wouldnt take nicely to another nested loop, please do the large if :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it looks like this.options.raw isn't set if a model is passed in query.js#L671. Is there a way to know if the query originated from user input on sequelize.query directly without using options.raw?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm no, no way to know, that is a bit of a problem since actually in this case you don't want a raw result.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could introduce a new option to toggle on or off the auto-mapping to model attributes behavior. Right now we assume we should map fields anytime the model option is set.
But it would require users writing raw queries to set on the new option in addition to setting the model.

@sankethkatta sankethkatta force-pushed the master branch 4 times, most recently from 82e9be2 to 8ed2b18 Compare July 27, 2015 18:19
@sankethkatta sankethkatta force-pushed the master branch 2 times, most recently from f89e722 to 932e654 Compare July 31, 2015 23:10
@sankethkatta
Copy link
Copy Markdown
Contributor Author

@mickhansen Trying another approach to this problem. I replaced fieldMap with a boolean mapToModel option. The mapping will no longer be on the code path for select queries, but will require users of the raw query api to explicitly set mapToModel: true to enable the behavior.

Let me know your thoughts!

@janmeier
Copy link
Copy Markdown
Member

janmeier commented Aug 3, 2015

@sankethkatta I like this approach, it looks pretty clean to me. I'll let @mickhansen comment on this too before merging but LGTM :)

@sankethkatta
Copy link
Copy Markdown
Contributor Author

@janmeier @mickhansen any update on this?

@janmeier
Copy link
Copy Markdown
Member

@sankethkatta Mick is on vacation, I'll bug him about this when he's back ;)

@sankethkatta
Copy link
Copy Markdown
Contributor Author

Thanks! 😃

@sankethkatta
Copy link
Copy Markdown
Contributor Author

ping! 🔔

@mattprivman
Copy link
Copy Markdown

Merge please?

@mickhansen
Copy link
Copy Markdown
Contributor

Hmm, nevermind the last comment i just deleted.

@sankethkatta
Copy link
Copy Markdown
Contributor Author

I don't necessarily like that the current approach requires the user to set the mapToModel option. However, given the performance limitations of running unnecessary loops and the fact it is not possible to know whether sequelize.query was called directly by the user (which we discussed pull/3995#discussion_r34085726). This seems like the best compromise, and I'd rather err on the side of explicitness anyways.

Let me know if you can think of a better way!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for being so late to comment.
Ideally i'd like a generic mapper passed to query, or one generated in query and then used here.

The less the query generator and query handler needs to know about Model the better.

Refactor this specific code to use this.options.fieldMap and then in sequelize.query you can use options.fieldMap = mapToModel && options.model && etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored in latest commit.

changelog.md Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[FEATURE] > [ADDED]
perhaps: "Map raw fields back to attribute names when using returning ..."

@mickhansen
Copy link
Copy Markdown
Contributor

Please add API docs to sequelize.query describing fieldMap and mapToModel options.
Excellent work!

(please squash commits to a single descriptive commit when ready aswell)

@sankethkatta
Copy link
Copy Markdown
Contributor Author

I addressed the comments and squashed the commits.
As for the documentation, do I need to write somewhere else in addition to the JSDoc? (which I did on lib/sequelize#L643.
Thanks!

mickhansen added a commit that referenced this pull request Sep 15, 2015
Map raw field names to attributes.
@mickhansen mickhansen merged commit c0bcf5c into sequelize:master Sep 15, 2015
@mickhansen
Copy link
Copy Markdown
Contributor

Nope jsdoc works :)

@mickhansen
Copy link
Copy Markdown
Contributor

Thank you for your work!

@sankethkatta
Copy link
Copy Markdown
Contributor Author

Awesome! Thank you for working through this with me! 😃

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.

5 participants