[MNT-25681] Search refactoring and unification (#12019)

* [MNT-25681] Search refactoring and unification

* [MNT-25681] CR fixes
This commit is contained in:
Michal Kinas
2026-07-02 17:46:30 +02:00
committed by GitHub
parent 37f8fe47ac
commit d8b36606e2
47 changed files with 882 additions and 453 deletions
@@ -46,9 +46,9 @@ Implements a [search widget](../../../lib/content-services/src/lib/search/models
| field | string | Field to apply the query fragment to. Required value |
| pattern | string | Regular expression pattern to restrict the format of the input text |
| placeholder | string | Text displayed in the widget when the input string is empty |
| searchSuffix | string | Text to append always in the search of a string |
| searchPrefix | string | Text to prepend always in the search of a string |
| allowUpdateOnChange | `boolean` | Enable/Disable the update fire event when text has been changed. By default is true. |
| searchSuffix | string | Text to append in the search of a string. Only applied when wildcard matching is enabled (the `search-wildcards-enabled` app config flag, default `true`). |
| searchPrefix | string | Text to prepend in the search of a string. Only applied when wildcard matching is enabled (the `search-wildcards-enabled` app config flag, default `true`). |
| allowUpdateOnChange | `boolean` | Enable/Disable firing the search update when the text changes. Defaults to `false`; when disabled the search runs only when the user submits the value. |
| hideDefaultAction | boolean | Show/hide the widget actions. By default is false. |
## Details
@@ -115,8 +115,8 @@ that will be used when performing the actual query.
Every query fragment is stored and retrieved using its widget `id`.
It is your responsibility to format the query correctly.
Once your change to the query is finished, update the context and call the `update` method
to inform other components about the change:
Once your change to the query is finished, update the context and call the `execute` method
to rebuild and run the query so the results reflect your change:
```ts
@Component({...})
@@ -126,12 +126,15 @@ export class MyComponent implements SearchWidget, OnInit {
onUIChanged() {
this.context.queryFragments[this.id] = `some query`;
this.context.update();
void this.context.execute();
}
}
```
> **Note:** Earlier versions called `this.context.update()` here. The `update()` method and the
> `updated` stream have been removed; call `this.context.execute()` directly instead.
When executed, your fragment will be injected into the resulting query based on the category order in the application configuration file.
```text
@@ -2,7 +2,7 @@
Title: Search Query Builder service
Added: v2.3.0
Status: Active
Last reviewed: 2019-03-19
Last reviewed: 2026-06-29
---
# [Search Query Builder service](../../../lib/content-services/src/lib/search/services/search-query-builder.service.ts "Defined in search-query-builder.service.ts")
@@ -11,6 +11,17 @@ Stores information from all the custom search and faceted search widgets, compil
## Class members
### Properties
| Name | Type | Description |
| ----------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| userQuery | `string` | The raw query string typed by the user. Setting it stores the value, records it in `filterRawParams` and recomputes `parsedQuery` according to the current `searchMode`. |
| parsedQuery | `string` (read-only) | The query derived from `userQuery`. In `regular` mode the user terms are expanded against the configured fields (see `app:fields`) and optionally wildcarded; in `formula` mode it is identical to `userQuery`. |
| searchMode | `'regular' \| 'formula'` | Controls how `userQuery` is turned into `parsedQuery`. `regular` (default) parses the user input into a field query; `formula` uses the user input verbatim as an AFTS expression. |
| selectedConfigurationId | `string` | Id of the currently active search configuration. Setting it also records the value in `filterRawParams`. |
| encodedQuery | `string` (read-only) | The Base64-encoded `filterRawParams`, produced by `encodeQuery()` and written to the `q` route query parameter. |
| wildcardsEnabled | `boolean` (read-only) | Reads the `search-wildcards-enabled` app config flag (default `true`). When enabled, query terms are suffixed with `*` so partial matches are returned. |
### Methods
- **addFilterQuery**(query: `string`)<br/>
@@ -25,9 +36,10 @@ Stores information from all the custom search and faceted search widgets, compil
- **Returns** `SearchRequest` - The finished query
- **encodeQuery**()<br/>
Encodes query shards stored in `filterRawParams` property.
- **execute**(queryBody?: `SearchRequest`)<br/>
Builds and executes the current query.
- _queryBody:_ `SearchRequest` - (Optional)
- **execute**(updateQueryParams: `boolean` = `true`, queryBody?: `SearchRequest`)<br/>
Builds and executes the current query, then emits the result on the `executed` stream.
- _updateQueryParams:_ `boolean` - (Optional) When `true` (default) the encoded query is written to the `q` route query parameter. Pass `false` to execute without updating the URL.
- _queryBody:_ `SearchRequest` - (Optional) Pre-built query to execute instead of building one from the current state.
- **getDefaultConfiguration**(): [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined`<br/>
- **Returns** [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined` -
@@ -70,6 +82,11 @@ Stores information from all the custom search and faceted search widgets, compil
- **Returns** `boolean` -
- **isOperator**(input: `string`): `boolean`<br/>
Checks whether the supplied string is a logical `AND` or `OR` operator. Used when parsing a multi-word user query in `regular` search mode.
- _input:_ `string` - String to check
- **Returns** `boolean` - `true` if the trimmed string is `AND` or `OR`, otherwise `false`
- **loadConfiguration**(): [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)<br/>
- **Returns** [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts) -
@@ -86,7 +103,10 @@ Stores information from all the custom search and faceted search widgets, compil
Removes an existing bucket from a field.
- _field:_ [`FacetField`](../../../lib/content-services/src/lib/search/models/facet-field.interface.ts) - The target field
- _bucket:_ [`FacetFieldBucket`](../../../lib/content-services/src/lib/search/models/facet-field-bucket.interface.ts) - Bucket to remove
- **resetToDefaults**()<br/>
- **resetToDefaults**(withNavigate: `boolean` = `false`, resetUserQuery: `boolean` = `true`)<br/>
Resets the query builder back to the default search configuration.
- _withNavigate:_ `boolean` - (Optional) When `true`, clears the `q` route query parameter while resetting. Defaults to `false`.
- _resetUserQuery:_ `boolean` - (Optional) When `true` (default), the `userQuery` and its parsed form are cleared. Pass `false` to keep the current user query while resetting the rest of the options.
- **search**(queryBody: `SearchRequest`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>`<br/>
@@ -97,14 +117,13 @@ Stores information from all the custom search and faceted search widgets, compil
- _scope:_ `RequestScope` -
- **update**(queryBody?: `SearchRequest`)<br/>
Builds the current query and triggers the `updated` event.
- _queryBody:_ `SearchRequest` - (Optional)
- **updateSearchQueryParams**() <br/>
Encodes the query and navigates to existing search route adding encoded query as a search param.
- **updateSelectedConfiguration**(index: `number`)<br/>
- _index:_ `number` -
- **updateSelectedConfiguration**(id: `string`, resetFilters: `boolean` = `true`, shouldExecute: `boolean` = `true`)<br/>
Switches the active search configuration to the one matching the supplied id (only relevant when multiple configurations are provided).
- _id:_ `string` - Id of the configuration to select
- _resetFilters:_ `boolean` - (Optional) When `true` (default), the current search options are reset before applying the new configuration. Pass `false` to keep them.
- _shouldExecute:_ `boolean` - (Optional) When `true` (default), the query is executed immediately after switching configuration.
## Details
@@ -127,10 +146,6 @@ You can use custom widgets to populate and edit the following parts of the resul
```ts
constructor(queryBuilder: SearchQueryBuilderService) {
queryBuilder.updated.subscribe(query => {
this.queryBuilder.execute();
});
queryBuilder.executed.subscribe(data => {
this.onDataLoaded(data);
});
@@ -138,6 +153,29 @@ constructor(queryBuilder: SearchQueryBuilderService) {
}
```
To run a search, build the query state (for example by setting `userQuery` or by letting a
search widget update `queryFragments`) and then call `execute()`. The result is delivered
through the `executed` stream.
```ts
this.queryBuilder.userQuery = 'invoice';
void this.queryBuilder.execute();
```
> **Note:** Earlier versions exposed an `updated` stream and an `update()` method that built the
> query and emitted it so that a subscriber could call `execute()`. Both have been removed; build
> the query state and call `execute()` directly instead.
### Search modes
The builder supports two search modes, selected through the `searchMode` property:
- **regular** (default) - the text in `userQuery` is treated as user input and parsed into a
field query. The query is split into terms, each term is matched against the fields listed in
the `app:fields` search configuration entry (falling back to `cm:name`), and a `*` wildcard is
appended when wildcards are enabled. Bare `AND`/`OR` words are preserved as operators.
- **formula** - the text in `userQuery` is used verbatim as an [AFTS](https://docs.alfresco.com/content-services/latest/develop/search-api/) expression, allowing callers that already build their own query syntax to bypass parsing.
> **Note:** From ADF 3.0.0, the query contains the `"facetFormat": "V2"` parameter so that all the responses have the same structure whether they come from search queries containing facetFields, facetQueries, grouped facetQueries or facetIntervals.
## Runtime Configuration
+2 -3
View File
@@ -90,7 +90,6 @@ export class YourSearchComponent implements OnInit {
ngOnInit() {
this.queryBuilder.resetToDefaults();
this.queryBuilder.updated.subscribe(() => void this.queryBuilder.execute());
this.queryBuilder.executed.subscribe((data) => {
this.queryBuilder.paging.skipCount = 0;
this.onSearchResultLoaded(data);
@@ -103,7 +102,7 @@ export class YourSearchComponent implements OnInit {
onSearchQueryChanged(string: string) {
this.queryBuilder.userQuery = decodeURIComponent(string);
this.queryBuilder.update();
void this.queryBuilder.execute();
}
async onPaginationChanged(pagination: Pagination) {
@@ -111,7 +110,7 @@ export class YourSearchComponent implements OnInit {
maxItems: pagination.maxItems,
skipCount: pagination.skipCount
};
this.queryBuilder.update();
void this.queryBuilder.execute();
}
}
```
@@ -16,6 +16,7 @@ This page describes how you can configure the search configuration.
- [Steps Involved In Search Configuration](#steps-involved-in-search-configuration)
- [Configuration](#configuration)
- [Extra fields and filter queries](#extra-fields-and-filter-queries)
- [Search modes and wildcards](#search-modes-and-wildcards)
- [Sorting](#sorting)
- [Categories and widgets](#categories-and-widgets)
- [Facet Fields](#facet-fields)
@@ -267,6 +268,44 @@ settings:
Note that the entries of the `filterQueries` array are joined using the `AND` operator.
### Search modes and wildcards
When a user types free text into a search input, the [Search Query Builder Service](../../content-services/services/search-query-builder.service.md) turns that text (its `userQuery`) into the final query according to the configured *search mode*:
- **regular** (default) - the user input is parsed into a field query. Each term is matched
against the fields listed in the `app:fields` array (falling back to `cm:name` when it is not
set), and a `*` wildcard is appended to each term when wildcards are enabled. Words that are
exactly `AND` or `OR` are preserved as logical operators.
- **formula** - the user input is passed through verbatim as an
[AFTS](https://docs.alfresco.com/content-services/latest/develop/search-api/) expression. Use
this mode when the caller already builds valid query syntax.
The `app:fields` entry lists the fields used to expand a `regular` user query:
```json
{
"search": {
...
"app:fields": ["cm:name", "cm:title", "cm:description"]
...
}
}
```
For example, with the configuration above and wildcards enabled, the user query `report` is
expanded to `(cm:name:"report*" OR cm:title:"report*" OR cm:description:"report*")`.
Wildcard matching is controlled by the top-level `search-wildcards-enabled` flag in
`app.config.json` (default `true`). When set to `false`, terms are matched exactly and the
trailing `*` is not added (this also disables the `searchPrefix`/`searchSuffix` of the
[Search text component](../content-services/components/search-text.component.md)):
```json
{
"search-wildcards-enabled": false
}
```
### Sorting
The Sorting configuration section consists of two blocks: