This commit is contained in:
parent
588f60391a
commit
9d92a20872
7 changed files with 135 additions and 76 deletions
19
src/controllers/SearchController.js
Normal file
19
src/controllers/SearchController.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export default class SearchController {
|
||||
searchService
|
||||
|
||||
constructor(searchService) {
|
||||
this.searchService = searchService
|
||||
}
|
||||
|
||||
search = (req, res) => {
|
||||
const query = req.params.query
|
||||
const results = this.searchService.search(query)
|
||||
if (!results) {
|
||||
return res.status(404).json({
|
||||
message: `No matches for query ${query}`,
|
||||
})
|
||||
}
|
||||
|
||||
return res.json(results)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
import express from "express"
|
||||
import entries from "./routes/entries.js"
|
||||
import tags from "./routes/tags.js"
|
||||
import search from "./routes/search.js"
|
||||
import cors from "cors"
|
||||
import { validateApiKey } from "./middlewear/auth.js"
|
||||
import morgan from "morgan"
|
||||
|
||||
const app = express()
|
||||
|
||||
const port = process.env.PORT || 4000
|
||||
|
|
@ -14,12 +16,13 @@ app.use(express.json())
|
|||
app.use("/", validateApiKey)
|
||||
app.use("/entries", entries)
|
||||
app.use("/tags", tags)
|
||||
app.use("/search", search)
|
||||
|
||||
app.listen(port, () => {
|
||||
console.info(`TB-INFO eolas-api running on NodeJS ${process.version}`)
|
||||
console.info(`TB-INFO eolas-api server running at http://localhost:${port}`)
|
||||
console.info(`TB-INFO eolas-api running on NodeJS ${process.version}`)
|
||||
console.info(`TB-INFO eolas-api server running at http://localhost:${port}`)
|
||||
})
|
||||
|
||||
app.get("/health", (req, res) => {
|
||||
res.status(200).json({ status: "ok" })
|
||||
res.status(200).json({ status: "ok" })
|
||||
})
|
||||
|
|
|
|||
12
src/routes/search.js
Normal file
12
src/routes/search.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import express from "express"
|
||||
import SearchService from "../services/SearchService.js"
|
||||
import SearchController from "../controllers/SearchController.js"
|
||||
import database from "../db/connection.js"
|
||||
|
||||
const router = express.Router()
|
||||
const searchService = new SearchService(database)
|
||||
const searchController = new SearchController(searchService)
|
||||
|
||||
router.get("/:query", searchController.search)
|
||||
|
||||
export default router
|
||||
|
|
@ -1,68 +1,73 @@
|
|||
import { GET_ALL_ENTRIES, GET_ENTRY, GET_ENTRIES_FOR_TAG, GET_BACKLINKS_FOR_ENTRY, GET_OUTLINKS_FOR_ENTRY } from "../sql/entries.js"
|
||||
import {
|
||||
GET_ALL_ENTRIES,
|
||||
GET_ENTRY,
|
||||
GET_ENTRIES_FOR_TAG,
|
||||
GET_BACKLINKS_FOR_ENTRY,
|
||||
GET_OUTLINKS_FOR_ENTRY,
|
||||
} from "../sql/entries.js"
|
||||
|
||||
export default class EntriesService {
|
||||
database
|
||||
database
|
||||
|
||||
constructor(database) {
|
||||
this.database = database
|
||||
}
|
||||
constructor(database) {
|
||||
this.database = database
|
||||
}
|
||||
|
||||
getEntry = (title) => {
|
||||
return this.database.prepare(GET_ENTRY).get(title)
|
||||
}
|
||||
getEntry = (title) => {
|
||||
return this.database.prepare(GET_ENTRY).get(title)
|
||||
}
|
||||
|
||||
getAllEntries = (sort, limit) => {
|
||||
const entries = this.database.prepare(GET_ALL_ENTRIES).all()
|
||||
getAllEntries = (sort, limit) => {
|
||||
const entries = this.database.prepare(GET_ALL_ENTRIES).all()
|
||||
|
||||
const sorted =
|
||||
sort === "date" ? this._sortByDate(entries) : this._sortByTitle(entries, "title")
|
||||
const sorted =
|
||||
sort === "date" ? this._sortByDate(entries) : this._sortByTitle(entries, "title")
|
||||
|
||||
const sliced = sorted.slice(0, Number(limit) || -1)
|
||||
const sliced = sorted.slice(0, Number(limit) || -1)
|
||||
|
||||
return {
|
||||
count: sliced.length,
|
||||
data: sliced,
|
||||
}
|
||||
}
|
||||
return {
|
||||
count: sliced.length,
|
||||
data: sliced,
|
||||
}
|
||||
}
|
||||
|
||||
getEntriesForTag = (tag, sort) => {
|
||||
const entries = this.database.prepare(GET_ENTRIES_FOR_TAG).all(tag)
|
||||
return {
|
||||
count: entries.length,
|
||||
data:
|
||||
sort === "date"
|
||||
? this._sortByDate(entries)
|
||||
: this._sortByTitle(entries, "entry_title"),
|
||||
}
|
||||
}
|
||||
getEntriesForTag = (tag, sort) => {
|
||||
const entries = this.database.prepare(GET_ENTRIES_FOR_TAG).all(tag)
|
||||
return {
|
||||
count: entries.length,
|
||||
data:
|
||||
sort === "date"
|
||||
? this._sortByDate(entries)
|
||||
: this._sortByTitle(entries, "entry_title"),
|
||||
}
|
||||
}
|
||||
|
||||
getBacklinksForEntry = (title) => {
|
||||
const backlinks = this.database.prepare(GET_BACKLINKS_FOR_ENTRY).all(title)
|
||||
const sorted = this._sortByTitle(backlinks, "source_entry_title")
|
||||
const list = sorted.flatMap((i) => i.source_entry_title)
|
||||
return {
|
||||
count: backlinks.length,
|
||||
data: list
|
||||
}
|
||||
}
|
||||
getBacklinksForEntry = (title) => {
|
||||
const backlinks = this.database.prepare(GET_BACKLINKS_FOR_ENTRY).all(title)
|
||||
const sorted = this._sortByTitle(backlinks, "source_entry_title")
|
||||
const list = sorted.flatMap((i) => i.source_entry_title)
|
||||
return {
|
||||
count: backlinks.length,
|
||||
data: list,
|
||||
}
|
||||
}
|
||||
|
||||
getOutlinksForEntry = (title) => {
|
||||
const outlinks = this.database.prepare(GET_OUTLINKS_FOR_ENTRY).all(title)
|
||||
const sorted = this._sortByTitle(outlinks, "target_entry_title")
|
||||
const list = sorted.flatMap((i) => i.target_entry_title)
|
||||
return {
|
||||
count: outlinks.length,
|
||||
data: list
|
||||
}
|
||||
getOutlinksForEntry = (title) => {
|
||||
const outlinks = this.database.prepare(GET_OUTLINKS_FOR_ENTRY).all(title)
|
||||
const sorted = this._sortByTitle(outlinks, "target_entry_title")
|
||||
const list = sorted.flatMap((i) => i.target_entry_title)
|
||||
return {
|
||||
count: outlinks.length,
|
||||
data: list,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
_sortByTitle = (entries, fieldName) => {
|
||||
return entries.sort((a, b) => a[fieldName].localeCompare(b[fieldName]))
|
||||
}
|
||||
|
||||
_sortByTitle = (entries, fieldName) => {
|
||||
return entries.sort((a, b) => a[fieldName].localeCompare(b[fieldName]))
|
||||
}
|
||||
|
||||
_sortByDate = (entries, fieldName = "last_modified") => {
|
||||
const sorted = entries.sort((a, b) => new Date(b[fieldName]) - new Date(a[fieldName]))
|
||||
return sorted
|
||||
}
|
||||
_sortByDate = (entries, fieldName = "last_modified") => {
|
||||
const sorted = entries.sort((a, b) => new Date(b[fieldName]) - new Date(a[fieldName]))
|
||||
return sorted
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
src/services/SearchService.js
Normal file
17
src/services/SearchService.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { SEARCH } from "../sql/search.js"
|
||||
|
||||
export default class SearchService {
|
||||
database
|
||||
|
||||
constructor(database) {
|
||||
this.database = database
|
||||
}
|
||||
|
||||
search = (query) => {
|
||||
const results = this.database.prepare(SEARCH).all(query.trim())
|
||||
return {
|
||||
count: results.length,
|
||||
data: results,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,27 @@
|
|||
import { GET_ALL_TAGS, GET_TAGS_FOR_ENTRY } from "../sql/tags.js"
|
||||
|
||||
export default class TagsService {
|
||||
database
|
||||
database
|
||||
|
||||
constructor(database) {
|
||||
this.database = database
|
||||
}
|
||||
constructor(database) {
|
||||
this.database = database
|
||||
}
|
||||
|
||||
getAllTags = () => {
|
||||
const tags = this.database.prepare(GET_ALL_TAGS).all()
|
||||
const sorted = this._sortTags(tags, "name")
|
||||
const list = sorted.flatMap((tag) => tag.name)
|
||||
return { count: tags.length, data: list }
|
||||
}
|
||||
getAllTags = () => {
|
||||
const tags = this.database.prepare(GET_ALL_TAGS).all()
|
||||
const sorted = this._sortTags(tags, "name")
|
||||
const list = sorted.flatMap((tag) => tag.name)
|
||||
return { count: tags.length, data: list }
|
||||
}
|
||||
|
||||
getTagsForEntry = (entryTitle) => {
|
||||
const tags = this.database.prepare(GET_TAGS_FOR_ENTRY).all(entryTitle)
|
||||
const sorted = this._sortTags(tags, "tag_name")
|
||||
const list = sorted.flatMap((tag) => tag.tag_name)
|
||||
return { count: tags.length, data: list }
|
||||
}
|
||||
getTagsForEntry = (entryTitle) => {
|
||||
const tags = this.database.prepare(GET_TAGS_FOR_ENTRY).all(entryTitle)
|
||||
const sorted = this._sortTags(tags, "tag_name")
|
||||
const list = sorted.flatMap((tag) => tag.tag_name)
|
||||
return { count: tags.length, data: list }
|
||||
}
|
||||
|
||||
_sortTags = (tags, fieldName) => {
|
||||
return tags.sort((a, b) => a[fieldName].localeCompare(b[fieldName]))
|
||||
}
|
||||
_sortTags = (tags, fieldName) => {
|
||||
return tags.sort((a, b) => a[fieldName].localeCompare(b[fieldName]))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
src/sql/search.js
Normal file
3
src/sql/search.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
const SEARCH = `SELECT title as entry, snippet(entries_fts, 1, '<mark>', '</mark>', '...', 24) as excerpt FROM entries_fts WHERE entries_fts MATCH ? ORDER BY rank LIMIT 25`
|
||||
|
||||
export { SEARCH }
|
||||
Loading…
Add table
Reference in a new issue