Skip to content

Overview only ever fetches the first 100 office files in total, so every category under-reports and some render empty #99

Description

@MaximBuilds

Summary

OfficeOverview fetches the files for every category in a single unpaginated DAV SEARCH, then splits the results into categories client-side. The request omits <d:limit>, so the server applies its default of 100 results.

The consequence is that the Overview can never display more than 100 office files in total, across all categories combined, no matter how large the library is:

  • Every category under-reports once the library exceeds 100 office files.
  • Because the 100-row budget is spent in oc_mimetypes.id order (see the query plan below), the categories whose mimetype IDs sort later can receive zero rows and render "No … found" while the files plainly exist.

There is no indication in the UI that anything was truncated.

Observed on a real instance

Category Files in the account Shown in Overview
Documents 293 (194 .doc + 99 .docx) 100 (99 .docx + 1 .doc)
Presentations 102 (45 .ppt + 57 .pptx) 0"No Presentations found"

All of these files are owned by the current user, live under the searched root, and have correct mimetypes in oc_filecache. So ~66% of Documents and 100% of Presentations are missing from the page.

Root cause

src/services/officeFiles.ts builds the search with no limit element:

	return `<?xml version="1.0" encoding="UTF-8"?>
<d:searchrequest ${getDavNameSpaces()}>
	<d:basicsearch>
		<d:select>...</d:select>
		<d:from>
			<d:scope>
				<d:href>${getRootPath()}/</d:href>
				<d:depth>infinity</d:depth>
			</d:scope>
		</d:from>
		<d:where>
			<d:or>
${conditions}
			</d:or>
		</d:where>
	</d:basicsearch>
</d:searchrequest>`

and getAllOfficeFiles() is called once with the union of every creator's mimetypes, caching the result for all categories.

When no limit is supplied, the server defaults to 100 — apps/dav/lib/Files/FileSearchBackend.php:

$limit = $query->limit;
$maxResults = $limit->maxResults !== 0 ? (int)$limit->maxResults : 100;

Why the loss is deterministic, not random

No ORDER BY is applied, so row order comes from the query plan — and the plan groups rows by mimetype ID ascending. EXPLAIN for the query shape the backend issues (storage + path prefix + mimetype IN (…), LIMIT 100):

1  SIMPLE  s  const  PRIMARY,storages_id_index  storages_id_index  259   const              1    Using index
1  SIMPLE  m  range  PRIMARY,mimetype_id_index  mimetype_id_index  1022  NULL               6    Using where; Using index
1  SIMPLE  f  ref    …,fs_storage_mimetype,…    fs_storage_mimetype 16   const,…m.id       178   Using where

oc_mimetypes is range-scanned on mimetype_id_index (so mimetypes are visited in ID order), and oc_filecache is joined per mimetype via fs_storage_mimetype on (storage, mimetype). The scan therefore emits all files of the lowest mimetype ID, then the next, until the limit is hit.

With the IDs in this instance:

mimetype id mimetype files rows consumed of the 100
5 …wordprocessingml.document 99 99
29 application/msword 194 1
32 application/vnd.ms-powerpoint 45 0 — never reached
33 …presentationml.presentation 57 0 — never reached

Replicating the query with ORDER BY mimetype LIMIT 100 returns exactly 99 .docx + 1 .doc, matching the UI exactly. (Ordering the same set by fileid instead would have returned 16 presentations — so this is specifically an artefact of the mimetype-ordered plan, and which categories starve depends on the account's mimetype ID distribution.)

Filtering happens after this truncated fetch, so All / Mine / Shared with me cannot recover the missing files.

Steps to reproduce

  1. Have more than 100 office files in the account, where a mimetype with a low oc_mimetypes.id accounts for ≈100 or more rows.
  2. Open Office → Overview.
  3. Any category: only a subset of the files appears.
  4. A category whose mimetype ID sorts after the first 100 rows shows "No … found" despite the files existing.

Expected behaviour

Every category lists its files regardless of total library size — or at minimum, the UI indicates that results were truncated.

Notes / possible directions

  • The existing TODO in officeFiles.ts anticipates the payload cost of an unpaginated search, but assumes it returns the full result set. It doesn't: the server silently truncates at 100.
  • Per-category searches (one SEARCH per creator's mimetypes, issued when the category is opened) would bound the payload and stop one category starving the others. This looks like the smallest correct fix.
  • Adding an explicit <d:limit> to the current single search only raises the ceiling; it does not remove the starvation, since one mimetype can still consume the whole budget.
  • MAX_DISPLAY_FILES = 200 exceeds the server's effective default of 100, so the UI can never reach its own display limit — which may be why the cap went unnoticed.
  • Possibly related, possibly intended: with richdocuments doc_format=ooxml, the searched union contains only OOXML and legacy MS mimetypes, so .odt / .ods / .odp files never appear in the Overview at all.

Versions

Component Version
office 1.0.0
Nextcloud 34.0.2
richdocuments 11.1.0 (doc_format=ooxml)
Database MariaDB 11

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions