fix nested multi-entities query#138
Conversation
|
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
left a comment
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Your are right, the fields were leakings! I've pushed a new commit that fix this.
| 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'}]) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
How about sorting pipeline configurations based on the project's name? :D
There was a problem hiding this comment.
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.
| 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.""" |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
You are also right! I've continued my work from your branch.
test_find_by_sub_entity_field_nested fail on upstream fix test_find_order_fields
|
Hi @renaudll , do you have any updates? |
|
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! |
|
Great! Sorry, I didn't mean to sound pushy. We appreciate the time you are taking to fix this. |
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
|
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
left a comment
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
Unfortunately, assertSetEqual was introduced in Python 2.7 and we still need to support 2.6. assertEqual is our only friend for comparison.
| self._update_row(entity_type, row, data) | ||
| self._update_row(entity_type, row, data) | ||
| row["id"] = next_id | ||
| row["created_at"] = datetime.datetime.now() |
There was a problem hiding this comment.
created_at can be set by a shotgun.create call. You should test for "created_at" not in data first.
| ] | ||
|
|
||
| # handle the ordering of the recordset | ||
| # Request additional fields from results |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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!
|
|
||
| """ | ||
|
|
||
| import copy |
There was a problem hiding this comment.
This doesn't seem to be used anywhere in the file.
|
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
left a comment
There was a problem hiding this comment.
One typo and one question. Otherwise this looks good to go.
| row["id"] = next_id | ||
| row["created_at"] = datetime.datetime.now() | ||
|
|
||
| if "create_at" not in row: |
There was a problem hiding this comment.
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"]: |
There was a problem hiding this comment.
Could you elaborate on why removing this was needed?
There was a problem hiding this comment.
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. :|
There was a problem hiding this comment.
The previous code was incorrect, let's remote it. We're in a better shape now.
|
Thank you so much @renaudll . This was awesome. |
This reverts commit 73554b5.

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 :
I've made the changes and implemented some additional tests, hope it help!