Skip to content

Searching inside documents

Besides names and keywords, search can match the words inside documents: the text of a brochure, a contract or a spreadsheet. Processing extracts the text of each new version once; GET P/search with in=content then matches it, within what the caller may see. This page is for developers and evaluators.

Type Text extracted from
PDF The text of every page
Word, PowerPoint, Excel (docx, pptx, xlsx) The document body with footnotes and endnotes (deleted tracked changes left out), the slides in order, the cells
OpenDocument (odt, odp, ods) The document’s content
Text, Markdown, CSV, JSON, YAML The text
HTML, XHTML, XML The text, without markup, scripts, styles and comments
Images, legacy Office formats (doc, xls, ppt), RTF, everything else Nothing: processing.text is skipped. There is no OCR

The first 2 MiB of text per version are searchable. processing.text is done when text was extracted, skipped for a type without text, and failed when the bytes could not be read (the file is still available). Only the current version’s text matches: a new version replaces the old text. The application can switch extraction off (text_extraction_enabled); new versions are then not searchable by their text. A quarantined file’s text is removed.

in says where q matches: name, keywords, content, or any combination (in=name,keywords,content). The default stays name,keywords, so content search is something you ask for.

find-in-documents.ts
import { files } from './files-client';
// Search inside documents: the words in the text of PDFs, Word, PowerPoint, Excel, OpenDocument, text, HTML and JSON
// files, as well as in names and keywords. Results are ranked by relevance; each hit says where it matched.
export async function findInDocuments(words: string, folderId: string) {
const result = await files.search({
q: words, // whole words; "a quoted phrase" matches as a phrase
in: ['name', 'keywords', 'content'],
folder_id: folderId,
recursive: true,
limit: 20,
});
return result.items.map((hit) => ({
file_id: hit.file_id,
name: hit.name,
// [{ field: 'content', snippet: '… the spring brochure for …' }]: at most 200 characters around the first match
snippet: hit.highlights?.find((h) => h.field === 'content')?.snippet ?? null,
}));
}
  • Words match whole words, without regard to case; "a quoted phrase" matches as a phrase.
  • Every filter applies as usual (folder_id with recursive, kind, tags, dates, custom.<key>), and the caller’s permissions apply inside the query: a user finds only documents they may view.
  • Sorted by relevance (the default with q), a content search pages through the best 1,000 matches; sorted by name, date or size it pages through every match.

Each hit that matched in its text carries a highlight with field: "content": at most 200 characters around the first matching word, with … where it was cut.

{ "file_id": "01J9Z6S9Q5F5R6Y2W2GZ3B3K4D", "name": "spring-campaign.pdf", "kind": "pdf",
"highlights": [ { "field": "content", "snippet": "… the spring brochure goes to print on 3 March, with the new …" } ] }

In the libraries: in: ['content'] and hit.highlights in ccs-files-client and ccs-files-user-client, SearchField.content and SearchHit.highlights in the Flutter package. Every parameter is on Search query.