diff --git a/package-lock.json b/package-lock.json index cdf8666..586b459 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@types/mkdirp": "^1.0.0", "@types/node": "^13.9.8", "@types/pino": "^6.3.8", + "@types/string-similarity": "^4.0.0", "7zip-bin": "^5.1.1", "fs-extra": "^10.0.0", "imghash": "^0.0.9", @@ -33,6 +34,7 @@ "sharp": "^0.28.1", "socket.io": "^4.1.1", "socket.io-stream": "^0.5.3", + "string-similarity": "^4.0.4", "typescript": "^3.8.3", "unrar": "github:cnboker/node-unrar", "xml2js": "^0.4.23" @@ -1460,6 +1462,11 @@ "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", "dev": true }, + "node_modules/@types/string-similarity": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/string-similarity/-/string-similarity-4.0.0.tgz", + "integrity": "sha512-dMS4S07fbtY1AILG/RhuwmptmzK1Ql8scmAebOTJ/8iBtK/KI17NwGwKzu1uipjj8Kk+3mfPxum56kKZE93mzQ==" + }, "node_modules/@types/unzipper": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@types/unzipper/-/unzipper-0.10.3.tgz", @@ -10999,6 +11006,11 @@ "node": ">=8" } }, + "node_modules/string-similarity": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz", + "integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==" + }, "node_modules/string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", @@ -13450,6 +13462,11 @@ "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", "dev": true }, + "@types/string-similarity": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/string-similarity/-/string-similarity-4.0.0.tgz", + "integrity": "sha512-dMS4S07fbtY1AILG/RhuwmptmzK1Ql8scmAebOTJ/8iBtK/KI17NwGwKzu1uipjj8Kk+3mfPxum56kKZE93mzQ==" + }, "@types/unzipper": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@types/unzipper/-/unzipper-0.10.3.tgz", @@ -20826,6 +20843,11 @@ "strip-ansi": "^5.2.0" } }, + "string-similarity": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz", + "integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==" + }, "string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", diff --git a/package.json b/package.json index f8bec7c..ade7349 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@types/mkdirp": "^1.0.0", "@types/node": "^13.9.8", "@types/pino": "^6.3.8", + "@types/string-similarity": "^4.0.0", "7zip-bin": "^5.1.1", "fs-extra": "^10.0.0", "imghash": "^0.0.9", @@ -58,6 +59,7 @@ "sharp": "^0.28.1", "socket.io": "^4.1.1", "socket.io-stream": "^0.5.3", + "string-similarity": "^4.0.4", "typescript": "^3.8.3", "unrar": "github:cnboker/node-unrar", "xml2js": "^0.4.23" diff --git a/utils/searchmatchscorer.utils.ts b/utils/searchmatchscorer.utils.ts new file mode 100644 index 0000000..d0aac91 --- /dev/null +++ b/utils/searchmatchscorer.utils.ts @@ -0,0 +1,94 @@ +/* + * MIT License + * + * Copyright (c) 2015 Rishi Ghan + * + The MIT License (MIT) + +Copyright (c) 2015 Rishi Ghan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/* + * Revision History: + * Initial: 2021/07/29 Rishi Ghan + */ + +import { each, map, isUndefined, isNull, assign } from "lodash"; +import stringSimilarity from "string-similarity"; +import https from "https"; +import { createWriteStream } from "fs"; +import path from "path"; +import { calculateLevenshteinDistance } from "./imagetransformation.utils"; + +export const matchScorer = (searchMatches, searchQuery, rawFileDetails) => { + // 1. Check if it exists in the db (score: 0) + // 2. Check if issue name matches strongly (score: ++) + // 3. Check if issue number matches strongly (score: ++) + // 4. Check if issue covers hash match strongly (score: +++) + // 5. Check if issue year matches strongly (score: +) + + each(searchMatches, (match, idx) => { + // Check for the issue name match + + if ( + !isNull(searchQuery.issue.searchParams.searchTerms.name) && + !isNull(match.name) + ) { + const issueNameScore = stringSimilarity.compareTwoStrings( + searchQuery.issue.searchParams.searchTerms.name, + match.name + ); + match.score = issueNameScore; + } + + // Issue number matches + if ( + !isNull(searchQuery.issue.searchParams.searchTerms.number) && + !isNull(match.issue_number) + ) { + if ( + parseInt( + searchQuery.issue.searchParams.searchTerms.number, + 10 + ) === parseInt(match.issue_number, 10) + ) { + match.score += 1; + } + } + // Cover image hash match + const fileName = match.id + "_" + rawFileDetails.name; + https.get(match.image.small_url, (response) => { + const fileStream = response.pipe( + createWriteStream(`./userdata/temporary/${fileName}`) + ); + fileStream.on("finish", async () => { + const levenshteinDistance = await calculateLevenshteinDistance( + fileName, + path.resolve(`./userdata/temporary/${fileName}`) + ); + }); + }); + + return match; + }); + + return searchMatches; +}; diff --git a/yarn.lock b/yarn.lock index 62247b4..6df0374 100644 --- a/yarn.lock +++ b/yarn.lock @@ -738,6 +738,11 @@ "resolved" "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz" "version" "1.0.1" +"@types/string-similarity@^4.0.0": + "integrity" "sha512-dMS4S07fbtY1AILG/RhuwmptmzK1Ql8scmAebOTJ/8iBtK/KI17NwGwKzu1uipjj8Kk+3mfPxum56kKZE93mzQ==" + "resolved" "https://registry.npmjs.org/@types/string-similarity/-/string-similarity-4.0.0.tgz" + "version" "4.0.0" + "@types/unzipper@^0.10.3": "integrity" "sha512-01mQdTLp3/KuBVDhP82FNBf+enzVOjJ9dGsCWa5z8fcYAFVgA9bqIQ2NmsgNFzN/DhD0PUQj4n5p7k6I9mq80g==" "resolved" "https://registry.npmjs.org/@types/unzipper/-/unzipper-0.10.3.tgz" @@ -5700,6 +5705,11 @@ "astral-regex" "^1.0.0" "strip-ansi" "^5.2.0" +"string-similarity@^4.0.4": + "integrity" "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==" + "resolved" "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz" + "version" "4.0.4" + "string-width@^1.0.1": "integrity" "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=" "resolved" "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz"