add elastic search
This commit is contained in:
@@ -1,4 +1,16 @@
|
|||||||
import {Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Req} from '@nestjs/common';
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
UseGuards,
|
||||||
|
Req,
|
||||||
|
Query,
|
||||||
|
ClassSerializerInterceptor, UseInterceptors
|
||||||
|
} from '@nestjs/common';
|
||||||
import {PostsService} from './posts.service';
|
import {PostsService} from './posts.service';
|
||||||
import {CreatePostDto} from './dto/create-post.dto';
|
import {CreatePostDto} from './dto/create-post.dto';
|
||||||
import {UpdatePostDto} from './dto/update-post.dto';
|
import {UpdatePostDto} from './dto/update-post.dto';
|
||||||
@@ -6,6 +18,7 @@ import JwtAuthenticationGuard from "../authentication/jwt-authentication.guard";
|
|||||||
import RequestWithUser from "../authentication/requestWithUser.interface";
|
import RequestWithUser from "../authentication/requestWithUser.interface";
|
||||||
|
|
||||||
@Controller('posts')
|
@Controller('posts')
|
||||||
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
export class PostsController {
|
export class PostsController {
|
||||||
constructor(private readonly postsService: PostsService) {
|
constructor(private readonly postsService: PostsService) {
|
||||||
}
|
}
|
||||||
@@ -16,6 +29,15 @@ export class PostsController {
|
|||||||
return this.postsService.createPost(post, req.user);
|
return this.postsService.createPost(post, req.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
async getPosts(@Query('search') search: string) {
|
||||||
|
if (search) {
|
||||||
|
return this.postsService.searchForPosts(search);
|
||||||
|
}
|
||||||
|
return this.postsService.getAllPosts();
|
||||||
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
findAll() {
|
findAll() {
|
||||||
return this.postsService.findAll();
|
return this.postsService.findAll();
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ import {InjectRepository} from "@nestjs/typeorm";
|
|||||||
import {Repository} from "typeorm";
|
import {Repository} from "typeorm";
|
||||||
import Post from './entities/post.entity';
|
import Post from './entities/post.entity';
|
||||||
import {PostNotFoundException} from "./exception/postNotFound.exception";
|
import {PostNotFoundException} from "./exception/postNotFound.exception";
|
||||||
|
import PostsSearchService from "./postsSearch.service";
|
||||||
|
import {In} from "typeorm";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PostsService {
|
export class PostsService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Post) private repo: Repository<Post>,
|
@InjectRepository(Post) private repo: Repository<Post>,
|
||||||
|
private postsSearchService: PostsSearchService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +27,19 @@ export class PostsService {
|
|||||||
return newPost;
|
return newPost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async searchForPosts(text: string) {
|
||||||
|
const results = await this.postsSearchService.search(text);
|
||||||
|
const ids = results.flatMap(result => result.hits.hits.map(hit => hit._source.id));
|
||||||
|
if (!ids.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.repo
|
||||||
|
.find({
|
||||||
|
where: {id: In(ids)}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getAllPosts() {
|
getAllPosts() {
|
||||||
return this.repo.find({relations: ['author']});
|
return this.repo.find({relations: ['author']});
|
||||||
@@ -64,11 +80,26 @@ export class PostsService {
|
|||||||
return `This action returns a #${id} post`;
|
return `This action returns a #${id} post`;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(id: number, updatePostDto: UpdatePostDto) {
|
async update(id: number, post: UpdatePostDto) {
|
||||||
return `This action updates a #${id} post`;
|
await this.repo.update(id, post);
|
||||||
|
const updatedPost = await this.repo.findOne({
|
||||||
|
where: {id},
|
||||||
|
relations: {
|
||||||
|
author: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (updatedPost) {
|
||||||
|
await this.postsSearchService.update(updatedPost);
|
||||||
|
return updatedPost;
|
||||||
|
}
|
||||||
|
throw new PostNotFoundException(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(id: number) {
|
async remove(id: number) {
|
||||||
return `This action removes a #${id} post`;
|
const deleteResponse = await this.repo.delete(id);
|
||||||
|
if (!deleteResponse.affected) {
|
||||||
|
throw new PostNotFoundException(id);
|
||||||
|
}
|
||||||
|
await this.postsSearchService.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,81 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import {Injectable} from '@nestjs/common';
|
||||||
import { ElasticsearchService } from '@nestjs/elasticsearch';
|
import {ElasticsearchService} from '@nestjs/elasticsearch';
|
||||||
import Post from "./entities/post.entity";
|
import Post from "./entities/post.entity";
|
||||||
import {PostSearchResult} from "./types/postSearchResult.interface";
|
import {PostSearchResult} from "./types/postSearchResult.interface";
|
||||||
import {PostSearchBody} from "./types/postSearchBody.interface";
|
import {PostSearchBody} from "./types/postSearchBody.interface";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class PostsSearchService {
|
export default class PostsSearchService {
|
||||||
index = 'posts'
|
index = 'posts';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly elasticsearchService: ElasticsearchService
|
private readonly elasticsearchService: ElasticsearchService
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
async indexPost(post: Post) {
|
async indexPost(post: Post) {
|
||||||
return this.elasticsearchService.index<PostSearchBody>({
|
return this.elasticsearchService.index<PostSearchBody>({
|
||||||
index: this.index,
|
index: this.index,
|
||||||
body: {
|
document: {
|
||||||
id: post.id,
|
id: post.id,
|
||||||
title: post.title,
|
title: post.title,
|
||||||
content: post.content,
|
content: post.content,
|
||||||
authorId: post.author.id
|
authorId: post.author.id
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async search(text: string) {
|
||||||
|
const result = await this.elasticsearchService.search<PostSearchResult>({
|
||||||
|
index: this.index,
|
||||||
|
query: {
|
||||||
|
multi_match: {
|
||||||
|
query: text,
|
||||||
|
fields: ['title', 'content'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const hits = result.hits.hits;
|
||||||
|
return hits.map((item) => item._source);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async remove(postId: number) {
|
||||||
|
await this.elasticsearchService.deleteByQuery({
|
||||||
|
index: this.index,
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
id: postId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(post: Post) {
|
||||||
|
const newBody: PostSearchBody = {
|
||||||
|
id: post.id,
|
||||||
|
title: post.title,
|
||||||
|
content: post.content,
|
||||||
|
authorId: post.author.id
|
||||||
|
};
|
||||||
|
|
||||||
|
const script = Object.entries(newBody).reduce((result, [key, value]) => {
|
||||||
|
return `${result} ctx._source.${key}='${value}';`;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
await this.elasticsearchService.updateByQuery({
|
||||||
|
index: this.index,
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
id: post.id,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
script: {
|
||||||
|
source: script
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async search(text: string) {
|
|
||||||
const response = await this.elasticsearchService.search<PostSearchResult>({
|
|
||||||
index: this.index,
|
|
||||||
body: {
|
|
||||||
query: {
|
|
||||||
multi_match: {
|
|
||||||
query: text,
|
|
||||||
fields: ['title', 'content']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const hits = body.hits.hits;
|
|
||||||
return hits.map((item) => item._source);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {PostSearchBody} from "./postSearchBody.interface";
|
||||||
|
|
||||||
export interface PostSearchResult {
|
export interface PostSearchResult {
|
||||||
hits: {
|
hits: {
|
||||||
|
|||||||
Reference in New Issue
Block a user