Improve search return values and performance

This commit is contained in:
Robbie Antenesse 2020-02-06 17:28:30 -07:00
parent a3f6137dec
commit 326747c0ce
1 changed files with 78 additions and 64 deletions

View File

@ -1,5 +1,5 @@
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const { Op, fn } = require('sequelize'); const { Op, fn, col } = require('sequelize');
const BooksController = require('../bookData'); const BooksController = require('../bookData');
const { quickSearchInventaire } = require('./Inventaire'); const { quickSearchInventaire } = require('./Inventaire');
@ -14,7 +14,7 @@ class SearchController {
constructor(sequelizeModels, searchTerm, options = defaultSearchOptions) { constructor(sequelizeModels, searchTerm, options = defaultSearchOptions) {
this.models = sequelizeModels; this.models = sequelizeModels;
this.searchTerm = searchTerm; this.searchTerm = searchTerm;
this.searchBy = options.searchBy; this.searchBy = options.searchBy.replace('title', 'name').replace('author', 'description');
this.source = options.source; this.source = options.source;
this.lang = options.language; this.lang = options.language;
} }
@ -23,28 +23,40 @@ class SearchController {
return typeof this.searchTerm !== 'undefined' && this.searchTerm !== ''; return typeof this.searchTerm !== 'undefined' && this.searchTerm !== '';
} }
get includeQuery() { get bookReferenceSearchAttributes() {
return [ return {
include: [
{ {
as: 'Interactions',
model: this.models.Review, model: this.models.Review,
where: { text: { [Op.not]: null } }, attributes: ['id'],
attributes: [[fn('COUNT', 'id'), 'total']], // Get the total number of text reviews required: false,
as: 'reviews',
}, },
{ {
as: 'Reviews',
model: this.models.Review, model: this.models.Review,
where: { rating: { [Op.not]: null } }, attributes: ['id'],
attributes: [[fn('AVG', 'rating'), 'average']], // Get the average star rating required: false,
as: 'rating',
}, },
] {
} as: 'Ratings',
get orderQuery() {
return [{
model: this.models.Review, model: this.models.Review,
attributes: [[fn('COUNT', 'id'), 'total']], // Get the total number of text reviews attributes: ['rating'],
}, 'total', 'DESC']; // Order references from most to least interaction required: false,
},
], // These are all subsets of Review model specified in BookReference associations
attributes: [
[col('BookReference.id'), 'id'],
'name',
'description',
'sources',
'covers',
[fn('COUNT', col('Interactions.id')), 'totalInteractions'],
[fn('COUNT', col('Reviews.id')), 'numReviews'],
[fn('AVG', col('Ratings.rating')), 'averageRating'],
],
order: [[col('totalInteractions'), 'DESC']],
};
} }
async search() { async search() {
@ -63,19 +75,18 @@ class SearchController {
} }
// Add any search results that match refs with the same URI and delete from results array // Add any search results that match refs with the same URI and delete from results array
searchResults.forEach((result, i) => { const urisToCheck = searchResults.filter(
// If the result is not already in bookReferences result => !bookReferences.some(ref => result.uri === ref.sources[this.source])
if (!bookReferences.some(ref => result.uri === ref.sources[this.source])) { ).map(result => result.uri);
// Check if the URI is already in references table
const reference = await this.searchReferencesBySourceCode(this.source, result.uri); if (urisToCheck.length > 0) {
if (reference) { const foundReferences = await this.searchReferencesBySourceCodes(this.source, urisToCheck);
bookReferences.push(reference); return [
searchResults[i] = null; ...bookReferences,
...foundReferences,
...searchResults.filter(result => !urisToCheck.includes(result.uri)),
];
} }
} else { // If the result is already in references, null it out.
searchResults[i] = null;
}
});
return [ return [
...bookReferences, ...bookReferences,
@ -84,24 +95,7 @@ class SearchController {
} }
async searchReferences() { async searchReferences() {
const { BookReference, Review } = this.models; const { BookReference } = this.models;
// const includeQuery = [{
// model: Review,
// include: [
// {
// model: Reaction.scope('Review'),
// group: ['reactionType'],
// attributes: ['reactionType', [fn('COUNT', 'reactionType'), 'count']]
// },
// ],
// order: [{
// model: Reaction.scope('Review'),
// attributes: [[fn('COUNT', 'id'), 'total']],
// limit: 1,
// }, 'total', 'DESC'],
// limit: 5,
// }];
const exact = await BookReference.findAll({ const exact = await BookReference.findAll({
where: { where: {
@ -114,9 +108,10 @@ class SearchController {
}, },
] ]
}, },
include: includeQuery(Review), ...this.bookReferenceSearchAttributes,
order: orderQuery(Review), }).then( // Empty results give 1 empty model in an array, so filter those out
}); references => references.filter(ref => typeof ref.id !== 'undefined' && ref.id !== null)
);
if (exact.length > 0) { if (exact.length > 0) {
return exact; return exact;
@ -136,9 +131,10 @@ class SearchController {
}, },
] ]
}, },
include: this.includeQuery, ...this.bookReferenceSearchAttributes,
order: this.orderQuery, }).then( // Empty results give 1 empty model in an array, so filter those out
}); references => references.filter(ref => typeof ref.id !== 'undefined' && ref.id !== null)
);
} }
async searchReferencesBySourceCode(source, sourceId) { async searchReferencesBySourceCode(source, sourceId) {
@ -151,8 +147,26 @@ class SearchController {
}, },
}, },
}, },
include: this.includeQuery, ...this.bookReferenceSearchAttributes,
}); }).then( // Empty results give 1 empty model in an array, so filter those out
references => references.filter(ref => typeof ref.id !== 'undefined' && ref.id !== null)
);
}
async searchReferencesBySourceCodes(source, sourceIds) {
const sourceJSONKey = `"${source}"`; // Enable searching withing JSON column.
return await this.models.BookReference.findOne({
where: {
[Op.or]: sourceIds.map(sourceId => ({
source: {
[sourceJSONKey]: sourceId,
},
})),
},
...this.bookReferenceSearchAttributes,
}).then( // Empty results give 1 empty model in an array, so filter those out
references => references.filter(ref => typeof ref.id !== 'undefined' && ref.id !== null)
);
} }
/** /**