# @yuuvis/client-framework/smart-search

Secondary entry point of `@yuuvis/client-framework`. Import from `@yuuvis/client-framework/smart-search`.

A visual, guided query builder that turns chip-based user input into a [CMIS](https://docs.oasis-open.org/cmis/CMIS/v1.1/CMIS-v1.1.html) query. Instead of typing query syntax, users pick object types, fields, operators and values through an inline autocomplete; the component assembles a valid, injection-safe `SELECT … WHERE …` statement and emits it on every change.

## Core features

- **Full-text search bar** — a `CONTAINS` search with a selectable scope (`all` = metadata + content, `metadata`, or `content`) and an optional object-type restriction.
- **Type blocks** — one or more blocks, each targeting one or more object types. Conditions inside a block apply to the fields shared by all of its types (plus inherited base/system fields such as `system:creationDate`).
- **Guided condition building** — a step-by-step `type → field → operator → value` flow. Operators are filtered to those that make sense for the field's type (e.g. `>`/`<` for numbers and dates, date presets for dates, `= true/false` for booleans, `empty`/`not empty` for everything).
- **Real metadata widgets** — the value step renders the field's actual editor (datepicker, catalog select, organization picker, …) via the metadata-form renderer, so values are entered the same way as in object forms.
- **Nested groups** — parenthesized AND/OR sub-expressions, nestable to arbitrary depth.
- **Table conditions** — query into table-type properties by their columns, matched against any row (`tableField[*].(…)`).
- **Save / restore** — capture the whole search as a plain-data, JSON-serializable [`SmartSearchState`](src/lib/smart-search.interface.ts) and reload it later.
- **Injection-safe output** — all values are escaped; values that don't match their field's declared type are quoted as a fallback so a hostile value can never break out of the literal.

## Usage

`SmartSearchComponent` is a standalone component — import it directly.

```ts
import { Component, signal } from '@angular/core';
import { SmartSearchComponent } from '@yuuvis/client-framework/smart-search';

@Component({
  selector: 'app-search',
  imports: [SmartSearchComponent],
  template: `
    <yuv-smart-search
      [types]="['document', 'invoice']"
      [skipProperties]="['system:traceId']"
      (queryChange)="onQuery($event)"
    />
  `
})
export class SearchComponent {
  query = signal('');

  onQuery(cmisQuery: string) {
    // An empty string means "no query" — treat it as a cleared search.
    this.query.set(cmisQuery);
  }
}
```

### Inputs

| Input            | Type       | Default | Description                                                                                   |
| ---------------- | ---------- | ------- | --------------------------------------------------------------------------------------------- |
| `types`          | `string[]` | `[]`    | Object-type ids that may be searched. Set at least one to enable building blocks.             |
| `skipProperties` | `string[]` | `[]`    | Field ids to hide from the field picker (e.g. internal/system properties).                    |

### Outputs

| Output        | Type     | Description                                                                                 |
| ------------- | -------- | ------------------------------------------------------------------------------------------- |
| `queryChange` | `string` | The current CMIS query. Emitted on every change; `''` means an empty search ("no query").   |

### Saving and restoring state

`getState()` returns a serializable snapshot; `loadState()` restores one. `clear()` resets the whole search.

```ts
import { Component, viewChild } from '@angular/core';
import { SmartSearchComponent } from '@yuuvis/client-framework/smart-search';

@Component({ /* … */ })
export class SearchComponent {
  private search = viewChild.required(SmartSearchComponent);

  persist() {
    localStorage.setItem('search', JSON.stringify(this.search().getState()));
  }

  restore() {
    const raw = localStorage.getItem('search');
    if (raw) this.search().loadState(JSON.parse(raw));
  }

  reset() {
    this.search().clear();
  }
}
```

## How it produces a query

The emitted query joins independent **units** — the full-text unit and each type block — with `OR`:

```sql
-- types=['invoice'], full-text "acme" in metadata, condition: total > 1000
SELECT * FROM system:object
WHERE (system:metadata CONTAINS('acme'))
   OR (objectTypeId = 'invoice' AND total > 1000)
```

Within a block, the type restriction is always `AND`-ed with the condition expression, and the conditions combine with the block's own AND/OR combinator. Nested groups add parentheses; table conditions render as `tableField[*].(col op val …)`.

## Architecture

| File                                                               | Responsibility                                                                          |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| [`smart-search.component.ts`](src/lib/smart-search.component.ts)   | Host component — public API, focus management, autocomplete plumbing.                   |
| [`smart-search-edit.controller.ts`](src/lib/smart-search-edit.controller.ts) | All state and mutators (signals); provided per component instance.            |
| [`smart-search-group.component.ts`](src/lib/smart-search-group.component.ts) | Recursive renderer for a block body, nested group or table.                   |
| [`smart-search.query.ts`](src/lib/smart-search.query.ts)          | Pure CMIS query builders (`buildCmisQuery`, value escaping, operator rendering).        |
| [`smart-search.tree.ts`](src/lib/smart-search.tree.ts)            | Immutable tree helpers (find / update / remove containers and conditions).              |
| [`smart-search.interface.ts`](src/lib/smart-search.interface.ts)  | Data model (`SearchBlock`, `FieldCondition`, `ConditionGroup`, `SmartSearchState`, …).  |
