Skip to content

Api pagination#5067

Merged
jburel merged 30 commits intoome:developfrom
will-moore:api_pagination
Feb 24, 2017
Merged

Api pagination#5067
jburel merged 30 commits intoome:developfrom
will-moore:api_pagination

Conversation

@will-moore
Copy link
Copy Markdown
Member

@will-moore will-moore commented Jan 31, 2017

What this PR does

Updates pagination to use ?offset=200 to show page 2 of results instead of ?page=2.
We also count the total number of objects that would be returned by all "list objects" queries and return this in a new "meta" json object, along with other pagination info (current limit, offset & max limit):
E.g. /images/

"meta": {
    "totalCount": 16739,
    "limit": 200,
    "maxLimit": 500,
    "offset": 0
},

This PR also adds new config settings for the default api "limit" and the "max limit" in a new api_settings.py. Other api settings have also been moved to this file.

Testing this PR

  1. Go to any top-level object listing, e.g. /projects/ or /images/ and check for "meta" in json.
    • Check that 'totalCount' is correct (same as in clients)
  2. Filter E.g. filter images by Dataset / orphaned and check that totalCount is correct.
  3. Try using ?offset=100 and / or ?limit=50. Check that these values are shown in "meta" (and that they do pagination as expected)
  4. Test the max limit by trying a limit above 500. E.g. use ?limit=1000. Should see that the actual limit used in the query is restricted to the max_limit (500).
  5. Configure the default limit bin/omero config set omero.web.api.limit 300
  6. Configure the max limit bin/omero config set omero.web.api.max_limit 1000
  7. Check that these are used correctly in the scenarios above.

Related reading

Discussed at web meeting 2017-01-26 and 2017-02-08

@jburel jburel added the develop label Feb 1, 2017
@snoopycrimecop
Copy link
Copy Markdown
Member

Conflicting PR. Removed from build OMERO-DEV-merge-push#584. See the console output for more details.
Possible conflicts:

  • PR Api wells #5047 will-moore 'Api wells'
    • components/tools/OmeroWeb/test/integration/test_api_images.py
    • components/tools/OmeroWeb/test/integration/test_api_containers.py
  • PR Api child count #5058 will-moore 'Api child count'
    • components/tools/OmeroWeb/test/integration/test_api_images.py
    • components/tools/OmeroWeb/test/integration/test_api_containers.py

@snoopycrimecop snoopycrimecop mentioned this pull request Feb 8, 2017
2 tasks
@snoopycrimecop
Copy link
Copy Markdown
Member

Conflicting PR. Removed from build OMERO-DEV-merge-push#585. See the console output for more details.
Possible conflicts:

  • PR remove django 1.6 related workarounds #4991 aleksandra-tarkowska 'remove django 1.6 related workarounds'
    • components/tools/OmeroWeb/omeroweb/settings.py
  • PR Api wells #5047 will-moore 'Api wells'
    • components/tools/OmeroWeb/test/integration/test_api_images.py
    • components/tools/OmeroWeb/test/integration/test_api_containers.py
  • PR Api child count #5058 will-moore 'Api child count'
    • components/tools/OmeroWeb/test/integration/test_api_images.py
    • components/tools/OmeroWeb/test/integration/test_api_containers.py
    • components/tools/OmeroWeb/omeroweb/settings.py

@will-moore will-moore closed this Feb 9, 2017
@will-moore will-moore reopened this Feb 17, 2017
@snoopycrimecop
Copy link
Copy Markdown
Member

Conflicting PR. Removed from build OMERO-DEV-merge-push#593. See the console output for more details.
Possible conflicts:

  • Conflict detection failed
    • components/tools/OmeroWeb/test/integration/test_api_images.py

unnecessary objects to query.
We return just the query and omero.sys.ParametersI for the query.

:param obj_type: Object type. E.g. "Project" see above
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

typo e.g.

:param opts: Dict of options for filtering by
offset, limit and owner for all objects.
Additional opts handled by _getQueryString()
E.g. filter Dataset by 'project'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same

from functools import update_wrapper
from api_exceptions import NotFoundError, BadRequestError, CreatedObject
from api_exceptions import NotFoundError, BadRequestError, CreatedObject, \
MethodNotSupportedError
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

order them

E.g. filter Dataset by 'project'
:return: (query, params)
"""
# We disable pagination since we want to count ALL results
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

something to test for large number of results returned


# For any given release of api, we may support
# one or more versions of the api.
# E.g. /api/v1.0/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

typo

# Check that page 2 gives next 2 projects
page = 2
payload = {'limit': limit, 'page': page}
payload = {'limit': limit, 'offset': limit}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just to avoid possible mistake or miss some errors, I will use different values e.g. limit 2 projects 3 instead of 2/2

@jburel
Copy link
Copy Markdown
Member

jburel commented Feb 21, 2017

FYI Point 1: this is difficult in the sense that for example all projects are loaded via api and not possible to verify in client e.g. log as "root"
Better to be tested one via "standard call" and one via api and compare the result

@will-moore
Copy link
Copy Markdown
Member Author

@jburel You can reproduce client behaviour of filtering by group & owner by using ?group=3&owner=4

@jburel
Copy link
Copy Markdown
Member

jburel commented Feb 21, 2017

I was going for the general call. I think it will be better to rely on a test b/c I have to use filtering in order to validate. This means that I cannot validate w/o filtering

@will-moore
Copy link
Copy Markdown
Member Author

OK, I will add a test that checks the "totalCount" when filtering by group & owner.

@will-moore will-moore closed this Feb 21, 2017
@will-moore will-moore reopened this Feb 21, 2017
for example, duplicate Wells were being counted, 1 per WellSample.
@will-moore
Copy link
Copy Markdown
Member Author

Last commit fixes this issue:
When counting wells the query was counting Wells multiple times, once for each WellSample.

Wells are loaded with this query:

  select obj from Well obj join fetch obj.details.owner as owner join fetch obj.details.creationEvent
  left outer join fetch obj.wellSamples as wellSamples
  left outer join fetch wellSamples.image as image
  left outer join fetch wellSamples.plateAcquisition as plateAcquisition
  where obj.plate.id = :pid order by obj.column, obj.row, obj.id

Wells were being counted with this query:

select count(obj) from Well obj join  obj.details.owner as owner join  obj.details.creationEvent
left outer join  obj.wellSamples as wellSamples
left outer join  wellSamples.image as image
left outer join  wellSamples.plateAcquisition as plateAcquisition
where obj.plate.id = :pid 

@atarkowska
Copy link
Copy Markdown
Member

atarkowska commented Feb 21, 2017

why do you need all optional joins if you don't use them in counting query?

@will-moore
Copy link
Copy Markdown
Member Author

For every query that we make with the /api/ we also do a 'count' query, so there is a simple conversion function that creates the 'count' query from the original 'data loading' query.
See conn.buildCountQuery(). This essentially does:

        query = query.replace("select obj ", "select count(distinct obj) ")
        query = query.replace("fetch", "")
        query = query.split("order by")[0]

Any more-complex conversion we do will have to work with ALL the queries, so will need to be more customised to different data types.

@will-moore
Copy link
Copy Markdown
Member Author

To test last few commits:

  • try negative numbers for ?offset and ?limit
  • Check ['meta']['totalCount'] json is correct for Wells in /plates/:id/wells/

@jburel
Copy link
Copy Markdown
Member

jburel commented Feb 22, 2017

maxLimit and limit should be > 0 e.g. ?limit=0 should reset to the default
I should not be able to set negative value in config for them
localhost:4080/api/v0/m/projects/
result
{"meta": {"totalCount": 1, "maxLimit": -1, "limit": -1, "offset": 0}, "data": [{"url:project": "http://localhost:4080/api/v0/m/projects/1/", "omero:details": {"owner": {"UserName": "root", "FirstName": "root", "omero:details": {"@type": "TBD#Details", "permissions": {"canDelete": true, "perm": "------", "canEdit": true, "canAnnotate": true, "canLink": true, "@type": "TBD#Permissions"}}, "LastName": "root", "@id": 0, "@type": "http://www.openmicroscopy.org/Schemas/OME/2016-06#Experimenter"}, "group": {"omero:details": {"@type": "TBD#Details", "permissions": {"canDelete": true, "perm": "rw----", "canEdit": true, "canAnnotate": true, "canLink": true, "@type": "TBD#Permissions"}}, "@id": 0, "@type": "http://www.openmicroscopy.org/Schemas/OME/2016-06#ExperimenterGroup", "Name": "system"}, "@type": "TBD#Details", "permissions": {"canDelete": true, "perm": "rw----", "canEdit": true, "canAnnotate": true, "canLink": true, "@type": "TBD#Permissions"}}, "url:datasets": "http://localhost:4080/api/v0/m/projects/1/datasets/", "@id": 1, "@type": "http://www.openmicroscopy.org/Schemas/OME/2016-06#Project", "Name": "project1"}]}

@will-moore
Copy link
Copy Markdown
Member Author

If I go to https://cowfish.openmicroscopy.org/webtrial/api/v0/m/projects/?limit=-1
I see

"meta": {
"totalCount": 64,
"maxLimit": 500,
"limit": 200,
"offset": 0
},

@jburel
Copy link
Copy Markdown
Member

jburel commented Feb 22, 2017

That's not the problem.
I was allowed to set the configuration to negative value
e.g.
bin/omero config set omero.web.api.max_limit -1

@will-moore
Copy link
Copy Markdown
Member Author

If I use ?limit=0 (which is supported by the OMERO api) I get this, which is potentially useful

"meta": {
"totalCount": 64,
"maxLimit": 500,
"limit": 0,
"offset": 0
},
"data": []

@will-moore
Copy link
Copy Markdown
Member Author

Ah, OK - sorry. Yes, I guess the smallest max_limit should be 1

@jburel
Copy link
Copy Markdown
Member

jburel commented Feb 22, 2017

It is useful if do not have any results
but when I do http://web-dev-merge.openmicroscopy.org/api/v0/m/plates/1902/wells/?limit=0
{"meta": {"totalCount": 10, "maxLimit": 500, "limit": 0, "offset": 0}, "data": [{"WellSamples": [{"omero:details": {"owner": {"UserName": "root", "FirstName": "root", "MiddleName": "", "omero:details": {"@type": "TBD#Details", "permissions": {"isUserWrite": false, "isWorldWrite": false, "canDelete": true, "isWorldRead": false, "perm": "------", "canEdit": true, "canAnnotate": true, "isGroupAnnotate": false, "isGroupWrite": false, "canLink": true, "isUserRead": false, "@type": "TBD#Permissions", "isGroupRead": false}}, "Email": "", "LastName": "root", "@id": 0, "@type": "http://www.openmicroscopy.org/Schemas/OME/2016-06#Experimenter"}, "group": {"omero:details": {"@type": "TBD#Details", "permissions": {"isUserWrite": true, "isWorldWrite": false, "canDelete": true, "isWorldRead": false, "perm": "
I still have data

@will-moore
Copy link
Copy Markdown
Member Author

Very strange - I can repeat locally too. But I think this must be something about that particular query, (loading Wells and joining WellSamples etc) that is causing limit=0 to be interpreted as limit=1.
It seems to work fine for other queries, e.g.
http://web-dev-merge.openmicroscopy.org/api/v0/m/projects/1226/datasets/?limit=0

@jburel
Copy link
Copy Markdown
Member

jburel commented Feb 22, 2017

it works for projects, datasets etc.

Having this fail silently and return 0 is not helpful. Users need to know
that something has gone wrong.
Now uses same logic as webcient views.get_long_or_default()
@will-moore
Copy link
Copy Markdown
Member Author

@joshmoore Any idea why using params of limit=0 and offset=0 for this query doesn't return 0 objects (I get 1 result):

select obj from Well obj join fetch obj.details.owner as owner join fetch obj.details.creationEvent
left outer join fetch obj.wellSamples as wellSamples
left outer join fetch wellSamples.image as image
left outer join fetch wellSamples.plateAcquisition as plateAcquisition
where obj.plate.id = :pid order by obj.column, obj.row, obj.id

When I don't load WellSamples etc then limit=0 works OK (get no results) with

select obj from Well obj join fetch obj.details.owner as owner join fetch obj.details.creationEvent
where obj.plate.id = :pid order by obj.column, obj.row, obj.id

@jburel I now handle config of omero.web.api.limit and omero.web.api.max_limit to be a minimum value of 1.

@will-moore will-moore mentioned this pull request Feb 23, 2017
@joshmoore
Copy link
Copy Markdown
Member

@joshmoore Any idea why using params of limit=0 and offset=0 for this query doesn't return 0 objects (I get 1 result):

This might be caused by https://hibernate.atlassian.net/browse/HHH-8989 -- can you detect when limit==0 and not run the query?

@jburel
Copy link
Copy Markdown
Member

jburel commented Feb 23, 2017

The negative values are now correctly handled
@will-moore did you have a chance to look at @joshmoore's suggestion?

@will-moore
Copy link
Copy Markdown
Member Author

Sorry - missed @joshmoore's comment notification somehow. Yes, that should be trivial to do.

@jburel
Copy link
Copy Markdown
Member

jburel commented Feb 24, 2017

Looks good Thanks all

@jburel jburel merged commit a161fa5 into ome:develop Feb 24, 2017
@jburel jburel added this to the 5.3.0 milestone Mar 29, 2017
@will-moore will-moore deleted the api_pagination branch February 18, 2019 04:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants