/** @module controllers */
const BaseDatabaseController = require("./BaseDatabaseController");
/** Class representing a book controller. */
class BookController extends BaseDatabaseController {
/** Create a book controller. */
constructor() {
super();
}
/**
* Get a book by id.
*
* @param {number} id
* @returns {Promise<object>}
*/
async byId(id) {
const bookResponse = await this._query('SELECT * FROM books WHERE id = ?', id);
if (bookResponse.length !== 1) {
return null;
}
return bookResponse[0];
}
/**
* Get a book by title.
*
* @param {string} title
* @returns {Promise<object>}
*/
async byTitle(title) {
const bookResponse = await this._query('SELECT * FROM books WHERE title = ?', title);
if (bookResponse.length !== 1) {
return null;
}
return bookResponse[0];
}
/**
* Search books by title.
*
* @param {string} title
* @param {number} amount
* @returns {Promise<object[]>}
*/
async searchByTitle(title, amount) {
const books = await this._query('SELECT * FROM books WHERE MATCH(title) AGAINST(? IN NATURAL LANGUAGE MODE) AND title LIKE ? LIMIT ?', title, `%${title}%`, amount);
return books;
}
/**
* Search books by title using natural language mode.
*
* @param {string} title
* @param {number} amount
* @returns {Promise<object[]>}
*/
async searchByTitleNatural(title, amount) {
const books = await this._query('SELECT *, MATCH(title) AGAINST(? IN NATURAL LANGUAGE MODE) AS score FROM books WHERE MATCH(title) AGAINST(? IN NATURAL LANGUAGE MODE) LIMIT ?', title, title, amount);
return books;
}
/**
* Search books using OCR elements.
*
* @param {string[]} ocrElements
* @returns {Promise<object[]>}
*/
async searchByOCRElements(ocrElements) {
const books = await this._query('CALL getPossibleBooks(?)', ocrElements.join(','));
return books[0];
}
/**
* Search books by title and author name.
*
* @param {string} title
* @param {string} authorName
* @returns {Promise<object[]>}
*/
async searchByTitleAndAuthorName(title, authorName) {
const books = await this._query(
`WITH CandidateBooks AS (
SELECT id, MATCH(title) AGAINST(? IN NATURAL LANGUAGE MODE) AS title_score
FROM books
WHERE MATCH(title) AGAINST(? IN NATURAL LANGUAGE MODE)
ORDER BY title_score DESC
LIMIT 250
),
CandidateAuthors AS (
SELECT id, name, MATCH(name) AGAINST(? IN NATURAL LANGUAGE MODE) AS author_score
FROM authors
WHERE MATCH(name) AGAINST(? IN NATURAL LANGUAGE MODE)
ORDER BY author_score DESC
LIMIT 250
)
SELECT b.*, ca.name AS author_name
FROM CandidateBooks cb
JOIN books_authors ba ON ba.books_id = cb.id
JOIN CandidateAuthors ca ON ba.authors_id = ca.id
JOIN books b ON b.id = cb.id
ORDER BY (cb.title_score + ca.author_score) DESC
LIMIT 10;`,
title,
title,
authorName,
authorName
);
return books;
}
/**
* Get the cover URL of a book.
*
* @param {number} id
* @returns {Promise<string | null>}
*/
async getCoverURL(id) {
const bookResponse = await this._query('SELECT * FROM books WHERE id = ?', id);
if (bookResponse.length !== 1) {
return null;
}
const workResponse = await fetch(`https://openlibrary.org${bookResponse[0].openlibrary_id}.json`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
if (!workResponse.ok) {
return null;
}
const workData = await workResponse.json();
if (!workData.covers) {
return null;
}
if (workData.covers.length === 0) {
return null;
}
const coverId = workData.covers[0];
return `https://covers.openlibrary.org/b/id/${coverId}-M.jpg`;
}
/**
* Get books by author.
*
* @param {number} authorId
* @returns {Promise<object[]>}
*/
async byAuthor(authorId) {
const booksResponse = await this._query('SELECT * FROM books AS b JOIN books_authors AS ba WHERE b.id = ba.books_id AND ba.authors_id = ?', authorId);
return booksResponse;
}
/**
* Get books by term.
*
* @param {number} termId
* @returns {Promise<object[]>}
*/
async byTerm(termId) {
const booksResponse = await this._query('SELECT * FROM books AS b JOIN books_terms AS bt WHERE b.id = bt.books_id AND bt.terms_id = ?', termId);
return booksResponse;
}
/**
* Insert a book.
*
* @param {string} title
* @param {string} subtitle
* @param {date} publicationDate
* @returns {Promise<object>}
*/
async insert(title, subtitle, publicationDate) {
const bookInsertion = await this._query('INSERT INTO books (title, subtitle, publication_date) VALUES (?, ?, ?)', title, subtitle, publicationDate);
return await this.byId(bookInsertion.insertId);
}
}
module.exports = BookController;