feat: add search endpoint
Some checks failed
Deploy eolas-api / deploy (push) Failing after 26s

This commit is contained in:
Thomas Bishop 2025-12-11 19:14:45 +00:00
parent 588f60391a
commit 9d92a20872
7 changed files with 135 additions and 76 deletions

View 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)
}
}

View file

@ -1,9 +1,11 @@
import express from "express" import express from "express"
import entries from "./routes/entries.js" import entries from "./routes/entries.js"
import tags from "./routes/tags.js" import tags from "./routes/tags.js"
import search from "./routes/search.js"
import cors from "cors" import cors from "cors"
import { validateApiKey } from "./middlewear/auth.js" import { validateApiKey } from "./middlewear/auth.js"
import morgan from "morgan" import morgan from "morgan"
const app = express() const app = express()
const port = process.env.PORT || 4000 const port = process.env.PORT || 4000
@ -14,6 +16,7 @@ app.use(express.json())
app.use("/", validateApiKey) app.use("/", validateApiKey)
app.use("/entries", entries) app.use("/entries", entries)
app.use("/tags", tags) app.use("/tags", tags)
app.use("/search", search)
app.listen(port, () => { app.listen(port, () => {
console.info(`TB-INFO eolas-api running on NodeJS ${process.version}`) console.info(`TB-INFO eolas-api running on NodeJS ${process.version}`)

12
src/routes/search.js Normal file
View 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

View file

@ -1,4 +1,10 @@
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 { export default class EntriesService {
database database
@ -42,7 +48,7 @@ export default class EntriesService {
const list = sorted.flatMap((i) => i.source_entry_title) const list = sorted.flatMap((i) => i.source_entry_title)
return { return {
count: backlinks.length, count: backlinks.length,
data: list data: list,
} }
} }
@ -52,9 +58,8 @@ export default class EntriesService {
const list = sorted.flatMap((i) => i.target_entry_title) const list = sorted.flatMap((i) => i.target_entry_title)
return { return {
count: outlinks.length, count: outlinks.length,
data: list data: list,
} }
} }
_sortByTitle = (entries, fieldName) => { _sortByTitle = (entries, fieldName) => {

View 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,
}
}
}

3
src/sql/search.js Normal file
View 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 }