In the previous posts, we had know how to get All Documents in Index and show them with pagination. This tutorial show you way to implement a simple Full Text Search in an Angular 6 Application.
Related Posts:
– Angular 6 ElasticSearch – Quick Start – How to add Elasticsearch.js
– Angular 6 ElasticSearch example – Add Document to Index
– Angular 6 ElasticSearch example – Get All Documents in Index
– Angular 6 ElasticSearch example – Documents Pagination with Scroll
Elasticsearch Tutorials:
– Elasticsearch Overview
– ElasticSearch Filter vs Query
– ElasticSearch Full Text Queries – Basic
Elasticsearch full text Search
Add ElasticSearch to Angular 6 Project
Please visit Angular 6 ElasticSearch – Quick Start – How to add Elasticsearch.js for details.
With the post, you will know how to:
– Add ElasticSearch to Angular 6 Project
– Use it in the project
Add Document & Get All Documents in Index
– Add Document:
>> Please visit Angular 6 ElasticSearch example – Add Document to Index.
– Get All Documents:
>> Please visit Angular 6 ElasticSearch example – Get All Documents in Index.
>> with Pagination: Angular 6 ElasticSearch example – Documents Pagination with Scroll
Simple Full Text Search
Using Client.search()
function with match_phrase_prefix
, we create a simple full text search function:
fullTextSearch(_index, _type, _field, _queryText): any { return this.client.search({ index: _index, type: _type, filterPath: ['hits.hits._source', 'hits.total', '_scroll_id'], body: { 'query': { 'match_phrase_prefix': { [_field]: _queryText, } } }, // response for each document with only 'fullname' and 'address' fields '_source': ['fullname', 'address'] }); }
In the component to implement searching, we create html page that catch any key-up event in the text box:
<input type="text" (keyup)="search($event)" placeholder="enter address" class="input"> <div *ngFor="let customerSource of customerSources"> <customer-details [customer]='customerSource._source'></customer-details> </div> <div *ngIf="customerSources?.length < 1"> <p>Not found!</p> </div>
And search()
function:
search($event) { this.queryText = $event.target.value; this.es.fullTextSearch(INDEX,TYPE,'field', this.queryText).then( response => { this.customerSources = response.hits.hits; console.log(response); }, error => { console.error(error); }).then(() => { console.log('Search Completed!'); }); } }
To prevent hit the database after every keypress (it will make multiple queries to ElasticSearch), we implement a timeout by keeping track of the last keypress time, then update the subjects only if the last keypress was more than 100ms ago:
lastKeypress = 0; search($event) { if ($event.timeStamp - this.lastKeypress > 100) { this.queryText = $event.target.value; this.es.fullTextSearch(...); } this.lastKeypress = $event.timeStamp; }
So, with query:
-> POST http://localhost:9200/gkz_index/customer/_search?filter_path=hits.hits._source%2Chits.total%2C_scroll_id&_source=fullname%2Caddress { "query": { "match_phrase_prefix": { "address": "ave" } } }
The result will be like:
<- 200 { "hits": { "total": 2, "hits": [ { "_source": { "address": "1964 Facilisis Avenue\nBell Gardens Texas 87065", "fullname": "Katherin Kohen" } }, { "_source": { "address": "P.O. Box 120 2410 Odio Avenue\nPass Christian Delaware 03869", "fullname": "Merritt Watson" } } ] } }
Practice
0. Overview
Goal:
1. Generate Service & Components
Run cmd:
– ng g s elasticsearch
– ng g c customer/add-customer
– ng g c customer/customer-details
– ng g c customer/show-customers
– ng g c customer/search-customers
=> Project Structure:
2. App Module
app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { AddCustomerComponent } from './customer/add-customer/add-customer.component'; import { CustomerDetailsComponent } from './customer/customer-details/customer-details.component'; import { ShowCustomersComponent } from './customer/show-customers/show-customers.component'; import { SearchCustomersComponent } from './customer/search-customers/search-customers.component'; @NgModule({ declarations: [ AppComponent, AddCustomerComponent, CustomerDetailsComponent, ShowCustomersComponent, SearchCustomersComponent ], imports: [ BrowserModule, FormsModule, ReactiveFormsModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
3. ElasticSearch Service
elasticsearch.service.ts
import { Injectable } from '@angular/core'; import { Client } from 'elasticsearch-browser'; @Injectable({ providedIn: 'root' }) export class ElasticsearchService { private client: Client; constructor() { if (!this.client) { this.connect(); } } private connect() { this.client = new Client({ host: 'http://localhost:9200', log: 'trace' }); } // ... fullTextSearch(_index, _type, _field, _queryText): any { return this.client.search({ index: _index, type: _type, filterPath: ['hits.hits._source', 'hits.total', '_scroll_id'], body: { 'query': { 'match_phrase_prefix': { [_field]: _queryText, } } }, '_source': ['fullname', 'address'] }); } }
4. Components
customer/customer.interface.ts
export interface Customer { fullname: string; age: number; address: string; published: string; } export interface CustomerSource { source: Customer; }
4.1 Add Document Component
Please visit: 4_Add_Document_Component (in the previous post) for details.
4.2 Details Component
You can find it at: Details_Component.
In this tutorial, we just modify code to hide age
and published
:
<div *ngIf="customer"> <div> <label>Full Name: </label> {{customer.fullname}} </div> <div *ngIf="customer.age > 0"> <label>Age: </label> {{customer.age}} </div> <div> <label>Address: </label> {{customer.address}} </div> <div *ngIf="customer.published?.length > 0"> <label>Published: </label> {{customer.published}} </div> <hr/> </div>
4.3 Show Documents Component
Please visit:
- Show_Documents_Component
- or Show_Documents_Component with Pagination
3.4 Search Documents Component
search-customers.component.ts
import { Component, OnInit } from '@angular/core'; import { CustomerSource } from '../customer.interface'; import { ElasticsearchService } from '../../elasticsearch.service'; @Component({ selector: 'app-search-customers', templateUrl: './search-customers.component.html', styleUrls: ['./search-customers.component.css'] }) export class SearchCustomersComponent implements OnInit { private static readonly INDEX = 'gkz_index'; private static readonly TYPE = 'customer'; customerSources: CustomerSource[]; private queryText = ''; private lastKeypress = 0; constructor(private es: ElasticsearchService) { this.queryText = ''; } ngOnInit() { } search($event) { if ($event.timeStamp - this.lastKeypress > 100) { this.queryText = $event.target.value; this.es.fullTextSearch( SearchCustomersComponent.INDEX, SearchCustomersComponent.TYPE, 'address', this.queryText).then( response => { this.customerSources = response.hits.hits; console.log(response); }, error => { console.error(error); }).then(() => { console.log('Search Completed!'); }); } this.lastKeypress = $event.timeStamp; } }
search-customers.component.html
<div style="margin-top:20px"> <input type="text" (keyup)="search($event)" placeholder="enter address" class="input"> <div *ngFor="let customerSource of customerSources" style="margin-top:20px"> <customer-details [customer]='customerSource._source'></customer-details> </div> <div *ngIf="customerSources?.length < 1" style="margin-top:20px"> <p>Not found!</p> </div> </div>
5. App Routing Module
app-routing.module.ts
import { AddCustomerComponent } from './customer/add-customer/add-customer.component'; import { ShowCustomersComponent } from './customer/show-customers/show-customers.component'; import { SearchCustomersComponent } from './customer/search-customers/search-customers.component'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: '', redirectTo: 'add', pathMatch: 'full' }, { path: 'add', component: AddCustomerComponent }, { path: 'customers', component: ShowCustomersComponent }, { path: 'search', component: SearchCustomersComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
6. App Component
app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'ozenero'; description = 'Angular - Elasticsearch'; }
app.component.html
<div class="container" style="width: 400px"> <div style="color: blue; margin-bottom: 20px"> <h2>{{title}}</h2> <h4>{{description}}</h4> </div> <nav> <a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a> <a routerLink="customers" class="btn btn-primary active" role="button" routerLinkActive="active">Customers</a> <a routerLink="search" class="btn btn-primary active" role="button" routerLinkActive="active">Search by Address</a> </nav> <router-outlet></router-outlet> </div>
7. Check Result
Run Angular 6 App, go to http://localhost:4200/
, add Customer Data, then choose Search by Address tab:
Sourcecode
Angular6Elasticsearch-full-text-search