feat: search suggestions + results UI improvements

This commit is contained in:
Nick
2019-03-10 01:28:58 -05:00
parent ab42e5e1ab
commit f7664339f4
4 changed files with 80 additions and 18 deletions

View File

@@ -12,7 +12,7 @@ module.exports = {
* INIT
*/
async init() {
// -> Create Index
// -> Create Search Index
const indexExists = await WIKI.models.knex.schema.hasTable('pagesVector')
if (!indexExists) {
await WIKI.models.knex.schema.createTable('pagesVector', table => {
@@ -21,11 +21,19 @@ module.exports = {
table.string('locale')
table.string('title')
table.string('description')
table.specificType('titleTk', 'TSVECTOR')
table.specificType('descriptionTk', 'TSVECTOR')
table.specificType('contentTk', 'TSVECTOR')
table.specificType('tokens', 'TSVECTOR')
})
}
// -> Create Words Index
const wordsExists = await WIKI.models.knex.schema.hasTable('pagesWords')
if (!wordsExists) {
await WIKI.models.knex.raw(`
CREATE TABLE "pagesWords" AS SELECT word FROM ts_stat(
'SELECT to_tsvector(''simple'', pages."title") || to_tsvector(''simple'', pages."description") || to_tsvector(''simple'', pages."content") FROM pages WHERE pages."isPublished" AND NOT pages."isPrivate"'
)`)
await WIKI.models.knex.raw('CREATE EXTENSION IF NOT EXISTS pg_trgm')
await WIKI.models.knex.raw(`CREATE INDEX "pageWords_idx" ON "pagesWords" USING GIN (word gin_trgm_ops)`)
}
},
/**
* QUERY
@@ -35,14 +43,20 @@ module.exports = {
*/
async query(q, opts) {
try {
let suggestions = []
const results = await WIKI.models.knex.raw(`
SELECT id, path, locale, title, description
FROM "pagesVector", to_tsquery(?) query
WHERE (query @@ "titleTk") OR (query @@ "descriptionTk") OR (query @@ "contentTk")
WHERE query @@ "tokens"
ORDER BY ts_rank(tokens, query) DESC
`, [tsquery(q)])
if (results.rows.length < 5) {
const suggestResults = await WIKI.models.knex.raw(`SELECT word, word <-> ? AS rank FROM "pagesWords" WHERE similarity(word, ?) > 0.2 ORDER BY rank LIMIT 5;`, [q, q])
suggestions = suggestResults.rows.map(r => r.word)
}
return {
results: results.rows,
suggestions: [],
suggestions,
totalHits: results.rows.length
}
} catch (err) {
@@ -58,8 +72,8 @@ module.exports = {
*/
async created(page) {
await WIKI.models.knex.raw(`
INSERT INTO "pagesVector" (path, locale, title, description, "titleTk", "descriptionTk", "contentTk") VALUES (
'?', '?', '?', '?', to_tsvector('?'), to_tsvector('?'), to_tsvector('?')
INSERT INTO "pagesVector" (path, locale, title, description, tokens) VALUES (
'?', '?', '?', '?', (setweight(to_tsvector('${this.config.dictLanguage}', '?'), 'A') || setweight(to_tsvector('${this.config.dictLanguage}', '?'), 'B') || setweight(to_tsvector('${this.config.dictLanguage}', '?'), 'C'))
)
`, [page.path, page.locale, page.title, page.description, page.title, page.description, page.content])
},
@@ -73,9 +87,9 @@ module.exports = {
UPDATE "pagesVector" SET
title = '?',
description = '?',
"titleTk" = to_tsvector('?'),
"descriptionTk" = to_tsvector('?'),
"contentTk" = to_tsvector('?')
tokens = (setweight(to_tsvector('${this.config.dictLanguage}', '?'), 'A') ||
setweight(to_tsvector('${this.config.dictLanguage}', '?'), 'B') ||
setweight(to_tsvector('${this.config.dictLanguage}', '?'), 'C'))
WHERE path = '?' AND locale = '?' LIMIT 1
`, [page.title, page.description, page.title, page.description, page.content, page.path, page.locale])
},
@@ -110,8 +124,11 @@ module.exports = {
async rebuild() {
await WIKI.models.knex('pagesVector').truncate()
await WIKI.models.knex.raw(`
INSERT INTO "pagesVector" (path, locale, title, description, "titleTk", "descriptionTk", "contentTk")
SELECT path, "localeCode" AS locale, title, description, to_tsvector(title) AS "titleTk", to_tsvector(description) AS "descriptionTk", to_tsvector(content) AS "contentTk"
INSERT INTO "pagesVector" (path, locale, title, description, "tokens")
SELECT path, "localeCode" AS locale, title, description,
(setweight(to_tsvector('${this.config.dictLanguage}', title), 'A') ||
setweight(to_tsvector('${this.config.dictLanguage}', description), 'B') ||
setweight(to_tsvector('${this.config.dictLanguage}', content), 'C')) AS tokens
FROM "pages"
WHERE pages."isPublished" AND NOT pages."isPrivate"`)
}