@ -12,7 +12,7 @@ module.exports = {
* INIT
* INIT
* /
* /
async init ( ) {
async init ( ) {
// -> Create Index
// -> Create Search Index
const indexExists = await WIKI . models . knex . schema . hasTable ( 'pagesVector' )
const indexExists = await WIKI . models . knex . schema . hasTable ( 'pagesVector' )
if ( ! indexExists ) {
if ( ! indexExists ) {
await WIKI . models . knex . schema . createTable ( 'pagesVector' , table => {
await WIKI . models . knex . schema . createTable ( 'pagesVector' , table => {
@ -21,11 +21,19 @@ module.exports = {
table . string ( 'locale' )
table . string ( 'locale' )
table . string ( 'title' )
table . string ( 'title' )
table . string ( 'description' )
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
* QUERY
@ -35,14 +43,20 @@ module.exports = {
* /
* /
async query ( q , opts ) {
async query ( q , opts ) {
try {
try {
let suggestions = [ ]
const results = await WIKI . models . knex . raw ( `
const results = await WIKI . models . knex . raw ( `
SELECT id , path , locale , title , description
SELECT id , path , locale , title , description
FROM "pagesVector" , to_tsquery ( ? ) query
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)])
` , [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 {
return {
results : results . rows ,
results : results . rows ,
suggestions : [ ] ,
suggestions ,
totalHits : results . rows . length
totalHits : results . rows . length
}
}
} catch ( err ) {
} catch ( err ) {
@ -58,8 +72,8 @@ module.exports = {
* /
* /
async created ( page ) {
async created ( page ) {
await WIKI . models . knex . raw ( `
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])
` , [page.path, page.locale, page.title, page.description, page.title, page.description, page.content])
} ,
} ,
@ -73,9 +87,9 @@ module.exports = {
UPDATE "pagesVector" SET
UPDATE "pagesVector" SET
title = '?' ,
title = '?' ,
description = '?' ,
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
WHERE path = '?' AND locale = '?' LIMIT 1
` , [page.title, page.description, page.title, page.description, page.content, page.path, page.locale])
` , [page.title, page.description, page.title, page.description, page.content, page.path, page.locale])
} ,
} ,
@ -110,8 +124,11 @@ module.exports = {
async rebuild ( ) {
async rebuild ( ) {
await WIKI . models . knex ( 'pagesVector' ) . truncate ( )
await WIKI . models . knex ( 'pagesVector' ) . truncate ( )
await WIKI . models . knex . raw ( `
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"
FROM "pages"
WHERE pages . "isPublished" AND NOT pages . "isPrivate" ` )
WHERE pages . "isPublished" AND NOT pages . "isPrivate" ` )
}
}