# @soleil-se/search-helper

Simplifying searching in Sitevision.

## Install

```sh
npm install @soleil-se/search-helper
```

## Migration

Migration from version 1 to version 2 of this package.

DataCreator**

[SearchHit](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/SearchHit.html) is now used in DataCreators instead of Node.\
It’s recommended to use the SearchHit as much as possible.

```js
function dataCreator(node) {
function dataCreator(hit) {
  const node = hit.getNode();
  return {
    id: node.getIdentifier(),
    displayName: Properties.get(node, 'displayName'),
  }
}
```

Count**

Property for hit count is renamed from `hitCount` to `count`.

```js
const {
  hits,
  hitCount,
  count,
 } = new SearchHelper().search('*', 0, 10);
```

## Getting started

### Import

```javascript
import SearchHelper from '@soleil-se/search-helper';
```

### Options

* `effectiveCount` - Set [effective count](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/SearchResult.html#getEffectiveCount-int-), otherwise `50` will be used and the value of `start` will be added continuously.
* `removeQueryStopWords` - Remove query stop words for available languages.

```javascript
const searchHelper = new SearchHelper({
  effectiveCount: 50,
  removeQueryStopWords: false,
});
```

### `search(query, start, num)`

Performs a search.

```javascript
const result = new SearchHelper()
  .search('*', 0, 10);
```

`result` is an object containing:

```javascript
query: String, // The search query.
count: Number, // Hit count.
sort: Array, // Array of used sort fields.
filters: Array, // Array of used filters.
highlight: Array, // Array of used highlight fields.
time: String, // Time and date search was performed.
hits: Array, // Array of search hits.
suggestions: Array, // Suggestions if SpellCheck was used.
```

### `wildcardSearch(query, start, num)`

Performs a search with a wildcard query.

### `phraseSearch(query, start, num)`

Performs a search with a phrase query.

### `addDataCreator(dataCreator)`

Adds a Data Creator, uses [SearchHit](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/SearchHit.html).

```javascript
import Properties from '@sitevision/api/server/Properties';


function dataCreator(hit) {
  return {
    id: hit.getField('id'),
    name: hit.getField('name'),
  }
}


const result = new SearchHelper()
  .addDataCreator(dataCreator)
  .search('*', 0, 10);
```

Get Node from [SearchHit](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/SearchHit.html) if needed.

```javascript
import Properties from '@sitevision/api/server/Properties';


function dataCreator(hit) {
  const node = hit.getNode();
  return {
    id: node.getIdentifier(),
    displayName: Properties.get(node, 'displayName'),
  }
}


const result = new SearchHelper()
  .addDataCreator(dataCreator)
  .search('*', 0, 10);
```

### `setDisplayQuery(query)`

Set display query.

## Sorting

Functions for handling sort fields.

### `addSort(field, isDescending)`

Adds a Sort Field.

```javascript
const result = new SearchHelper()
  .addSort('name', true)
  .search('*', 0, 10);
```

### `removeSort(field)`

Removes a Sort Field.

### `clearSort()`

Clears all Sort Fields.

## Filtering

Functions for handling filters.

### `addFilter(filter)`

Adds a Filter.

```javascript
const result = new SearchHelper()
  .addFilter('+svtype:page')
  .search('*', 0, 10);
```

### `removeFilter(filter)`

Removes a Filter.

### `clearFilters()`

Clears all Filters.

## Highlight

Functions for handling highlight fields.

### `addHighlight(field)`

Adds a Highlight Field.

```javascript
const result = new SearchHelper()
  .addHighlight('summary')
  .search('*', 0, 10);
```

### `removeHighlight(field)`

Removes a Highlight Field.

### `clearHighlights()`

Clears all Highlight Fields.

### `setHighlightOptions({ count, tag })`

See [HighlightBuilder](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/searcher/builder/HighlightBuilder.html) for more information.

```javascript
const result = new SearchHelper()
  .addHighlight('summary')
  .setHighlightOptions({ count: 500, tag: 'em' });
  .search('*', 0, 10);
```

## Index

Function for handling indicies.

### `setIndex(indexNode)`

Set index from an index node.

```javascript
const result = new SearchHelper()
  .setIndex(indexNode)
  .search('*', 0, 10);
```

### `setIndexById(indexId)`

Set index from an index ID.

```javascript
const result = new SearchHelper()
  .setIndexById('303.462d60ec167c69393b9667ab')
  .search('*', 0, 10);
```

### `setUserIdentityIndex()`

Sets the user identity index.

### `setUserIndex()`

Sets the user index. `sv:simpleUser` are indexed here.

## Advanced

### `setMonitor()`

Used for query logging.

### `setSpellCheck(query)`

Used for getting suggestions.

### `setParser(parser)`

Set a [Parser](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/searcher/component/Parser.html), can be [ExtendedDismaxParser](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/searcher/builder/ExtendedDismaxParserBuilder.html) or [StandardParser](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/searcher/builder/StandardParserBuilder.html).

```javascript
import StandardParserBuilder from 'StandardParserBuilder';


const standardBuilder = StandardParserBuilder.setDefaultField('name.analyzed');


const result = new SearchHelper()
  .setParser(standardBuilder)
  .search('*', 0, 10);
```

## Static functions

### `getWildcardQuery(query, options)`

Returns a string containing a wildcard query: `+(foo foo*)` or `+(foo foo*) +(bar bar*)` with mutliple words.\
Optional parameter for options `field` and `removeQueryStopWords`.

```javascript
import { getWildcardQuery } from '@soleil-se/search-helper';


// returns: +(foo foo*)
const query = getWildcardQuery('foo');


// returns: +(foo foo*) +(bar bar*)
const query = getWildcardQuery('foo bar');


// returns: +(Kanelbullar Kanelbullar*) +(kardemumma kardemumma*) +(solen solen*)
const query = getWildcardQuery('Kanelbullar med kardemumma i solen', { removeQueryStopWords: true });


// returns: +name.analyzed:(foo foo*)
const query = getWildcardQuery('foo', { field: 'name.analyzed' });
```

### `getPhraseQuery(query, field)`

Returns a string containing a phrase query: `+(foo\ bar foo\ bar*)`.\
Optional parameter for `field`:

```javascript
import { getPhraseQuery } from '@soleil-se/search-helper';


// returns: +(foo foo*)
const query = getPhraseQuery('foo');


// returns: +(foo\ bar foo\ bar*)
const query = getPhraseQuery('foo bar');


// returns: +name.analyzed:(foo\ bar foo\ bar*)
const query = getPhraseQuery('foo bar', 'name.analyzed');
```

### `escapeQuerySyntaxChars(query)`

Escapes query syntax characters.

```javascript
import { escapeQuerySyntaxChars } from '@soleil-se/search-helper';


// returns: foo\+bar
const query = escapeQuerySyntaxChars('foo+bar');
```

### `removeStopWords(query, lang)`

Remove stop words from a query string, returns a string without stop words.

* `query` - The query string to remove stop words from.
* `lang` - The language of the query string, defaults to the current locale language. Available languages are ‘sv’, ‘en’, ‘no’, ‘de’, ‘es’.

```javascript
import { removeStopWords } from '@soleil-se/search-helper';


// returns: foo bar solen
const query = removeStopWords('foo och bar i solen');


// returns: foo bar solen
const query = removeStopWords('foo och bar i solen', 'sv');
```

### `getClickTrackingUri(hit)`

> **DEPRECATED**
>
> Use [SearchHit.getClickTrackingUri()](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/SearchHit.html#getClickTrackingUri--) instead, which is available since Sitevision 2023.07.1.

Returns a click tracking URI from a [SearchHit](https://developer.sitevision.se/webdav/files/apidocs/senselogic/sitevision/api/search/SearchHit.html).

## TypeScript

SearchHelper supports TypeScript generics for fully typed results when using data creators.

```typescript
import SearchHelper from '@soleil-se/search-helper';


// Define your custom hit type
type MyHit = {
  id: string;
  name: string;
  url: string;
  description?: string;
};


// Create a data creator function
function hitDataCreator(hit: SearchHit): MyHit {
  return {
    id: hit.getField('id'),
    name: hit.getField('name'),
    url: hit.getField('url'),
    description: hit.getField('description'),
  };
}


// Use generic SearchHelper for typed results
function search(query: string) {
  return new SearchHelper<MyHit>()
    .addDataCreator(hitDataCreator)
    .addFilter('+svtype:page')
    .search(query, 0, 50);
}


// Usage with full type safety
const results = search('foo');


// results.hits is now typed as MyHit[]
results.hits.forEach((hit) => {
  console.log(`${hit.name}: ${hit.url}`);
  // TypeScript knows these properties exist!
});
```

### Multiple Data Creators

You can combine multiple data creators, and their results will be merged:

```typescript
type MyEnhancedHit = MyHit & {
  title: string;
  publishDate: string;
};


function titleDataCreator(hit: SearchHit) {
  return {
    title: hit.getField('title')
  };
}


function publishDateDataCreator(hit: SearchHit) {
  return {
    publishDate: new Date(hit.getLong('published')).toLocaleDateString()
  };
}


function searchEnhanced(query: string) {
  return new SearchHelper<MyEnhancedHit>()
    .addDataCreator(hitDataCreator)
    .addDataCreator(titleDataCreator)
    .addDataCreator(publishDateDataCreator)
    .search(query, 0, 50);
}
```

### Fallback for Non-Generic Usage

If you don’t specify a generic type, SearchHelper defaults to `SearchHit`:

```typescript
// This returns SearchHelperResult<SearchHit>
const defaultResults = new SearchHelper()
  .search('query', 0, 10);


// defaultResults.hits is SearchHit[]
```