Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Commit

Permalink
Implement basic search api
Browse files Browse the repository at this point in the history
  • Loading branch information
velopert committed Dec 6, 2018
1 parent 04bb244 commit d835255
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 13 deletions.
6 changes: 5 additions & 1 deletion GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ $ psql postgres
psql (10.4)
Type "help" for help.
postgres=# CREATE DATABASE velog;
postgres=# CREATE DATABASE velog
LC_COLLATE 'C'
LC_CTYPE 'C'
ENCODING 'UTF8'
TEMPLATE template0;
CREATE DATABASE
postgres=# CREATE USER velog WITH ENCRYPTED PASSWORD 'velogpw';
CREATE ROLE
Expand Down
17 changes: 17 additions & 0 deletions velog-backend/sql/posts_tsv.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
alter table posts
add column tsv tsvector;

create index posts_tsv on posts using gin(tsv);

update posts set tsv = to_tsvector(coalesce(title)) || to_tsvector(coalesce(body));

create or replace function posts_tsv_trigger() returns trigger as $$
begin
new.tsv := to_tsvector(coalesce(new.title)) || to_tsvector(coalesce(new.body));
return new;
end
$$ language plpgsql;


create trigger tsvectorupdate before insert or update on posts
for each row execute procedure posts_tsv_trigger();
63 changes: 63 additions & 0 deletions velog-backend/src/database/rawQuery/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// @flow
import Sequelize from 'sequelize';
import db from 'database/db';

type SearchParameter = {
tsquery: string,
fk_user_id?: string,
authorized?: boolean,
page?: number,
};

type SearchDataRow = {
id: string,
rank: number,
};

export const searchPosts = async ({
tsquery,
fk_user_id,
authorized,
page = 1,
}: SearchParameter): Promise<SearchDataRow[]> => {
const query = `
SELECT id, ts_rank(tsv, TO_TSQUERY($tsquery)) * 1000 + total_score AS rank
FROM posts
JOIN (select fk_post_id, SUM(score) as total_score from post_scores group by fk_post_id) as q on q.fk_post_id = posts.id
WHERE tsv @@ TO_TSQUERY($tsquery)
ORDER BY rank DESC
OFFSET ${10 * (page - 1)}
LIMIT 10
`;

try {
const rows = await db.query(query, {
bind: { tsquery },
});
return rows[0];
} catch (e) {
throw e;
}
};

export const countSearchPosts = async ({
tsquery,
fk_user_id,
authorized,
page = 1,
}: SearchParameter): Promise<number> => {
const query = `
SELECT COUNT(*) as count
FROM posts
WHERE tsv @@ TO_TSQUERY($tsquery)
`;

try {
const rows = await db.query(query, {
bind: { tsquery },
});
return rows[0][0].count;
} catch (e) {
throw e;
}
};
9 changes: 3 additions & 6 deletions velog-backend/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import Router from 'koa-router';
import type { Context } from 'koa';
import needsAuth from 'lib/middlewares/needsAuth';
import downloadImage from 'lib/downloadImage';
import crypto from 'crypto';
import { PostReadcounts } from 'database/views';
import auth from './auth';
import posts from './posts';
import files from './files';
Expand All @@ -15,8 +12,7 @@ import common from './common';
import sitemaps from './sitemaps';
import internal from './internal';
import atom from './atom';
import Post from '../database/models/Post';
import { getTagsList } from '../database/rawQuery/tags';
import search from './search/search';

const router: Router = new Router();

Expand All @@ -30,11 +26,12 @@ router.use('/common', common.routes());
router.use('/sitemaps', sitemaps.routes());
router.use('/internal', internal.routes());
router.use('/atom', atom.routes());
router.use('/search', search.routes());

router.get('/check', (ctx: Context) => {
console.log('avoiding cold start...');
ctx.body = {
version: '1.0.0-alpha.0',
version: '1.0.0',
origin: ctx.origin,
env: process.env.NODE_ENV,
};
Expand Down
6 changes: 0 additions & 6 deletions velog-backend/src/router/posts/posts.ctrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,25 +457,19 @@ export const listTrendingPosts = async (ctx: Context) => {

// check cursor
try {
console.time('getTrendingPosts');
const postIds = await getTrendingPosts(offset || 0);
console.timeEnd('getTrendingPosts');
if (!postIds || postIds.length === 0) {
ctx.body = [];
return;
}
console.time('readPostsByIds');
const posts = await Post.readPostsByIds(postIds.map(postId => postId.post_id));
const filtered = posts.filter(p => !p.is_private);
console.timeEnd('readPostsByIds');
const data = filtered
.map(serializePost)
.map(post => ({ ...post, body: formatShortDescription(post.body) }));

// retrieve commentCounts and inject
console.time('getCommentCounts');
const commentCounts = await getCommentCountsOfPosts(filtered.map(p => p.id));
console.timeEnd('getCommentCounts');
ctx.body = injectCommentCounts(data, commentCounts);
} catch (e) {
ctx.throw(500, e);
Expand Down
32 changes: 32 additions & 0 deletions velog-backend/src/router/search/search.ctrl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// @flow
import type { Context } from 'koa';
import Post, { serializePost } from 'database/models/Post';
import { searchPosts, countSearchPosts } from 'database/rawQuery/search';
import { formatShortDescription } from 'lib/common';

export const publicSearch = async (ctx: Context) => {
const { q } = ctx.query;
const transformed = `${q.replace(/ /, '|')}:*`;
try {
const [count, searchResult] = await Promise.all([
countSearchPosts({
tsquery: transformed,
}),
searchPosts({
tsquery: transformed,
}),
]);

const postIds = searchResult.map(r => r.id);
const posts = await Post.readPostsByIds(postIds);
const data = posts
.map(serializePost)
.map(post => ({ ...post, body: formatShortDescription(post.body) }));
ctx.body = {
count,
data,
};
} catch (e) {
ctx.throw(500, e);
}
};
8 changes: 8 additions & 0 deletions velog-backend/src/router/search/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @flow
import Router from 'koa-router';
import { publicSearch } from './search.ctrl';

const search = new Router();

search.get('/public', publicSearch);
export default search;

0 comments on commit d835255

Please sign in to comment.