Compare commits
3 Commits
326747c0ce
...
7d20609cdf
Author | SHA1 | Date |
---|---|---|
Robbie Antenesse | 7d20609cdf | |
Robbie Antenesse | 3600dd3d09 | |
Robbie Antenesse | 5ab6d36314 |
|
@ -9,6 +9,10 @@ class Inventaire {
|
|||
return 'https://inventaire.io';
|
||||
}
|
||||
|
||||
static getLink(uri) {
|
||||
return `${Inventaire.url}/entity/${uri}`;
|
||||
}
|
||||
|
||||
static handleQuickEntity(entityObject) {
|
||||
return {
|
||||
name: (
|
||||
|
@ -21,9 +25,10 @@ class Inventaire {
|
|||
? entityObject.description
|
||||
: null
|
||||
),
|
||||
source: 'inventaire',
|
||||
link: (
|
||||
typeof entityObject.uri !== 'undefined'
|
||||
? `${this.url}/entity/${entityObject.uri}`
|
||||
? `${Inventaire.url}/entity/${entityObject.uri}`
|
||||
: null
|
||||
),
|
||||
uri: (
|
||||
|
@ -36,7 +41,7 @@ class Inventaire {
|
|||
? entityObject.image.map(imageId => {
|
||||
return {
|
||||
uri: imageId.toString(),
|
||||
url: `${this.url}/img/entities/${imageId}`,
|
||||
url: `${Inventaire.url}/img/entities/${imageId}`,
|
||||
}
|
||||
})
|
||||
: []
|
||||
|
@ -67,9 +72,10 @@ class Inventaire {
|
|||
: null
|
||||
)
|
||||
),
|
||||
source: 'inventaire',
|
||||
link: (
|
||||
typeof entityObject.uri !== 'undefined'
|
||||
? `${this.url}/entity/${entityObject.uri}`
|
||||
? `${Inventaire.url}/entity/${entityObject.uri}`
|
||||
: null
|
||||
),
|
||||
uri: (
|
||||
|
@ -82,7 +88,7 @@ class Inventaire {
|
|||
|
||||
async getBookData(uri) {
|
||||
if (uri) {
|
||||
const request = fetch(`${this.url}/api/entities?action=by-uris&uris=${encodeURIComponent(uri)}`)
|
||||
const request = fetch(`${Inventaire.url}/api/entities?action=by-uris&uris=${encodeURIComponent(uri)}`)
|
||||
request.catch(exception => {
|
||||
console.error(exception);
|
||||
return {
|
||||
|
@ -103,7 +109,7 @@ class Inventaire {
|
|||
|
||||
if (typeof bookData.entities !== 'undefined' && typeof bookData.entities[uri] !== 'undefined') {
|
||||
const bookData = Inventaire.handleEntity(bookData.entities[uri], this.lang);
|
||||
bookData['covers'] = await this.getCovers();
|
||||
bookData['covers'] = await this.getCovers(bookData.uri);
|
||||
|
||||
return bookData;
|
||||
}
|
||||
|
@ -120,7 +126,7 @@ class Inventaire {
|
|||
}
|
||||
|
||||
// Note: property `wdt:P629` is a given entity (uri)'s list of editions (ISBNs).
|
||||
const editionsRequest = fetch(`${this.url}/api/entities?action=reverse-claims&uri=${encodeURIComponent(uri)}&property=wdt:P629`)
|
||||
const editionsRequest = fetch(`${Inventaire.url}/api/entities?action=reverse-claims&uri=${encodeURIComponent(uri)}&property=wdt:P629`)
|
||||
editionsRequest.catch(exception => {
|
||||
console.error(exception);
|
||||
return {
|
||||
|
@ -145,7 +151,7 @@ class Inventaire {
|
|||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const isbnsRequest = fetch(`${this.url}/api/entities?action=by-uris&uris=${encodeURIComponent(editionURIs)}`);
|
||||
const isbnsRequest = fetch(`${Inventaire.url}/api/entities?action=by-uris&uris=${encodeURIComponent(editionURIs)}`);
|
||||
isbnsRequest.catch(exception => {
|
||||
console.error(exception);
|
||||
return {
|
||||
|
@ -175,7 +181,7 @@ class Inventaire {
|
|||
const entity = responseJSON.entities[key];
|
||||
return {
|
||||
uri: entity.uri,
|
||||
url: typeof entity.claims['invp:P2'] !== 'undefined' ? `${this.url}/img/entities/${entity.claims['invp:P2'][0]}` : null,
|
||||
url: typeof entity.claims['invp:P2'] !== 'undefined' ? `${Inventaire.url}/img/entities/${entity.claims['invp:P2'][0]}` : null,
|
||||
publishDate: typeof entity.claims['wdt:P577'] !== 'undefined' ? entity.claims['wdt:P577'][0] : null,
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
const Inventaire = require('./bookData/Inventaire');
|
||||
const SearchController = require('./search');
|
||||
|
||||
class BookReferenceController {
|
||||
constructor(sequelizeModels, language) {
|
||||
this.models = sequelizeModels;
|
||||
this.lang = language;
|
||||
}
|
||||
|
||||
async createOrUpdateReference(source, sourceId) {
|
||||
const searchController = new SearchController(this.models);
|
||||
const existingReference = searchController.searchReferencesBySourceCode(source, sourceId);
|
||||
|
||||
if (existingReference.id !== null) {
|
||||
return existingReference;
|
||||
}
|
||||
|
||||
let dataClass;
|
||||
switch (source) {
|
||||
case 'openlibrary': {
|
||||
// break;
|
||||
}
|
||||
case 'inventaire':
|
||||
default: {
|
||||
dataClass = new Inventaire(this.lang);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get formatted book data from source
|
||||
const bookData = dataClass.getBookData(sourceId);
|
||||
|
||||
if (typeof bookData.uri !== 'undefined') {
|
||||
// Check for references by exact name and author from source
|
||||
const matchingReference = await searchController.searchReferencesForExactMatch(bookData.name, bookData.description);
|
||||
|
||||
if (matchingReference.id !== null) {
|
||||
// If a match is found, update the sources of reference in the database and return it.
|
||||
return await this.addSourceToReference(matchingReference, source, sourceId);
|
||||
}
|
||||
|
||||
return await this.createReference(bookData, source, sourceId);
|
||||
}
|
||||
|
||||
return {
|
||||
error: true,
|
||||
}
|
||||
}
|
||||
|
||||
async createReference(bookData, source, sourceId) {
|
||||
const newReference = await this.models.BookReference.create({
|
||||
name: bookData.name,
|
||||
description: bookData.description,
|
||||
sources: {
|
||||
[source]: sourceId,
|
||||
},
|
||||
covers: bookData.covers,
|
||||
locale: this.lang,
|
||||
});
|
||||
newReference.totalInteractions = 0;
|
||||
newReference.numReviews = 0;
|
||||
newReference.averageRating = null;
|
||||
newReference.Interactions = [];
|
||||
newReference.Reviews = [];
|
||||
newReference.Ratings = [];
|
||||
return newReference;
|
||||
}
|
||||
|
||||
async addSourceToReference(reference, source, sourceId) {
|
||||
const updatedSources = Object.assign({ [source]: sourceId }, reference.sources);
|
||||
return await reference.update({
|
||||
sources: updatedSources,
|
||||
}).then(() => {
|
||||
reference.sources = updatedSources;
|
||||
return reference;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BookReferenceController;
|
|
@ -11,16 +11,8 @@ const defaultSearchOptions = {
|
|||
}
|
||||
|
||||
class SearchController {
|
||||
constructor(sequelizeModels, searchTerm, options = defaultSearchOptions) {
|
||||
constructor(sequelizeModels) {
|
||||
this.models = sequelizeModels;
|
||||
this.searchTerm = searchTerm;
|
||||
this.searchBy = options.searchBy.replace('title', 'name').replace('author', 'description');
|
||||
this.source = options.source;
|
||||
this.lang = options.language;
|
||||
}
|
||||
|
||||
get hasQuery() {
|
||||
return typeof this.searchTerm !== 'undefined' && this.searchTerm !== '';
|
||||
}
|
||||
|
||||
get bookReferenceSearchAttributes() {
|
||||
|
@ -59,52 +51,58 @@ class SearchController {
|
|||
};
|
||||
}
|
||||
|
||||
async search() {
|
||||
const bookReferences = await this.searchReferences();
|
||||
async search(searchTerm, options = defaultSearchOptions) {
|
||||
const searchBy = options.searchBy.replace('title', 'name').replace('author', 'description');
|
||||
const { source, language } = options;
|
||||
|
||||
const bookReferences = await this.searchReferences(searchTerm, options);
|
||||
let searchResults;
|
||||
switch (this.source) {
|
||||
switch (source) {
|
||||
case 'openlibrary': {
|
||||
searchResults = await this.searchOpenLibrary(this.searchBy);
|
||||
searchResults = await this.searchOpenLibrary(searchBy);
|
||||
break;
|
||||
}
|
||||
case 'inventaire':
|
||||
default: {
|
||||
searchResults = await quickSearchInventaire(this.searchTerm, this.lang);
|
||||
searchResults = await quickSearchInventaire(searchTerm, language);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add any search results that match refs with the same URI and delete from results array
|
||||
const urisToCheck = searchResults.filter(
|
||||
result => !bookReferences.some(ref => result.uri === ref.sources[this.source])
|
||||
result => !bookReferences.some(ref => result.uri === ref.sources[source])
|
||||
).map(result => result.uri);
|
||||
|
||||
let extraReferences = [];
|
||||
if (urisToCheck.length > 0) {
|
||||
const foundReferences = await this.searchReferencesBySourceCodes(this.source, urisToCheck);
|
||||
return [
|
||||
...bookReferences,
|
||||
...foundReferences,
|
||||
...searchResults.filter(result => !urisToCheck.includes(result.uri)),
|
||||
];
|
||||
extraReferences = await this.searchReferencesBySourceCodes(source, urisToCheck);
|
||||
}
|
||||
|
||||
return [
|
||||
...bookReferences,
|
||||
...searchResults.filter(result => result !== null),
|
||||
...extraReferences,
|
||||
...searchResults.filter( // Only show the rest of the search results
|
||||
result => !extraReferences.some(
|
||||
ref => result.uri === ref.sources[source]
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
async searchReferences() {
|
||||
async searchReferences(searchTerm, options = defaultSearchOptions) {
|
||||
const searchBy = options.searchBy.replace('title', 'name').replace('author', 'description');
|
||||
const { language } = options;
|
||||
|
||||
const { BookReference } = this.models;
|
||||
|
||||
const exact = await BookReference.findAll({
|
||||
where: {
|
||||
[Op.and]: [ // All of the contained cases are true
|
||||
{
|
||||
[this.searchBy]: this.searchTerm, // searchBy is exactly searchTerm
|
||||
[searchBy]: searchTerm, // searchBy is exactly searchTerm
|
||||
},
|
||||
{
|
||||
locale: this.lang,
|
||||
locale: language,
|
||||
},
|
||||
]
|
||||
},
|
||||
|
@ -122,12 +120,12 @@ class SearchController {
|
|||
where: {
|
||||
[Op.and]: [ // All of the contained cases are true
|
||||
{
|
||||
[this.searchBy]: { // `name` or `description`
|
||||
[Op.substring]: this.searchTerm, // LIKE '%searchTerm%'
|
||||
[searchBy]: { // `name` or `description`
|
||||
[Op.substring]: searchTerm, // LIKE '%searchTerm%'
|
||||
},
|
||||
},
|
||||
{
|
||||
locale: this.lang,
|
||||
locale: language,
|
||||
},
|
||||
]
|
||||
},
|
||||
|
@ -155,7 +153,7 @@ class SearchController {
|
|||
|
||||
async searchReferencesBySourceCodes(source, sourceIds) {
|
||||
const sourceJSONKey = `"${source}"`; // Enable searching withing JSON column.
|
||||
return await this.models.BookReference.findOne({
|
||||
return await this.models.BookReference.findAll({
|
||||
where: {
|
||||
[Op.or]: sourceIds.map(sourceId => ({
|
||||
source: {
|
||||
|
@ -218,14 +216,14 @@ class SearchController {
|
|||
}
|
||||
|
||||
async searchWikiBooks(term) {
|
||||
if (!this.hasQuery) {
|
||||
if (!term) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const query = this.mediaWikiQuery('https://en.wikibooks.org/w/api.php', {
|
||||
action: 'query',
|
||||
list: 'search',
|
||||
srsearch: this.searchTerm,
|
||||
srsearch: term,
|
||||
srprop: '',
|
||||
});
|
||||
query(response => {
|
||||
|
@ -257,19 +255,19 @@ class SearchController {
|
|||
});
|
||||
}
|
||||
|
||||
async searchOpenLibrary(searchBy = 'title') {
|
||||
if (!this.hasQuery) {
|
||||
async searchOpenLibrary(searchTerm, searchBy = 'title') {
|
||||
if (!searchTerm) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fetch(`https://openlibrary.org/search.json?${searchBy}=${encodeURIComponent(this.searchTerm)}`)
|
||||
return fetch(`https://openlibrary.org/search.json?${searchBy}=${encodeURIComponent(searchTerm)}`)
|
||||
.then(res => res.json())
|
||||
.then(response => {
|
||||
if (!response.hasOwnProperty('docs')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const booksController = new BooksController('openLibrary', undefined, this.lang);
|
||||
const booksController = new BooksController('openLibrary');
|
||||
|
||||
// Format the response into usable objects
|
||||
const docs = response.docs.map(doc => {
|
||||
|
|
|
@ -6,9 +6,9 @@ async function routes(fastify, options) {
|
|||
const searchBy = typeof request.query.by !== 'undefined' ? request.query.by.trim() : 'title';
|
||||
const language = typeof request.query.lang !== 'undefined' ? request.query.lang.trim().split('-')[0] : undefined; // Get base language in cases like 'en-US'
|
||||
const source = typeof request.query.source !== 'undefined' ? request.query.source.trim() : undefined; // Get base language in cases like 'en-US'
|
||||
const controller = new SearchController(fastify.models, searchTerm, { searchBy, source, language });
|
||||
const controller = new SearchController(fastify.models);
|
||||
|
||||
return await controller.search();
|
||||
return await controller.search(searchTerm, { searchBy, source, language });
|
||||
});
|
||||
|
||||
fastify.get('/api/search/cover', async (request, reply) => {
|
||||
|
|
Loading…
Reference in New Issue