A LoopBack microservice used for searching over configured models. It uses in-build Full Text Search support in PostgreSQL and MySQL databases to perform searches.
npm i @sourceloop/search-service
-
Create a new Loopback4 Application (If you don't have one already)
lb4 testapp
-
Install the search service
npm i @sourceloop/search-service
-
Bind the configuration for the service to the
SearchServiceBindings.Config
key -this.bind({ useCustomSequence: false, //default type: SearchResult, //default models: [ //required ToDo, //according to project { model: User, columns: { name: 'username', description: 'about', }, }, ], controller: { //optional name: 'ToDo', basePath: '/todo/search', authorizations: ['SearchTodo'], }, });
-
Add the
SearchService
to your Loopback4 Application (inapplication.ts
)// import the SearchServiceComponent import {SearchServiceComponent} from '@sourceloop/search-service'; // add Component for SearchServiceComponent this.component(SearchServiceComponent);
-
Set up a Loopback4 Datasource with
dataSourceName
property set toSearchServiceBindings.DATABASE_NAME
. You can see an example datasource here. This service supports MySQL and PostgreSQL based datasources as of now. -
Start the application
npm start
The service comes with a default sequence with inbuilt authentication and authorization using the loopback4-authentication and loopback4-authorization. If you want to use your custom sequence instead, set this option as true.
Type of the result set, by default it is a model with two properties - name
and description
;
It sets the models and columns over which the search is performed. It's value is a list with two types of values -
- Models - A class that extends the Loopback Model.
- An Object with two properties -
- model - A class that extends the Loopback Model
- columns - A key-value map that maps the keys of the database model to the keys in the result set.
Configuration for the controller, it accepts three properties -
- name - name of the Controller, by default it is SearchController
- basePath - basePath of the controller, be default it is {name}/search, if even the name is not provided, it is just /search
- authentication - if authentication should be required or not, this must be enable to use authorizations
- authorizations - list of string corresponding to the permissions required for the search API, by default equal to
['*']
(allowed for everyone). - recents - provider an endpoint to fetch recent searches. authentication must be enabled to use this.
- recentCount - number of recent searches to be saved for each user, default value is 5.
Here is a Sample Implementation DataSource
implementation using environment variables.
import {inject, lifeCycleObserver, LifeCycleObserver} from '@loopback/core';
import {juggler} from '@loopback/repository';
import {SearchServiceBindings.DATABASE_NAME} from '@sourceloop/search-service';
const config = {
name: SearchServiceBindings.DATABASE_NAME,
connector: 'postgresql',
url: '',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
schema: process.env.DB_SCHEMA,
};
@lifeCycleObserver('datasource')
export class SearchDbDataSource extends juggler.DataSource implements LifeCycleObserver {
static dataSourceName = SearchServiceBindings.DATABASE_NAME;
static readonly defaultConfig = config;
constructor(
// You need to set datasource configuration name as 'datasources.config.search' otherwise you might get Errors
@inject('datasources.config.search', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
}
}
Also if you are using postgres
datasource, you need to add the following function in your db -
CREATE OR REPLACE FUNCTION f_concat_ws(text, VARIADIC text[])
RETURNS text LANGUAGE sql IMMUTABLE AS 'SELECT array_to_string($2, $1)';
And to improve performance define an index like this -
CREATE INDEX tbl_fts_idx ON main.<TABLENAME> USING GIN (
to_tsvector('english', f_concat_ws(' ', <COLUMN1>, <COLUMN2>)));
Authorization: Bearer <token> where <token> is a JWT token signed using JWT issuer and secret.
Content-Type: application/json
in the response and in request if the API method is NOT GET
{version}: Defines the API Version
200: Successful Response. Response body varies w.r.t API 401: Unauthorized: The JWT token is missing or invalid 403: Forbidden : Not allowed to execute the concerned API 404: Entity Not Found 400: Bad Request (Error message varies w.r.t API) 201: No content: Empty Response