ifinder-search · v1.4 · 2026-11-06 · sha256 7aa6f62cae26cb55

ifinder-search v1.4A

Immutable. This exact content never changes and is served at /api/v1/blob/7aa6f62cae26cb55.

---
name: ifinder-search
description: >
  Search an IntraFind iFinder enterprise index through the iHub iFinder tools
  (iFinder_search, iFinder_getFields, iFinder_getFacetValues, iFinder_discover,
  iFinder_getContent, iFinder_getMetadata, iFinder_listProfiles). Use this skill
  whenever a question has to be answered from iFinder documents, or when a
  search returns nothing, too much, or the wrong thing and the query needs
  fixing. It covers the IntraFind query syntax — Lucene plus the `MODE/`,
  `THES/`, `ENTITY/`, `NEAR/`, `UNIT/`, `DATE/`, `NUMBER/` and `OR/` operators
  for linguistic matching, thesaurus expansion, entity and proximity search —
  when a field needs a `.keyword` suffix, filters versus query terms, how a
  query's words combine (OR with minimum_should_match 3<-25%), which
  way to rewrite a query after zero, too few or too many hits, facets, sorting,
  paging and the discovery loop for an unfamiliar corpus. Do NOT use it
  for web search or for other document sources.
license: Apache-2.0
metadata:
  author: IntraFind
  version: '1.4'
---

# Searching iFinder

iFinder is an enterprise search index over documents pulled from file shares,
SharePoint, Confluence, mail, ticket systems and similar sources. You reach it
through iHub tools that call the iFinder public API **as the signed-in user**,
so results are already cut to what that person is allowed to see. A document
you cannot find may simply not be yours to read.

## The one rule that breaks most queries

Every text field is indexed **twice**:

| You want to                                  | Use                 | Example                        |
| -------------------------------------------- | ------------------- | ------------------------------ |
| match words, ranked by relevance             | the plain name      | `creators:john`                |
| match a value exactly, filter, facet or sort | the `.keyword` name | `creators.keyword:"DOE, John"` |

`creators:"DOE, John"` finds documents whose author field _contains those
words_. `creators.keyword:"DOE, John"` matches only that exact string, whole and
unanalyzed. Use the plain name in the `query`, the `.keyword` name everywhere
values must match exactly.

Three things this rule does **not** apply to:

- **Dates, numbers and booleans** take no suffix: `modificationDate`,
  `file.size`, `file.isDirectory`.
- **`navigationTree`** is a hierarchy field, faceted and filtered under its
  plain name.
- **Some fields have no `.keyword` at all** — `content`, `title`, `url`,
  `subject`, `otherText`. You cannot facet or sort on them. A filter on
  `title.keyword` matches nothing; it does not error.

And some fields are the other way round: `id`, `aclAllows`, `agentType`,
`context`, `accessInfo.*` are stored but not analyzed, so only the `.keyword`
variant matches. `accessInfo.source:fileshare` finds nothing;
`accessInfo.source.keyword:fileshare` works.

**When unsure, ask the deployment rather than guessing:** `iFinder_getFields`
returns every field with the exact name to use per purpose. A `null` means the
field does not serve that purpose at all.

```
iFinder_getFields()
→ creators:         { type: text, fullTextSearch: "creators",
                      filter/aggregation/sort: "creators.keyword" }
  content:          { type: text, fullTextSearch: "content",
                      filter: null, aggregation: null, sort: null }
  modificationDate: { type: date, fullTextSearch: null,
                      filter/aggregation/sort: "modificationDate" }
```

This is also the only way to learn a deployment's **custom fields**, which are
named `cust.*` and differ per installation.

## Query syntax

The `query` takes Lucene syntax.

```
annual report                          bare words, relevance-ranked — see
                                       "How iFinder combines the words"
"annual report"                        exact phrase
title:budget AND language:de           field-qualified, boolean
creators:john OR owners:john           either
report NOT draft                       exclusion
budget*                                prefix wildcard
modificationDate:[2026-01-01 TO *]     open-ended range
_exists_:cust.classification           the field is present
*                                      everything
```

Boolean operators also accept German aliases: `UND`, `ODER`, `NICHT`.

Reserved characters (`+ - && || ! ( ) { } [ ] ^ " ~ * ? : \ /`) need escaping
with `\` when they are part of a literal value. Quote any value containing
spaces, commas or a colon.

### IntraFind operators

iFinder does not run a plain Lucene parser. The search service hands OpenSearch
the query as `intrafind_query_string`, which adds operators plain Lucene has no
equivalent for. They work in `query` and in `filter`:

| Operator                    | Does                                                                    |
| --------------------------- | ----------------------------------------------------------------------- |
| `MODE/e&Müller`             | Exact — no lemma, compound or diacritic loosening                       |
| `MODE/c&Bundesligaspiel`    | Decompound — also matches documents saying just "Liga"                  |
| `THES/&Stiefel`             | Expand with thesaurus synonyms, broader and narrower terms              |
| `ENTITY/PERS`               | Any person name, whatever it says — also `LOC`, `ORG`, `EMAIL`, `PHONE` |
| `NEAR/S(vertrag kündigung)` | Both terms in the same sentence (`P` paragraph, `5` within 5 tokens)    |
| `UNIT/>=(5 kg)`             | A weight over 5 kg written in the text, units converted                 |
| `DATE/>=(2026-01-01)`       | A date in the text, however it is written                               |
| `NUMBER/[10 TO 100]`        | A number in that range in the text                                      |
| `OR/2(a b c d)`             | OR group where at least 2 clauses must match                            |

`NEAR/S` is the one to reach for first: two terms in the same sentence is a far
stronger signal than two terms in the same document, and it sharpens a vague
query without guessing at field names.

A field prefix goes in front: `content:NEAR/S(ENTITY/PERS AND Kündigungsfrist)`.

These operators are always understood: the IntraFind parser is iFinder's
default, and a search profile cannot switch it off. An operator can still match
nothing for a reason of its own — `THES/` needs a configured thesaurus,
`ENTITY/` a field analyzed for entities, and a typo (`ENTITY/PERSON`,
`near/S`) is silent. So when an operator matters, run the query with and
without it and compare `totalFound` rather than trusting a zero.

Full grammar, every option and worked examples:
[references/intrafind-query-syntax.md](references/intrafind-query-syntax.md).

## Query or filter?

`iFinder_search` takes both. They are ANDed together, but only the `query`
influences ranking.

```
iFinder_search({
  query: "Kündigungsfrist",
  filter: [
    'application.keyword:PDF',
    'creators.keyword:"DOE, John"',
    'modificationDate:[2026-01-01 TO *]'
  ]
})
```

Put the **topic** in `query` and every **narrowing criterion** in `filter`.
Folding a filter into the query (`Kündigungsfrist AND application.keyword:PDF`)
still works but skews relevance, because the format term counts toward the
score.

## How iFinder combines the words of a query

iFinder's search profiles combine the bare words of a query with **OR**, and
require a share of them through `minimum_should_match: 3<-25%`. Expect that
setting:

| Bare words in the query | Must match           |
| ----------------------- | -------------------- |
| 1 – 3                   | all of them          |
| 4                       | 3                    |
| 5 – 7                   | all but one          |
| 8                       | 6 (a quarter misses) |

`minimum_should_match` counts bare words only. Write the operator out and it
does not apply: `urlaub OR ferien OR abwesenheit` matches any one of the three,
and `vertrag AND kündigung AND frist AND lieferant` requires all four. To set a
threshold of your own, use `OR/2(vertrag kündigung frist lieferant)` — any two
of the four.

Filters are always ANDed with the query.

## Retrying: narrow or widen, never both ways at once

A retry moves the result set in one direction only:

| You got                        | The next query needs                                                 | Same words plus more? |
| ------------------------------ | -------------------------------------------------------------------- | --------------------- |
| too many hits                  | a filter, a phrase, `NEAR/S(…)`, or words joined with explicit `AND` | only with `AND`       |
| **0 hits**                     | fewer words, other words, alternatives joined with explicit `OR`     | **never**             |
| some hits, but you want others | fewer words, other words, explicit `OR`                              | no                    |

**Zero hits is a dead end for that wording.** `vacation policy` found nothing,
so `vacation policy company` cannot find anything either: up to three bare
words must all match, and every document the longer query could match also
contains `vacation` and `policy`. Neither can `"vacation policy"` (a phrase is
stricter still) or the same query with a new filter. Padding it to four words
or more does not help either: then one word may miss, so the hits are
documents that lack `vacation` or `policy` — off topic, not the missing
documents. A retry after zero has to _remove_ or _replace_ words:

```
✗ vacation policy                →  vacation policy company       (all 3 required: still 0)
✗ vacation policy                →  "vacation policy" 2026        (stricter: still 0)
✗ vacation policy                →  vacation policy company handbook
                                                                 (3 of 4: hits drift off topic)
✓ vacation policy                →  vacation                      (fewer words)
✓ vacation policy                →  Urlaub OR Urlaubsregelung OR vacation
                                                                 (other words, explicit OR)
✓ vacation policy                →  THES/&Urlaub                  (synonyms)
✓ vacation policy                →  urlaub*                       (prefix)
```

The corpus is often in another language than the question. Try the German
word for an English question and the other way round before anything else —
`Urlaub`, `Urlaubsantrag`, `Abwesenheit` rather than more English words.

**Finding more or different documents works the same way.** To find documents
a query missed, drop a word, swap a word for a synonym, or join alternatives
with an explicit `OR` (`Reisekosten OR Spesen OR "travel expenses"`).

**To narrow, do not rely on bare extra words.** Up to three words, each added
word narrows. From four on, one in four may miss, so a fifth bare word may not
narrow at all. A filter, a phrase, `NEAR/S(…)` or an explicit `AND` always
narrows.

When a search tool result carries a `hint`, it was written for this decision:
follow it.

## Standard fields worth knowing

Content and identity: `title`, `content`, `subject`, `id`, `url`,
`accessInfo.deepLink` (the link to give a user).

People: `creators`, `owners`, `task.assignee`, `appointment.organizer`,
`message.sender` — all lexical order, `"DOE, John"`.

Dates: `modificationDate`, `creationDate`, `indexingDate`, `message.sendDate`,
`task.dueDate`. ISO 8601, UTC.

Source and type: `sourceName` (the data source), `sourceType` (human-readable
system name), `application` (Word, PDF, Confluence…), `mediaType` (MIME type),
`documentClasses`, `navigationTree` (the breadcrumb hierarchy),
`sourceLocations.label`.

Files: `file.name`, `file.extension` (lower-cased), `file.size` (bytes),
`file.isDirectory`.

Language: `language` (main, ISO 639-1), `languages` (all detected).

Per-class fields exist under a class prefix — `person.*`, `task.*`,
`appointment.*`, `message.*`, `image.*`, `container.*`, `attachment.*`,
`project.*`, `document.pageCount`. See
[references/field-reference.md](references/field-reference.md) for the full
catalog with the `.keyword` column.

A plain-word query searches a configured default set of fields, not everything —
`title`, `content` and a bundle of metadata copied into `otherText`. Fields
outside it (dates, ACLs, IDs, processing status) are reachable only when named
explicitly.

## Discovery loop for an unfamiliar corpus

Do not guess at an index you have not looked at.

1. **`iFinder_listProfiles()`** — which search profiles the user may search
   with, and which one is their default. A profile scopes the corpus; the wrong
   one is the difference between 4 hits and 4,000. A profile marked
   `available: false` is configured but not assigned to this user. When the
   user picked the search profile for the chat (the system prompt says it is
   fixed, or `iFinder_listProfiles` answers `fixed: true`), every call searches
   that profile: skip this step and do not pass another `searchProfile`.
2. **`iFinder_discover({ searchProfile })`** — one probe returning total
   document count, top facet values, sample titles and the field catalog. This
   is the fastest way to see what a profile actually contains.
3. **`iFinder_getFacetValues({ facet: "sourceName.keyword" })`** — the full
   value list for one field when the probe's top-N is truncated. Use it to learn
   the exact spelling of a source, author or application before filtering on it.
   Guessing `application.keyword:pdf` when the index says `PDF` returns nothing.
4. **`iFinder_search`** with what you learned.

## Reading results

`iFinder_search` returns `totalFound`, `results[]` and, when you asked for them,
`facets[]`. Each hit carries `id`, `score`, `title`, `sourceName`, dates,
`deepLink` and the fields you requested in `returnFields`.

Then:

- **`iFinder_getMetadata({ documentId })`** — full metadata for one hit, no
  content. Cheap.
- **`iFinder_getContent({ documentId })`** — the extracted text. This is what
  you read to answer a question. Use `maxLength` to cap it.

`documentId` is the hit's `id`, and nothing else: not its title, not its file
name, not its link. Both tools reject anything that is not an id. A title is
not even unique — the same file is usually indexed several times (see below).

Cite documents by `title` plus `deepLink`, never by bare `id` — the id means
nothing to the reader. Keep the id where it does not show and still survives
into the next turn: as the link's title, which renders as a tooltip:

```text
[Schulungsangebot.pptx](https://…/Schulungsangebot.pptx "SharePoint › Vertrieb › Dokumente · onedrive-d4HF8X5AZOWTbeGW")
```

The tooltip tells the reader where the link leads (source system, site or
folder from `navigationTree`), and a follow-up question ("who wrote the second
one?") finds the id in the earlier answer instead of guessing it. Keep `"` and
`|` out of the title — one breaks the link, the other a table cell.

### You know the title but not the id

Tool results do not carry over between turns; only the answer text does. When a
document is known only by its title, find it again before calling
`iFinder_getMetadata` or `iFinder_getContent`:

```
iFinder_search({ query: 'title:"Schulungsangebot.pptx"', maxResults: 5 })
```

Then pick the hit whose `deepLink` (or `sourceName`) matches the one cited
earlier, and use its `id`. Never pass the title itself as `documentId`.

### Size the result set before you pull it

Every hit that matched text carries its teasers, so a 50-hit response is large
whatever you put in `returnFields` — `returnFields` selects document fields, it
does not shrink the teasers. When you do not yet know whether a query is any
good, spend one hit first:

```
iFinder_search({
  query: "…",
  maxResults: 1,
  returnFacets: ["application.keyword", "sourceName.keyword"]
})
```

That returns `totalFound` and the value distribution of the whole result set for
the price of a single hit — enough to see whether the filter matched, which
formats and sources the topic lives in, and how many hits are worth fetching.
Then run the real search.

### The same file appears more than once

`totalFound` counts index documents, not distinct files. A document crawled from
two sources — a SharePoint copy and a file-share copy of the same spreadsheet —
is two documents with two ids, and a folder holding several revisions multiplies
that again. Nine hits can be one file.

Before reporting a count to the user, group by `file.name` (or `title`) and say
how many distinct documents there are. `returnFacets: ["sourceName.keyword"]`
shows at a glance when one result set spans several copies of the same corpus.

## Paging

`maxResults` is capped at 100 per call. Page with `from`:

```
iFinder_search({ query: "…", maxResults: 50, from: 0 })   // hits 1–50
iFinder_search({ query: "…", maxResults: 50, from: 50 })  // hits 51–100
```

Check `totalFound` before paging. If it is in the thousands, narrow with filters
instead — reading 40 pages is never the right answer.

## Sorting

`sort` takes `field:asc` / `field:desc`, applied in order. Only sortable fields
work: dates, numbers, and the `.keyword` variant of text fields.

```
sort: ["modificationDate:desc"]                   // newest first
sort: ["sourceName.keyword:asc", "title:desc"]    // ✗ title has no .keyword
```

Omit `sort` to rank by relevance — which is the right default for a question,
and the wrong one for "the latest N".

## Facets

Pass `returnFacets` to get the value distribution of a result set alongside the
hits:

```
iFinder_search({
  query: "Datenschutz",
  returnFacets: ["sourceName.keyword", "application.keyword", "language.keyword"]
})
```

Good for "where does this topic live?" and for offering the user a next filter.
The facet block that rides along with a search is capped; `iFinder_getFacetValues`
returns the long tail.

## When a search disappoints

| Symptom                                 | Likely cause                                      | Fix                                        |
| --------------------------------------- | ------------------------------------------------- | ------------------------------------------ |
| 0 hits with a filter                    | `.keyword` missing, or on a field that has none   | `iFinder_getFields`, then re-filter        |
| 0 hits, exact value                     | wrong spelling or casing                          | `iFinder_getFacetValues` on that field     |
| 0 hits, topic query                     | too many words, or words the corpus doesn't use   | fewer words, synonyms, other language, OR  |
| 0 hits, plausible query                 | wrong search profile (unless fixed for the chat)  | `iFinder_listProfiles`, then retry         |
| too few hits, more expected             | query too strict                                  | drop or swap words, explicit `OR`          |
| far too many hits                       | topic-only query, or 4+ bare words (one may miss) | `filter`, phrase, `NEAR/S`, explicit `AND` |
| right topic, wrong docs                 | ranking skewed by filter terms in the query       | move them to `filter`                      |
| a known document missing                | the user cannot read it                           | say so; do not retry                       |
| sort ignored                            | sorting on an analyzed text field                 | use the `.keyword` variant                 |
| 0 hits with `THES/`, `NEAR/`, `ENTITY/` | no thesaurus, field without entities, or a typo   | re-run without it and compare `totalFound` |
| a German compound finds nothing         | analyzer stopped at the surface form              | `MODE/c&` or `MODE/s&`                     |
| a name or code matches too loosely      | lemma or compound expansion                       | `MODE/e&`                                  |

Never report "there are no documents about X" after one failed query. Widen the
wording and confirm the profile and the field names first — the common case is a
query fault, not an empty corpus. And never answer a zero with the same words
plus more: up to three words it finds nothing again, from four on it finds
documents that lack one of your words.

## Related references

- [references/field-reference.md](references/field-reference.md) — the standard
  field catalog with types, `.keyword` availability and purposes.
- [references/query-cookbook.md](references/query-cookbook.md) — worked queries
  for recurring tasks.
- [references/intrafind-query-syntax.md](references/intrafind-query-syntax.md) —
  the IntraFind operators (`MODE/`, `THES/`, `ENTITY/`, `NEAR/`, `UNIT/`,
  `DATE/`, `NUMBER/`, `OR/`, `DISMAX/`) in full.