122 lines
4.3 KiB
Markdown
122 lines
4.3 KiB
Markdown
# `GET /article/search`
|
||
|
||
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||
|
||
Fuzzy article search. Use this to build a search box / result list with category facets.
|
||
|
||
## Query parameters
|
||
|
||
| Param | Required | Description |
|
||
|-------|----------|-------------|
|
||
| `target` | yes | Search string. Split on spaces; each word must match (AND). Each word is matched with `LIKE %word%` against EAN, supplier article number, name, article number, or barcode. |
|
||
| `filter` | no | Comma-separated category IDs. Omit or leave empty to search all categories. See [Category filter](#category-filter). |
|
||
| `offset` | no | Pagination offset (default `0`). |
|
||
| `maxCount` | no | Page size (default `10`). |
|
||
|
||
## Search behaviour
|
||
|
||
- Multiple spaces are collapsed to one.
|
||
- Example: `target=bizz allmix` → both `bizz` **and** `allmix` must match somewhere on the article.
|
||
- By default only **active** articles (`cAktiv = 'Y'`) are returned.
|
||
- Without a category filter, at most **2000** rows are returned (before pagination).
|
||
- Results are ordered by product name ascending.
|
||
- `length` is the total match count (before `offset` / `maxCount`); `recordset` is the current page.
|
||
|
||
## Category filter
|
||
|
||
| `filter` value | Effect |
|
||
|----------------|--------|
|
||
| *(omitted or empty)* | All categories; `TOP 2000` |
|
||
| `751` | Include inactive articles (no `cAktiv = 'Y'` restriction). Still uses category branch logic if other IDs are present. |
|
||
| `16` or `16,39` | Restrict to articles in those categories (OR). No row limit. |
|
||
|
||
Pass category IDs as a comma-separated list: `filter=16,39`.
|
||
|
||
Whenever `filter` is present, **`categories` always includes every id from `filter` and its full ancestor chain** (parent → … → root), even when `length` is `0` or a filtered id has no hits. Those entries use master-data `name` / `parent` and may have `count: 0`. Other facet categories with hits are still returned as usual.
|
||
|
||
## Response `200`
|
||
|
||
```json
|
||
{
|
||
"length": 42,
|
||
"recordset": [
|
||
{
|
||
"categories": "16,39",
|
||
"available": 0,
|
||
"id": 49,
|
||
"stock": null,
|
||
"sku": "42000044",
|
||
"name": "BioBizz AllMix Peat Free 20L",
|
||
"price": 13.5,
|
||
"tax": 19
|
||
}
|
||
],
|
||
"categories": {
|
||
"16": { "name": "Dünger", "count": 42, "parent": 0 },
|
||
"39": { "name": "Erde", "count": 18, "parent": 16 }
|
||
}
|
||
}
|
||
```
|
||
|
||
Empty result with a nested filter still returns the leaf and its ancestors:
|
||
|
||
```json
|
||
{
|
||
"length": 0,
|
||
"recordset": [],
|
||
"categories": {
|
||
"16": { "name": "Dünger", "count": 0, "parent": 0 },
|
||
"39": { "name": "Erde", "count": 0, "parent": 16 }
|
||
}
|
||
}
|
||
```
|
||
|
||
### Top-level fields
|
||
|
||
| Field | Type | Notes |
|
||
|-------|------|-------|
|
||
| `length` | number | Total matching articles (unpaginated). |
|
||
| `recordset` | array | Current page of shaped article rows. |
|
||
| `categories` | object | Map keyed by category id (string). Facet counts for the current search (+ filters). |
|
||
|
||
### `recordset[]` fields
|
||
|
||
| Field | Type | Notes |
|
||
|-------|------|-------|
|
||
| `id` | number | Internal article ID (`kArtikel`) |
|
||
| `sku` | string | Article number — pass to [`GET /article/get`](get.md) |
|
||
| `name` | string | Display name |
|
||
| `stock` | number \| null | Stock in warehouse 1 |
|
||
| `available` | number \| null | Available stock |
|
||
| `price` | number \| null | Brutto VK, rounded to 1 decimal |
|
||
| `tax` | number \| null | Tax rate percent for the article’s Steuerklasse |
|
||
| `categories` | string \| null | Comma-separated category IDs on the article |
|
||
|
||
### `categories` map entries
|
||
|
||
| Field | Type | Notes |
|
||
|-------|------|-------|
|
||
| `name` | string \| null | Display label from category master data |
|
||
| `count` | number | Hit count under the current search (+ other filters); may be `0` |
|
||
| `parent` | number \| null | Parent category id, or `0` if root |
|
||
|
||
Counts include parent categories rolled up from each article’s category path. Use for category sidebar / facet UI. Resolve labels via `categories[id].name` (skip ids without a name).
|
||
|
||
## Errors
|
||
|
||
| Status | Body |
|
||
|--------|------|
|
||
| 400 | `{ "error": "Query parameter \"target\" is required" }` |
|
||
| 401 | `{ "error": "Unauthorized" }` |
|
||
| 500 | `{ "error": "..." }` |
|
||
|
||
## Example
|
||
|
||
```js
|
||
const res = await fetch(
|
||
'http://localhost:4208/api/article/search?target=bizz%20allmix&filter=16&maxCount=10',
|
||
{ credentials: 'include' }
|
||
);
|
||
const { categories, length, recordset } = await res.json();
|
||
```
|