nextcloud / nextcloud/server

Add support for WebDAV SEARCH on nc:metadata-photos-original_date_time, fileId and internal id field

Open
#57,873 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

0. Needs triage enhancement
Dominant language
PHP
Stars
36.9k
Forks
5.2k
Avg merge
2d 3h
Merged PRs (30d)
713

Description

Support deterministic cursor pagination in WebDAV SEARCH using oc:fileid

Motivation

Nextcloud clients need to retrieve large media collections through bounded WebDAV SEARCH requests.

Media results are primarily ordered by a date property such as:

  • DAV:getlastmodified
  • nc:upload_time
  • nc:metadata-photos-original_date_time

A date is not unique. Hundreds or thousands of files can share the same timestamp. Date-only cursor pagination can therefore stop advancing when all results in a limited response have the same date.

Missing capability

The client needs a deterministic secondary key:

date DESC, oc:fileid DESC

The next request can then continue using the last tuple returned by the previous page:

date < cursorDate
OR
(date = cursorDate AND oc:fileid < cursorFileId)

The current search backend declares oc:fileid selectable and searchable, but not sortable. See [FileSearchBackend.php](https://github.com/nextcloud/server/blob/master/apps/dav/lib/Files/FileSearchBackend.php).

DAV:displayname cannot be used as a replacement. Although it is sortable, a range comparison currently fails with:

HTTP 500
Unsupported comparison for field name: lte

Using only a result limit therefore cannot guarantee progress when many files share the same date.

Example request

This example retrieves the next media page after:

cursorDate   = 2026-06-01T10:00:00Z
cursorFileId = 123456
<?xml version="1.0" encoding="UTF-8"?>
<d:searchrequest
    xmlns:d="DAV:"
    xmlns:oc="http://owncloud.org/ns"
    xmlns:nc="http://nextcloud.org/ns">
    <d:basicsearch>
        <d:select>
            <d:prop>
                <oc:id/>
                <oc:fileid/>
                <d:displayname/>
                <d:getlastmodified/>
                <d:getcontenttype/>
                <d:getetag/>
            </d:prop>
        </d:select>

        <d:from>
            <d:scope>
                <d:href>/files/test</d:href>
                <d:depth>infinity</d:depth>
            </d:scope>
        </d:from>

        <d:where>
            <d:and>
                <d:or>
                    <d:like>
                        <d:prop><d:getcontenttype/></d:prop>
                        <d:literal>image/%</d:literal>
                    </d:like>
                    <d:like>
                        <d:prop><d:getcontenttype/></d:prop>
                        <d:literal>video/%</d:literal>
                    </d:like>
                </d:or>

                <d:or>
                    <d:lt>
                        <d:prop><d:getlastmodified/></d:prop>
                        <d:literal>2026-06-01T10:00:00Z</d:literal>
                    </d:lt>

                    <d:and>
                        <d:eq>
                            <d:prop><d:getlastmodified/></d:prop>
                            <d:literal>2026-06-01T10:00:00Z</d:literal>
                        </d:eq>

                        <d:lt>
                            <d:prop><oc:fileid/></d:prop>
                            <d:literal>123456</d:literal>
                        </d:lt>
                    </d:and>
                </d:or>
            </d:and>
        </d:where>

        <d:orderby>
            <d:order>
                <d:prop><d:getlastmodified/></d:prop>
                <d:descending/>
            </d:order>
            <d:order>
                <d:prop><oc:fileid/></d:prop>
                <d:descending/>
            </d:order>
        </d:orderby>

        <d:limit>
            <d:nresults>1000</d:nresults>
        </d:limit>
    </d:basicsearch>
</d:searchrequest>

The same cursor pattern should be usable with nc:metadata-photos-original_date_time as the primary date property when that indexed metadata field is available.

Requested behavior

  • Allow oc:fileid in d:orderby.
  • Continue supporting numeric comparisons on oc:fileid, including lt, lte, gt, gte and eq.
  • Allow indexed nc:metadata-photos-original_date_time metadata to be selected, filtered and ordered.
  • Preserve oc:id in d:select so clients can deduplicate returned resources.

This does not require new metadata or a new indexing mechanism. It exposes existing indexed fields sufficiently for deterministic, bounded cursor pagination.

Expected result

Clients can process arbitrarily large media collections in bounded requests, including collections containing more than the request limit with the same timestamp, without using an effectively unlimited SEARCH request or risking a non-advancing cursor.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with apps/dav/lib/Files/FileSearchBackend.php and trace the WebDAV SEARCH handling it feeds. Check how oc:fileid and nc:metadata-photos-original_date_time are exposed for selection, comparisons, and ordering, while preserving oc:id. Done means the supplied cursor query supports numeric oc:fileid comparisons, secondary ordering, and the indexed metadata field without requiring new indexing.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
api, backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.