fix nested multi-entities query by renaudll · Pull Request #138 · shotgunsoftware/python-api · GitHub
Skip to content

fix nested multi-entities query#138

Merged
jfboismenu merged 7 commits into
shotgunsoftware:masterfrom
SqueezeStudioAnimation:squeeze_fixes
May 4, 2017
Merged

fix nested multi-entities query#138
jfboismenu merged 7 commits into
shotgunsoftware:masterfrom
SqueezeStudioAnimation:squeeze_fixes

Conversation

@renaudll

@renaudll renaudll commented Mar 7, 2017

Copy link
Copy Markdown
Contributor

Hi,

We started using mockgun for testing our pipeline at Squeeze.
However I've realized that some calls to find that worked with Shotgun failed with mockgun.
This was mainly related to :

  • Using nested multi-entity when filtering (ex: ["project.Project.users.HumanUser.login", "is", "user1"])
  • Requested fields (indirectly through the order argument) not being returned.

I've made the changes and implemented some additional tests, hope it help!

@victoriagrey

Copy link
Copy Markdown
Contributor

@jfboismenu

Copy link
Copy Markdown
Contributor

Hi @renaudll , I'm dropping by just to let you know that I will be reviewing your fix later this week. We're so happy that people are contributing to mockgun. :) Shouldn't take long.

@jfboismenu jfboismenu left a comment

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.

Hi,
Thanks for your help. Unfortunately there seems to be a few issues with your pull request which prevents us from merging it as is.

Notably, it seems to add two behavioral changes that are not actually supported by a real Shotgun server, namely

  • ordered fields are not part of the result set
  • it appears you can't order on a multi entity field

There's a lot of good in there however and we'd love if you could submit an update that would address these issues.


# Include fields from the order argument in the searched fields
if order:
for o in order:

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.

You are trying to fix the find query here in order to be able to sort on fields that are not part of the returned result. This is awesome!!! Thanks so much.

However, it seems to be leaking the ordered fields into the result set, which doesn't seem to be the behaviour of the Shotgun API. I've run shotgun.find("HumanUser", [], order=[{'field_name': 'login', 'direction': 'asc'}]) like your test does on my Shotgun server and the field doesn't come back.

Did it on your end? Which version of Shotgun are you using?

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.

Your are right, the fields were leakings! I've pushed a new commit that fix this.

Comment thread tests/test_mockgun.py Outdated
self.assertEqual(self._pipeline_configutation_2['id'], item['id'])

# Test descending order
item = self._mockgun.find_one("PipelineConfiguration", [], order=[{'field_name': 'users.HumanUser.login', 'direction': 'desc'}])

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.

Could you provide another example of this working on an actual server? Running this query on my Shotgun server yields:

Fault: API read() CRUD ERROR #10: Read failed for entity type [PipelineConfiguration]: Invalid Shotgun Field PipelineConfigurationUserConnection.login

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.

I'm surprised that we could sort on a multi entity field. If there are multiple entities attached to the field which value are we supposed to even use for sorting. I'll check back with the backend gurus.

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.

To be clear, there is nothing wrong with the update to the order code you provided (except for it returning an extra field, as discussed above), so if you could just provide a unit test that would succeed on a real server that would be great. If I am indeed right that we can't order on multi-entity field values then we will probably want at a later date fix Mockgun to prevent it.

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 are using a lot of sorting on nested entity field on our side.
For example in production we have a custom entity named Episode that contain Sequences.
Here is the code I use to get all of it's sequence in the correct order.

filters = [
["sg_sequence.Sequence.sg_episode", "is", {'type': self.type, 'id': self.id}]
]
order = [
    {'field_name': 'sg_sequence.Sequence.sg_sequence_order', 'direction': 'asc'},
    {'field_name': 'sg_cut_order', 'direction': 'asc'},
]

However I just realised that the sg_episode and sg_sequence are 'entity' fields and the PipelineConfiguration.users is an 'multi_entity' fields. You might be right it may be not possible to sort using 'multi_entity' fields.

I was using the 'PipelineConfigure.users' field to have a simple test case without the need to create custom fields, however I might have to rename 'test_find_order_multi_entity' to 'test_find_order_nested_entity' and design a better test case that exclusively use 'entity' fields instead of 'multi_entity' fields. I'll think of something and update the pull request accordingly!

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.

How about sorting pipeline configurations based on the project's name? :D

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.

Yes good idea, I've pushed an update with this change. In a perfect world I would have loved to test complex nested queries like 'field1.EntityType.field2.EntityType.field3' etc but this should do the trick for now.

Comment thread tests/test_mockgun.py Outdated
self.assertEqual(self._pipeline_configutation_1['id'], item['id'])

def test_find_order_fields(self):
"""Ensure that additional fields passed through the order argument but NOT via the field argument are added to the resulting fields."""

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.

I think what we need here is actually a test that sorts on a nested field, like sorting PipelineConfigurations based on the associated project name.
And then also assert that the field we used to sort is not part of the result.

values.append(sub_field_value)
return values
# not multi entity, must be entity.
elif field_value is None:

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.

Adding these

elif field_value is None:
    return None

two lines to the original method are all that is necessary for your tests to pass. I recommend you restore the original code.

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'm not sure I'm following, by just cherry-picking those two line in the upstream code the tests "test_find_order_multi_entity" fail with the following key error "KeyError: 'users.HumanUser.login'". The rest of the changes in this method is necessary to properly handle requests like "users.HumanUser.login".

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.

Did you undo the code in find as well? I meant that you should undo the changes from _get_field_from_row method. I've pushed a branch that restores the original code from _get_field_from_row and injects the two lines. You can see the diff with master here.

When running the tests on that branch, everything still passes.

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.

You are also right! I've continued my work from your branch.

renaudll and others added 2 commits March 15, 2017 17:30
test_find_by_sub_entity_field_nested fail on upstream
fix test_find_order_fields
@jfboismenu

Copy link
Copy Markdown
Contributor

Hi @renaudll , do you have any updates?

@renaudll

Copy link
Copy Markdown
Contributor Author

Hi @jfboismenu , sorry for the delay. I want to continue working on this but don't have any free time atm. I will try sending an update next friday. Thanks!

@jfboismenu

Copy link
Copy Markdown
Contributor

Great! Sorry, I didn't mean to sound pushy. We appreciate the time you are taking to fix this.
Looking forward to the update.

@jfboismenu jfboismenu removed the request for review from manneohrstrom March 22, 2017 19:13
find: Fix leaking field from order
create: Automatically create_at fields
test_mockgun: Sort by entity field instead of multi-entity field
test_mockgun: add test_find_order_fields_leak
test_mockgun: add test_find_order_date created
@renaudll

Copy link
Copy Markdown
Contributor Author

Hi @jfboismenu , sorry for the delay, it was a crazy week! I've pushed an update that address all the points listed. Thank you so much for your time reviewing this and sorry again for the slow response.

P.S.: I've took the liberty of adding another little fix that set the 'create_at' field on the create method (SqueezeStudioAnimation@45ce172#diff-443157c398f7038db0d1eb0401aa7d4eR382). Don't hesitate to ask for a separated pull request!

@jfboismenu jfboismenu left a comment

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.

Only remaining issue is the created_at support you've added. Could you fix it please? Everything else looks great! Thanks for that awesome contribution.

Comment thread tests/test_mockgun.py Outdated
def test_find_order_fields_leak(self):
"""Ensure that additional fields passed through the order argument but NOT via the field argument are not added to the resulting fields."""
item = self._mockgun.find_one("HumanUser", [], fields=['email'], order=[{'field_name': 'login', 'direction': 'asc'}])
self.assertSetEqual(set(item.keys()), set(['id', 'type', 'email'])) # note: we don't verify the order yet

@jfboismenu jfboismenu Mar 31, 2017

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.

Unfortunately, assertSetEqual was introduced in Python 2.7 and we still need to support 2.6. assertEqual is our only friend for comparison.

Comment thread shotgun_api3/lib/mockgun/mockgun.py Outdated
self._update_row(entity_type, row, data)
self._update_row(entity_type, row, data)
row["id"] = next_id
row["created_at"] = datetime.datetime.now()

@jfboismenu jfboismenu Mar 31, 2017

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.

created_at can be set by a shotgun.create call. You should test for "created_at" not in data first.

Comment thread shotgun_api3/lib/mockgun/mockgun.py Outdated
]

# handle the ordering of the recordset
# Request additional fields from results

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.

I find the wording a bit confusing here, especially with additional. Extract fields from rows seems reasonable to me. Sorry to be nitpicky, but that comment feels confusing to me.

fields_to_remove = all_fields - requested_fields
for v in val:
for field in fields_to_remove:
v.pop(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.

Ah, never noticed that dict had a pop method just like list. Looks nicer than using del v[field]. You get the result back as well.

Nice!

Comment thread shotgun_api3/lib/mockgun/mockgun.py Outdated

"""

import copy

@jfboismenu jfboismenu Mar 31, 2017

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 doesn't seem to be used anywhere in the file.

@renaudll

Copy link
Copy Markdown
Contributor Author

Hi @jfboismenu , sorry again for the delay.

I've done the tweaks, however I still needed to modify mockgun a bit to ensure our pipeline unit-tests are working accordingly. see SqueezeStudioAnimation@7ea4244#diff-443157c398f7038db0d1eb0401aa7d4eL659

I've added tests in test_find_by_sub_entity_field that demonstrate the issue. What do you think?

@jfboismenu jfboismenu left a comment

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.

One typo and one question. Otherwise this looks good to go.

Comment thread shotgun_api3/lib/mockgun/mockgun.py Outdated
row["id"] = next_id
row["created_at"] = datetime.datetime.now()

if "create_at" not in row:

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.

createD_at

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.

Fixed! Sorry about that!

raise ShotgunError("Invalid deep query field %s.%s" % (entity_type, field))

# make sure that types in the query match type in the linked field
if entity_type2 != field_value["type"]:

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.

Could you elaborate on why removing this was needed?

@renaudll renaudll Apr 28, 2017

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.

You can check the test_find_by_sub_entity_field I've added in my previous commit.
It seem that this error get triggered when filtering multi-entity fields.

A simple example is the 'entity' field on a 'Task'.
The following filters are valid on a shotgun server but will fail on mockgun with this check:
sg.find('Task', [['entity.Asset.code', 'is', 999]])
sg.find('Task', [['entity.Shot.code', 'is', 999]])

I might be able to extend it to work better with multi entities by querying what are the valid entity types. However in the time we had removing it was the "quick and dirty" 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.

The previous code was incorrect, let's remote it. We're in a better shape now.

@jfboismenu

Copy link
Copy Markdown
Contributor

Thank you so much @renaudll . This was awesome.

@jfboismenu jfboismenu merged commit 73554b5 into shotgunsoftware:master May 4, 2017
jfboismenu pushed a commit that referenced this pull request May 4, 2017
@jfboismenu

Copy link
Copy Markdown
Contributor

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.

3 participants