some other stuff
Former-commit-id: d28d5c154962973c238e362cb44f6cecff37e7ce
This commit is contained in:
@@ -7,6 +7,18 @@ class Basket extends Component {
|
|||||||
super(Basket);
|
super(Basket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnMount() {
|
||||||
|
const basket = localStorage.getItem('basket');
|
||||||
|
if (basket) {
|
||||||
|
const basketItems = JSON.parse(basket);
|
||||||
|
this.setState({
|
||||||
|
...basketItems,
|
||||||
|
}, false);
|
||||||
|
console.log(basketItems, this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Render() {
|
Render() {
|
||||||
return {
|
return {
|
||||||
template: /* html */`
|
template: /* html */`
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
<script type="module" src="/components/basket.mjs"></script>
|
<script type="module" src="/components/basket.mjs"></script>
|
||||||
<script type="module" src="/components/basket-popout.mjs"></script>
|
<script type="module" src="/components/basket-popout.mjs"></script>
|
||||||
<script type="module" src="/components/accessability-popout.mjs"></script>
|
<script type="module" src="/components/accessability-popout.mjs"></script>
|
||||||
<script type="module" src="/components/accessability-popout.mjs"></script>
|
|
||||||
<script type="module" src="/components/notificationbar.mjs"></script>
|
<script type="module" src="/components/notificationbar.mjs"></script>
|
||||||
<script type="module" src="/components/storefront.mjs"></script>
|
<script type="module" src="/components/storefront.mjs"></script>
|
||||||
<script type="module" src="/components/tag.mjs"></script>
|
<script type="module" src="/components/tag.mjs"></script>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Database
|
||||||
|
|
||||||
|
I decided to go with a RDBMS, PostgreSql as I am familiar with it.
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
const ControllerMaster = require('./controller-master.js');
|
const ControllerMaster = require('./controller-master.js');
|
||||||
const Database = require('../database/database.js');
|
const Database = require('../database/database.js');
|
||||||
|
const Logger = require('../logger.js');
|
||||||
|
|
||||||
const PgFormat = require('pg-format');
|
const PgFormat = require('pg-format');
|
||||||
|
|
||||||
async function Search(fuzzyString) {
|
async function Search(fuzzyStrings) {
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
const dbres = await Database.Query(`
|
let dbres;
|
||||||
SELECT lego_brick.id, lego_brick.name, tag.name AS "tag", inv.price, inv.new_price AS "discount"
|
try {
|
||||||
FROM lego_brick
|
dbres = await Database.Query(PgFormat(`
|
||||||
LEFT JOIN lego_brick_tag AS tags ON tags.brick_id = lego_brick.id
|
SELECT lego_brick.id, lego_brick.name, tag.name AS "tag", inv.price, inv.new_price AS "discount"
|
||||||
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
FROM lego_brick
|
||||||
LEFT JOIN lego_brick_inventory AS inv ON inv.brick_id = lego_brick.id
|
LEFT JOIN lego_brick_tag AS tags ON tags.brick_id = lego_brick.id
|
||||||
WHERE lego_brick.id ~* $1 OR lego_brick.name ~* $1 OR tag.name ~* $1
|
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
||||||
`, [fuzzyString]);
|
LEFT JOIN lego_brick_inventory AS inv ON inv.brick_id = lego_brick.id
|
||||||
await Database.Query('END TRANSACTION;');
|
WHERE lego_brick.id ~* ANY(ARRAY[%L]) OR lego_brick.name ~* ANY(ARRAY[%L]) OR tag.name ~* ANY(ARRAY[%L])
|
||||||
|
`, fuzzyStrings, fuzzyStrings, fuzzyStrings), []);
|
||||||
|
await Database.Query('COMMIT TRANSACTION;');
|
||||||
|
} catch {
|
||||||
|
await Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Database Error');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// validate database response
|
// validate database response
|
||||||
if (dbres.rows.length === 0) {
|
if (dbres.rows.length === 0) {
|
||||||
@@ -30,8 +40,8 @@ async function Search(fuzzyString) {
|
|||||||
const bName = b.name.toLowerCase();
|
const bName = b.name.toLowerCase();
|
||||||
const aTag = a.tag.toLowerCase();
|
const aTag = a.tag.toLowerCase();
|
||||||
const bTag = b.tag.toLowerCase();
|
const bTag = b.tag.toLowerCase();
|
||||||
const aFuzzy = fuzzyString.toLowerCase();
|
const aFuzzy = fuzzyStrings[0].toLowerCase();
|
||||||
const bFuzzy = fuzzyString.toLowerCase();
|
const bFuzzy = fuzzyStrings[0].toLowerCase();
|
||||||
|
|
||||||
const aDist = ControllerMaster.LevenshteinDistance(aName, aFuzzy);
|
const aDist = ControllerMaster.LevenshteinDistance(aName, aFuzzy);
|
||||||
const bDist = ControllerMaster.LevenshteinDistance(bName, bFuzzy);
|
const bDist = ControllerMaster.LevenshteinDistance(bName, bFuzzy);
|
||||||
@@ -64,13 +74,22 @@ async function Search(fuzzyString) {
|
|||||||
|
|
||||||
async function SumPrices(bricksArr, quantityArray) {
|
async function SumPrices(bricksArr, quantityArray) {
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
const dbres = await Database.Query(PgFormat(`
|
let dbres;
|
||||||
SELECT COALESCE(new_price, price) AS "price"
|
try {
|
||||||
FROM lego_brick
|
dbres = await Database.Query(PgFormat(`
|
||||||
LEFT JOIN lego_brick_inventory AS brick_inventory ON brick_inventory.brick_id = lego_brick.id
|
SELECT COALESCE(new_price, price) AS "price"
|
||||||
WHERE lego_brick.id IN (%L);
|
FROM lego_brick
|
||||||
`, bricksArr), []);
|
LEFT JOIN lego_brick_inventory AS brick_inventory ON brick_inventory.brick_id = lego_brick.id
|
||||||
await Database.Query('END TRANSACTION;');
|
WHERE lego_brick.id IN (%L);
|
||||||
|
`, bricksArr), []);
|
||||||
|
await Database.Query('COMMIT TRANSACTION;');
|
||||||
|
} catch {
|
||||||
|
await Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Database Error');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// validate database response
|
// validate database response
|
||||||
if (dbres.rows.length === 0) {
|
if (dbres.rows.length === 0) {
|
||||||
@@ -90,15 +109,24 @@ async function SumPrices(bricksArr, quantityArray) {
|
|||||||
|
|
||||||
async function GetBulkBricks(bricksArr) {
|
async function GetBulkBricks(bricksArr) {
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
const dbres = await Database.Query(PgFormat(`
|
let dbres;
|
||||||
SELECT lego_brick.id, lego_brick.name, tag.name AS "tag", inv.price, inv.new_price AS "discount"
|
try {
|
||||||
FROM lego_brick
|
dbres = await Database.Query(PgFormat(`
|
||||||
LEFT JOIN lego_brick_tag AS tags ON tags.brick_id = lego_brick.id
|
SELECT lego_brick.id, lego_brick.name, tag.name AS "tag", inv.price, inv.new_price AS "discount"
|
||||||
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
FROM lego_brick
|
||||||
LEFT JOIN lego_brick_inventory AS inv ON inv.brick_id = lego_brick.id
|
LEFT JOIN lego_brick_tag AS tags ON tags.brick_id = lego_brick.id
|
||||||
WHERE lego_brick.id IN (%L);
|
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
||||||
`, bricksArr), []);
|
LEFT JOIN lego_brick_inventory AS inv ON inv.brick_id = lego_brick.id
|
||||||
await Database.Query('END TRANSACTION;');
|
WHERE lego_brick.id IN (%L);
|
||||||
|
`, bricksArr), []);
|
||||||
|
await Database.Query('COMMIT TRANSACTION;');
|
||||||
|
} catch {
|
||||||
|
await Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Database Error');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// validate database response
|
// validate database response
|
||||||
if (dbres.rows.length === 0) {
|
if (dbres.rows.length === 0) {
|
||||||
@@ -120,25 +148,34 @@ async function GetBulkBricks(bricksArr) {
|
|||||||
|
|
||||||
async function GetBrick(brickId) {
|
async function GetBrick(brickId) {
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
|
let dbres;
|
||||||
const dbres = await Database.Query(`
|
let colDbres;
|
||||||
SELECT lego_brick.id, lego_brick.name, tag.name AS "tag",
|
try {
|
||||||
inv.price, inv.new_price AS "discount", inv.stock,
|
dbres = await Database.Query(`
|
||||||
inv.last_updated AS "last_stock_update",
|
SELECT lego_brick.id, lego_brick.name, tag.name AS "tag",
|
||||||
weight, dimensions_x, dimensions_y, dimensions_z
|
inv.price, inv.new_price AS "discount", inv.stock,
|
||||||
FROM lego_brick
|
inv.last_updated AS "last_stock_update",
|
||||||
LEFT JOIN lego_brick_inventory AS inv ON inv.brick_id = lego_brick.id
|
weight, dimensions_x, dimensions_y, dimensions_z
|
||||||
LEFT JOIN lego_brick_tag AS tags ON tags.brick_id = lego_brick.id
|
FROM lego_brick
|
||||||
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
LEFT JOIN lego_brick_inventory AS inv ON inv.brick_id = lego_brick.id
|
||||||
WHERE lego_brick.id = $1;
|
LEFT JOIN lego_brick_tag AS tags ON tags.brick_id = lego_brick.id
|
||||||
`, [brickId]);
|
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
||||||
const colDbres = await Database.Query(`
|
WHERE lego_brick.id = $1;
|
||||||
SELECT lego_brick_colour.id, lego_brick_colour.name, lego_brick_colour.hexrgb,
|
`, [brickId]);
|
||||||
colour_type.name AS "colour_type"
|
colDbres = await Database.Query(`
|
||||||
FROM lego_brick_colour
|
SELECT lego_brick_colour.id, lego_brick_colour.name, lego_brick_colour.hexrgb,
|
||||||
LEFT JOIN colour_type AS colour_type ON lego_brick_colour.col_type = colour_type.id
|
colour_type.name AS "colour_type"
|
||||||
`, []);
|
FROM lego_brick_colour
|
||||||
await Database.Query('END TRANSACTION;');
|
LEFT JOIN colour_type AS colour_type ON lego_brick_colour.col_type = colour_type.id
|
||||||
|
`, []);
|
||||||
|
await Database.Query('COMMIT TRANSACTION;');
|
||||||
|
} catch {
|
||||||
|
await Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Database Error');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// validate database response
|
// validate database response
|
||||||
if (dbres.rows.length === 0) {
|
if (dbres.rows.length === 0) {
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
const axios = require("axios");
|
|
||||||
|
|
||||||
let StaticDictionary = [];
|
|
||||||
|
|
||||||
async function Init() {
|
|
||||||
await axios.get('http://www.mieliestronk.com/corncob_lowercase.txt').then(response => {
|
|
||||||
StaticDictionary = response.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// probability of trigram/bigram
|
|
||||||
const BigramCommonality = {
|
|
||||||
th: 1.52,
|
|
||||||
he: 1.28,
|
|
||||||
in: 0.94,
|
|
||||||
er: 0.94,
|
|
||||||
an: 0.82,
|
|
||||||
re: 0.68,
|
|
||||||
nd: 0.63,
|
|
||||||
at: 0.59,
|
|
||||||
on: 0.57,
|
|
||||||
nt: 0.56,
|
|
||||||
ha: 0.56,
|
|
||||||
es: 0.56,
|
|
||||||
st: 0.55,
|
|
||||||
en: 0.55,
|
|
||||||
ed: 0.53,
|
|
||||||
to: 0.52,
|
|
||||||
it: 0.50,
|
|
||||||
ou: 0.50,
|
|
||||||
ea: 0.47,
|
|
||||||
hi: 0.46,
|
|
||||||
is: 0.46,
|
|
||||||
or: 0.43,
|
|
||||||
ti: 0.34,
|
|
||||||
as: 0.33,
|
|
||||||
te: 0.27,
|
|
||||||
et: 0.19,
|
|
||||||
ng: 0.18,
|
|
||||||
of: 0.16,
|
|
||||||
al: 0.09,
|
|
||||||
de: 0.09,
|
|
||||||
se: 0.08,
|
|
||||||
le: 0.08,
|
|
||||||
sa: 0.06,
|
|
||||||
si: 0.05,
|
|
||||||
ar: 0.04,
|
|
||||||
ve: 0.04,
|
|
||||||
ra: 0.04,
|
|
||||||
ld: 0.02,
|
|
||||||
ur: 0.02,
|
|
||||||
};
|
|
||||||
|
|
||||||
const TrigramCommonality = {
|
|
||||||
the: 1.81,
|
|
||||||
and: 0.73,
|
|
||||||
tha: 0.33,
|
|
||||||
ent: 0.42,
|
|
||||||
ing: 0.72,
|
|
||||||
ion: 0.42,
|
|
||||||
tio: 0.31,
|
|
||||||
for: 0.34,
|
|
||||||
oft: 0.22,
|
|
||||||
sth: 0.21,
|
|
||||||
};
|
|
||||||
|
|
||||||
function MostProbableAlternateQueries(query) {
|
|
||||||
const words = query.split(' ');
|
|
||||||
|
|
||||||
const reconstruction = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < words.length; i++) {
|
|
||||||
const mostLikely = MostProbableMissSpelling(words[i]);
|
|
||||||
reconstruction.push([...mostLikely]);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(reconstruction)
|
|
||||||
|
|
||||||
// work out a bit of context to determine the most likely sentence
|
|
||||||
}
|
|
||||||
|
|
||||||
function MostProbableMissSpelling(word) {
|
|
||||||
// First work out if it's intended to be a word
|
|
||||||
|
|
||||||
console.log(word);
|
|
||||||
return BiGrams(word);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ConditionalTrigramProbability(token) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function ConditionalBigramProbability(token) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns list of tokens
|
|
||||||
function TriGrams(word) {
|
|
||||||
return NGrams(word, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BiGrams(word) {
|
|
||||||
return NGrams(word, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function NGrams(word, n) {
|
|
||||||
const tokens = [];
|
|
||||||
for (let i = 0; i < word.length - n + 1; i++) {
|
|
||||||
tokens.push(word.substring(i, i + n));
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Init,
|
|
||||||
MostProbableAlternateQueries,
|
|
||||||
MostProbableMissSpelling,
|
|
||||||
};
|
|
||||||
@@ -1,19 +1,29 @@
|
|||||||
const ControllerMaster = require('./controller-master.js');
|
const ControllerMaster = require('./controller-master.js');
|
||||||
const Database = require('../database/database.js');
|
const Database = require('../database/database.js');
|
||||||
|
const Logger = require('../logger.js');
|
||||||
|
|
||||||
const PgFormat = require('pg-format');
|
const PgFormat = require('pg-format');
|
||||||
|
|
||||||
async function Search(fuzzyString) {
|
async function Search(fuzzyStrings) {
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
const dbres = await Database.Query(`
|
let dbres;
|
||||||
SELECT lego_set.id, lego_set.name, tag.name AS "tag", inv.price, inv.new_price AS "discount"
|
try {
|
||||||
FROM lego_set
|
dbres = await Database.Query(PgFormat(`
|
||||||
LEFT JOIN lego_set_tag AS tags ON tags.set_id = lego_set.id
|
SELECT lego_set.id, lego_set.name, tag.name AS "tag", inv.price, inv.new_price AS "discount"
|
||||||
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
FROM lego_set
|
||||||
LEFT JOIN lego_set_inventory AS inv ON inv.set_id = lego_set.id
|
LEFT JOIN lego_set_tag AS tags ON tags.set_id = lego_set.id
|
||||||
WHERE lego_set.id ~* $1 OR lego_set.name ~* $1 OR tag.name ~* $1
|
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
||||||
`, [fuzzyString]);
|
LEFT JOIN lego_set_inventory AS inv ON inv.set_id = lego_set.id
|
||||||
await Database.Query('END TRANSACTION;');
|
WHERE lego_set.id ~* ANY(ARRAY[%L]) OR lego_set.name ~* ANY(ARRAY[%L]) OR tag.name ~* ANY(ARRAY[%L])
|
||||||
|
`, fuzzyStrings, fuzzyStrings, fuzzyStrings), []);
|
||||||
|
await Database.Query('COMMIT TRANSACTION;');
|
||||||
|
} catch {
|
||||||
|
await Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Database Error');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// validate database response
|
// validate database response
|
||||||
if (dbres.rows.length === 0) {
|
if (dbres.rows.length === 0) {
|
||||||
@@ -30,8 +40,8 @@ async function Search(fuzzyString) {
|
|||||||
const bName = b.name.toLowerCase();
|
const bName = b.name.toLowerCase();
|
||||||
const aTag = a.tag.toLowerCase();
|
const aTag = a.tag.toLowerCase();
|
||||||
const bTag = b.tag.toLowerCase();
|
const bTag = b.tag.toLowerCase();
|
||||||
const aFuzzy = fuzzyString.toLowerCase();
|
const aFuzzy = fuzzyStrings[0].toLowerCase();
|
||||||
const bFuzzy = fuzzyString.toLowerCase();
|
const bFuzzy = fuzzyStrings[0].toLowerCase();
|
||||||
|
|
||||||
const aDist = ControllerMaster.LevenshteinDistance(aName, aFuzzy);
|
const aDist = ControllerMaster.LevenshteinDistance(aName, aFuzzy);
|
||||||
const bDist = ControllerMaster.LevenshteinDistance(bName, bFuzzy);
|
const bDist = ControllerMaster.LevenshteinDistance(bName, bFuzzy);
|
||||||
@@ -74,13 +84,22 @@ async function Search(fuzzyString) {
|
|||||||
|
|
||||||
async function SumPrices(setsArray, quantityArray) {
|
async function SumPrices(setsArray, quantityArray) {
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
const dbres = await Database.Query(PgFormat(`
|
let dbres;
|
||||||
SELECT COALESCE(new_price, price) AS "price"
|
try {
|
||||||
FROM lego_set
|
dbres = await Database.Query(PgFormat(`
|
||||||
LEFT JOIN lego_set_inventory AS set_inventory ON set_inventory.set_id = lego_set.id
|
SELECT COALESCE(new_price, price) AS "price"
|
||||||
WHERE lego_set.id IN (%L);
|
FROM lego_set
|
||||||
`, setsArray), []);
|
LEFT JOIN lego_set_inventory AS set_inventory ON set_inventory.set_id = lego_set.id
|
||||||
await Database.Query('END TRANSACTION;');
|
WHERE lego_set.id IN (%L);
|
||||||
|
`, setsArray), []);
|
||||||
|
await Database.Query('COMMIT TRANSACTION;');
|
||||||
|
} catch {
|
||||||
|
await Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Database Error');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// validate database response
|
// validate database response
|
||||||
if (dbres.rows.length === 0) {
|
if (dbres.rows.length === 0) {
|
||||||
@@ -101,19 +120,28 @@ async function SumPrices(setsArray, quantityArray) {
|
|||||||
|
|
||||||
async function GetSet(setId) {
|
async function GetSet(setId) {
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
const dbres = await Database.Query(`
|
let dbres;
|
||||||
SELECT lego_set.id, lego_set.name, description, tag.name AS "tag",
|
try {
|
||||||
set_contents.brick_id, set_contents.amount, inv.price,
|
dbres = await Database.Query(`
|
||||||
date_released, weight, dimensions_x, dimensions_y, dimensions_z,
|
SELECT lego_set.id, lego_set.name, description, tag.name AS "tag",
|
||||||
new_price AS "discount", inv.stock, inv.last_updated AS "last_stock_update"
|
set_contents.brick_id, set_contents.amount, inv.price,
|
||||||
FROM lego_set
|
date_released, weight, dimensions_x, dimensions_y, dimensions_z,
|
||||||
LEFT JOIN lego_set_inventory AS inv ON inv.set_id = lego_set.id
|
new_price AS "discount", inv.stock, inv.last_updated AS "last_stock_update"
|
||||||
LEFT JOIN lego_set_tag AS tags ON tags.set_id = lego_set.id
|
FROM lego_set
|
||||||
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
LEFT JOIN lego_set_inventory AS inv ON inv.set_id = lego_set.id
|
||||||
LEFT JOIN set_descriptor AS set_contents ON set_contents.set_id = lego_set.id
|
LEFT JOIN lego_set_tag AS tags ON tags.set_id = lego_set.id
|
||||||
WHERE lego_set.id = $1;
|
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
||||||
`, [setId]);
|
LEFT JOIN set_descriptor AS set_contents ON set_contents.set_id = lego_set.id
|
||||||
await Database.Query('END TRANSACTION;');
|
WHERE lego_set.id = $1;
|
||||||
|
`, [setId]);
|
||||||
|
await Database.Query('COMMIT TRANSACTION;');
|
||||||
|
} catch {
|
||||||
|
await Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Database Error');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// validate database response
|
// validate database response
|
||||||
if (dbres.rows.length === 0) {
|
if (dbres.rows.length === 0) {
|
||||||
@@ -142,22 +170,31 @@ async function GetSet(setId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function GetSets(page, resPerPage) {
|
async function GetSets(page, resPerPage) {
|
||||||
// query for the set
|
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
const countRes = await Database.Query('SELECT COUNT (*) FROM lego_set;');
|
let countRes;
|
||||||
const total = parseInt(countRes.rows[0].count);
|
let dbres;
|
||||||
const dbres = await Database.Query(`
|
try {
|
||||||
|
countRes = await Database.Query('SELECT COUNT (*) FROM lego_set;');
|
||||||
|
dbres = await Database.Query(`
|
||||||
SELECT
|
SELECT
|
||||||
lego_set.id, lego_set.name, price, new_price AS "discount", tag.name AS "tag"
|
lego_set.id, lego_set.name, price, new_price AS "discount", tag.name AS "tag"
|
||||||
FROM lego_set
|
FROM lego_set
|
||||||
LEFT JOIN lego_set_inventory as inv ON lego_set.id = inv.set_id
|
LEFT JOIN lego_set_inventory as inv ON lego_set.id = inv.set_id
|
||||||
LEFT JOIN lego_set_tag AS tags ON tags.set_id = lego_set.id
|
LEFT JOIN lego_set_tag AS tags ON tags.set_id = lego_set.id
|
||||||
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
||||||
ORDER BY id ASC
|
ORDER BY id ASC
|
||||||
LIMIT $1
|
LIMIT $1
|
||||||
OFFSET $2;`,
|
OFFSET $2;`,
|
||||||
[resPerPage, page * resPerPage]);
|
[resPerPage, page * resPerPage]);
|
||||||
await Database.Query('END TRANSACTION;');
|
await Database.Query('COMMIT TRANSACTION;');
|
||||||
|
} catch {
|
||||||
|
await Database.Query('ROLLBACK TRANSACTION;');
|
||||||
|
Logger.Error('Database Error');
|
||||||
|
return {
|
||||||
|
error: 'Database error',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const total = parseInt(countRes.rows[0].count);
|
||||||
|
|
||||||
// validate database response
|
// validate database response
|
||||||
if (dbres.rows.length === 0) {
|
if (dbres.rows.length === 0) {
|
||||||
@@ -174,6 +211,7 @@ async function GetSets(page, resPerPage) {
|
|||||||
set.tags = [];
|
set.tags = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// combine (joined) rows into a single array
|
// combine (joined) rows into a single array
|
||||||
sets = sets.reduce((arr, current) => {
|
sets = sets.reduce((arr, current) => {
|
||||||
if (!arr.some(item => item.id === current.id)) {
|
if (!arr.some(item => item.id === current.id)) {
|
||||||
|
|||||||
168
src/controllers/spellchecker.js
Normal file
168
src/controllers/spellchecker.js
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
let StaticDictionary = [];
|
||||||
|
let MispellingDictionaryRAW = '';
|
||||||
|
|
||||||
|
async function Init() {
|
||||||
|
// yes i know it could be unreliable to get them from external, untrusted sources
|
||||||
|
// this algorithm is purely for fun and is very much a "work in progress"
|
||||||
|
await axios.get('http://www.mieliestronk.com/corncob_lowercase.txt').then(response => {
|
||||||
|
StaticDictionary = response.data;
|
||||||
|
});
|
||||||
|
|
||||||
|
await axios.get('https://www.dcs.bbk.ac.uk/~roger/missp.dat').then(response => {
|
||||||
|
MispellingDictionaryRAW = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// probability of trigram/bigram
|
||||||
|
const BigramCommonality = {
|
||||||
|
th: 1.52,
|
||||||
|
he: 1.28,
|
||||||
|
in: 0.94,
|
||||||
|
er: 0.94,
|
||||||
|
an: 0.82,
|
||||||
|
re: 0.68,
|
||||||
|
nd: 0.63,
|
||||||
|
at: 0.59,
|
||||||
|
on: 0.57,
|
||||||
|
nt: 0.56,
|
||||||
|
ha: 0.56,
|
||||||
|
es: 0.56,
|
||||||
|
st: 0.55,
|
||||||
|
en: 0.55,
|
||||||
|
ed: 0.53,
|
||||||
|
to: 0.52,
|
||||||
|
it: 0.50,
|
||||||
|
ou: 0.50,
|
||||||
|
ea: 0.47,
|
||||||
|
hi: 0.46,
|
||||||
|
is: 0.46,
|
||||||
|
or: 0.43,
|
||||||
|
ti: 0.34,
|
||||||
|
as: 0.33,
|
||||||
|
te: 0.27,
|
||||||
|
et: 0.19,
|
||||||
|
ng: 0.18,
|
||||||
|
of: 0.16,
|
||||||
|
al: 0.09,
|
||||||
|
de: 0.09,
|
||||||
|
se: 0.08,
|
||||||
|
le: 0.08,
|
||||||
|
sa: 0.06,
|
||||||
|
si: 0.05,
|
||||||
|
ar: 0.04,
|
||||||
|
ve: 0.04,
|
||||||
|
ra: 0.04,
|
||||||
|
ld: 0.02,
|
||||||
|
ur: 0.02,
|
||||||
|
};
|
||||||
|
|
||||||
|
const TrigramCommonality = {
|
||||||
|
the: 1.81,
|
||||||
|
and: 0.73,
|
||||||
|
tha: 0.33,
|
||||||
|
ent: 0.42,
|
||||||
|
ing: 0.72,
|
||||||
|
ion: 0.42,
|
||||||
|
tio: 0.31,
|
||||||
|
for: 0.34,
|
||||||
|
oft: 0.22,
|
||||||
|
sth: 0.21,
|
||||||
|
};
|
||||||
|
|
||||||
|
function MostProbableAlternateQueries(query) {
|
||||||
|
const words = query.split(' ');
|
||||||
|
|
||||||
|
const reconstruction = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < words.length; i++) {
|
||||||
|
const mostLikely = MostProbableMissSpelling(words[i]);
|
||||||
|
reconstruction.push([...mostLikely]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// go over suggested fixes and construct the permutations of the query
|
||||||
|
// for example if the query is 'brik 2x10X4', the reconstructions are:
|
||||||
|
// brick, [2x10x4, 2 x 10 x 4]
|
||||||
|
// therefore the permutations are:
|
||||||
|
// brick 2x10x4, brick 2 x 10 x 4
|
||||||
|
|
||||||
|
let totalLength = 1;
|
||||||
|
for (let i = 0; i < reconstruction.length; i++) {
|
||||||
|
const length = reconstruction[i].length;
|
||||||
|
totalLength *= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const permutations = [];
|
||||||
|
for (let i = 0; i < totalLength; i++) {
|
||||||
|
const permutation = [];
|
||||||
|
for (let j = 0; j < reconstruction.length; j++) {
|
||||||
|
const index = i % reconstruction[j].length;
|
||||||
|
permutation.push(reconstruction[j][index]);
|
||||||
|
}
|
||||||
|
permutations.push(permutation.join(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
// work out a bit of context to determine the most likely sentence
|
||||||
|
return permutations;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MostProbableMissSpelling(word) {
|
||||||
|
// First work out if it's intended to be a word
|
||||||
|
if (word.match(/[0-9]/g)) {
|
||||||
|
// it's got a number so probably not a word
|
||||||
|
// if it's a number, it's either a year, id or dimension
|
||||||
|
|
||||||
|
// if it's a dimension, we should split by x X
|
||||||
|
if (word.match(/[xX]/g)) {
|
||||||
|
// it's a dimension split by x X
|
||||||
|
const split = word.split(/[xX]/g);
|
||||||
|
return [split.join(' x '), word];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [word];
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's one letter, it's probably not a word
|
||||||
|
if (word.length === 1) {
|
||||||
|
return [word];
|
||||||
|
}
|
||||||
|
|
||||||
|
const trigrams = TriGrams(word);
|
||||||
|
const bigrams = BiGrams(word);
|
||||||
|
|
||||||
|
return [word];
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConditionalTrigramProbability(token) {
|
||||||
|
// closeness of token to the trigram commonality
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConditionalBigramProbability(token) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns list of tokens
|
||||||
|
function TriGrams(word) {
|
||||||
|
return NGrams(word, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BiGrams(word) {
|
||||||
|
return NGrams(word, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function NGrams(word, n) {
|
||||||
|
const tokens = [];
|
||||||
|
for (let i = 0; i < word.length - n + 1; i++) {
|
||||||
|
tokens.push(word.substring(i, i + n));
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Init,
|
||||||
|
MostProbableAlternateQueries,
|
||||||
|
MostProbableMissSpelling,
|
||||||
|
};
|
||||||
11
src/index.js
11
src/index.js
@@ -5,7 +5,7 @@ const API = require('./routes/api.js');
|
|||||||
|
|
||||||
const Database = require('./database/database.js');
|
const Database = require('./database/database.js');
|
||||||
|
|
||||||
const ngrams = require('./controllers/n-grams.js');
|
const ngrams = require('./controllers/spellchecker.js');
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
Config.Load();
|
Config.Load();
|
||||||
@@ -20,15 +20,16 @@ async function main() {
|
|||||||
Logger.Info('Pre-Init Complete');
|
Logger.Info('Pre-Init Complete');
|
||||||
|
|
||||||
await Database.Connect();
|
await Database.Connect();
|
||||||
|
await ngrams.Init();
|
||||||
|
|
||||||
Server.Listen(process.env.PORT);
|
Server.Listen(process.env.PORT);
|
||||||
API.Init();
|
API.Init();
|
||||||
|
|
||||||
await ngrams.Init();
|
|
||||||
|
|
||||||
ngrams.MostProbableAlternateQueries('brick 2x10x4');
|
ngrams.MostProbableAlternateQueries('brick 2x10X4 2 x 2');
|
||||||
ngrams.MostProbableAlternateQueries('lego star wars battlefront');
|
// ngrams.MostProbableAlternateQueries('brick 2 x 10 x 4 2x10X4');
|
||||||
ngrams.MostProbableAlternateQueries('lego stor was s');
|
// ngrams.MostProbableAlternateQueries('lego star wars battlefront');
|
||||||
|
// ngrams.MostProbableAlternateQueries('lego stor was s');
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|||||||
@@ -30,10 +30,11 @@ async function CalculateBasketPrice(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setSubtotal = await SetController.SumPrices(setList, setQuantities);
|
let setSubtotal = await SetController.SumPrices(setList, setQuantities);
|
||||||
const brickSubtotal = await BrickController.SumPrices(brickList, brickQuantities);
|
let brickSubtotal = await BrickController.SumPrices(brickList, brickQuantities);
|
||||||
|
|
||||||
console.log(setSubtotal, brickSubtotal);
|
if (setSubtotal.error) setSubtotal = 0;
|
||||||
|
if (brickSubtotal.error) brickSubtotal = 0;
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const ControllerMaster = require('../controllers/controller-master.js');
|
const ControllerMaster = require('../controllers/controller-master.js');
|
||||||
const BrickController = require('../controllers/brick-controller.js');
|
const BrickController = require('../controllers/brick-controller.js');
|
||||||
const SetController = require('../controllers/set-controller.js');
|
const SetController = require('../controllers/set-controller.js');
|
||||||
|
const SpellController = require('../controllers/spellchecker.js');
|
||||||
|
|
||||||
async function Search(req, res) {
|
async function Search(req, res) {
|
||||||
const q = req.query.q;
|
const q = req.query.q;
|
||||||
@@ -15,14 +16,16 @@ async function Search(req, res) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const alternateQueries = SpellController.MostProbableAlternateQueries(sanatisedQuery);
|
||||||
|
|
||||||
const pageRequested = req.query.page || 1;
|
const pageRequested = req.query.page || 1;
|
||||||
const perPage = req.query.per_page || 16;
|
const perPage = req.query.per_page || 16;
|
||||||
|
|
||||||
// TODO: it is tricky to do a database offset / limit here
|
// TODO: it is tricky to do a database offset / limit here
|
||||||
// due to the fact that we have to combine the results of
|
// due to the fact that we have to combine the results of
|
||||||
// the two queries, look into me (maybe merging the queries)
|
// the two queries, look into me (maybe merging the queries)
|
||||||
const brickResults = await BrickController.Search(sanatisedQuery);
|
const brickResults = await BrickController.Search(alternateQueries);
|
||||||
const setResults = await SetController.Search(sanatisedQuery);
|
const setResults = await SetController.Search(alternateQueries);
|
||||||
|
|
||||||
if (brickResults.error && setResults.error) {
|
if (brickResults.error && setResults.error) {
|
||||||
return res.send(JSON.stringify({
|
return res.send(JSON.stringify({
|
||||||
@@ -32,8 +35,8 @@ async function Search(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
if (brickResults) count += brickResults.length;
|
if (!brickResults.error) count += brickResults.length;
|
||||||
if (setResults) count += setResults.length;
|
if (!setResults.error) count += setResults.length;
|
||||||
|
|
||||||
if (brickResults.error) {
|
if (brickResults.error) {
|
||||||
// remove after the requested page
|
// remove after the requested page
|
||||||
@@ -72,8 +75,8 @@ async function Search(req, res) {
|
|||||||
const bName = b.name.toLowerCase();
|
const bName = b.name.toLowerCase();
|
||||||
const aTag = a.tag.toLowerCase();
|
const aTag = a.tag.toLowerCase();
|
||||||
const bTag = b.tag.toLowerCase();
|
const bTag = b.tag.toLowerCase();
|
||||||
const aFuzzy = q.toLowerCase();
|
const aFuzzy = alternateQueries[0].toLowerCase();
|
||||||
const bFuzzy = q.toLowerCase();
|
const bFuzzy = alternateQueries[0].toLowerCase();
|
||||||
|
|
||||||
const aDist = ControllerMaster.LevenshteinDistance(aName, aFuzzy);
|
const aDist = ControllerMaster.LevenshteinDistance(aName, aFuzzy);
|
||||||
const bDist = ControllerMaster.LevenshteinDistance(bName, bFuzzy);
|
const bDist = ControllerMaster.LevenshteinDistance(bName, bFuzzy);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
const Controller = require('../controllers/set-controller.js');
|
const Controller = require('../controllers/set-controller.js');
|
||||||
const Database = require('../database/database.js');
|
|
||||||
|
|
||||||
async function Get(req, res) {
|
async function Get(req, res) {
|
||||||
// get id from url
|
// get id from url
|
||||||
@@ -18,7 +17,7 @@ async function Get(req, res) {
|
|||||||
|
|
||||||
async function Featured(req, res) {
|
async function Featured(req, res) {
|
||||||
// query all sets and return all of them
|
// query all sets and return all of them
|
||||||
const { sets } = await Controller.GetSets(0, 8);
|
const { sets } = await Controller.GetSets(0, 10);
|
||||||
|
|
||||||
if (sets.error) {
|
if (sets.error) {
|
||||||
res.send(JSON.stringify(sets));
|
res.send(JSON.stringify(sets));
|
||||||
|
|||||||
Reference in New Issue
Block a user