Recipe / Bill of Materials MySQL Query - javascript

I am writing a Bill of Materials/Recipe App for Minecraft in Node.js, Express, React and MySQL.
I have what I think is a good structure for the database with the tables, but I am running into an issue with the Queries I need to run. Here is an example of what I am trying to do with an example output (I know the recipe is not correct MC players).
Ingredients Table:
RECIPE || INGREDIENT || QTY || TYPE
Piston || Redstone || 1 || RM
Piston || Iron Ingot || 1 || RM
Piston || Wood Planks || 3 || RM
Piston || Stone || 4 || RM
Sticky Piston || Piston || 2 || CO
Sticky Piston || Slimeball || 1 || RM
What I want is that when I Query for a Sticky Piston, there is a table returned with all of the required components summed together (on top) and all of the required Raw Materials summed together (below).
Example Required Output in Table format or JSON format:
INGREDIENT || QTY || TYPE
Piston || 2 || CO
Redstone || 2 || RM
Iron Ingot || 2 || RM
Wood Planks || 6 || RM
Stone || 8 || RM
Slimeball || 1 || RM
[
{
"Ingredient": "Piston",
"Qty": 2,
"Type": CO
},
{
"Ingredient": "Redstone",
"Qty": 2,
"Type": RM
},
{
"Ingredient": "Iron Ingot",
"Qty": 2,
"Type": RM
},
{
"Ingredient": "Wood Planks",
"Qty": 6,
"Type": RM
},
{
"Ingredient": "Stone",
"Qty": 8,
"Type": RM
},
{
"Ingredient": "Slimeball",
"Qty": 2,
"Type": RM
}
]
I assume it is a JOIN with some other fancy stuff that can do it all inside of MYSQL syntax, but I cannot figure it out despite two days of trying out different combinations.
Alternatively, I would be fine if instead of using MYSQL syntax, that JS was used that I can run on my Node.js server.
Here is a modified version of the schema per instructions from below. This is what my understanding of the relationship should be, but I still cannot figure out how to return all of the required resources when recipes are made up of other components that require other Materials as well.
CREATE TABLE Ingredients
(
MaterialID INT unsigned NOT NULL AUTO_INCREMENT,
Material VARCHAR(250) NOT NULL,
MaterialImage VARCHAR(250),
PRIMARY KEY (MaterialID)
);
CREATE TABLE Recipes
(
RecipeID INT unsigned NOT NULL AUTO_INCREMENT,
Recipe VARCHAR(250) NOT NULL,
PRIMARY KEY (ComponentID)
);
CREATE TABLE Recipes_Ingredients
(
RecipeID INT unsigned NOT NULL,
MaterialID INT unsigned NOT NULL,
Quantity INT unsigned NOT NULL
)
Thanks!

UPDATE/EDIT:
Updated to correct calculation of the Quantity
I've changed your table arrangement to better match the data you are trying to represent. Since a "recipe" can also be a "material", I've eliminated the recipe table, and stored that data in the materials table.
I also created a SQL Fiddle so you can play with the query and fine tune the results.
The first query below returns all "recipes" and their components. To get just one particular recipe, just add a condition to the where clause that selects the particular recipe.
If you would prefer to have the components listed in a single field, with one row per recipe, you can use the GROUP_CONCAT function and change the group by clause.
The second query below illustrates the GROUP_CONCAT function. It also shows how to change missing sub-ingredients from NULL to 'None'
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE `Ingredients`
(
`MaterialID` INT unsigned NOT NULL AUTO_INCREMENT,
`Material` VARCHAR(250) NOT NULL,
`MaterialImage` VARCHAR(250),
`IsRecipe` TINYINT(1) DEFAULT 0 NULL,
PRIMARY KEY (`MaterialID`)
);
CREATE TABLE `Recipes_Ingredients`
(
`id` INT unsigned NOT NULL AUTO_INCREMENT,
`RecipeID` INT unsigned NOT NULL,
`MaterialID` INT unsigned NOT NULL,
`Quantity` INT unsigned NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `Ingredients`
(`MaterialID`,`Material`,`MaterialImage`,`IsRecipe`)
VALUES
(1,'Redstone','redstone.jpg',0),
(2,'Iron Ingot','ironingot.jpg',0),
(3,'Wood Planks','woodplanks.jpg',0),
(4,'Stone','stone.jpg',0),
(5,'Slimeball','slimeball.jpg',0),
(6,'Piston','piston.jpg',1),
(7,'Sticky Piston','stickypiston.jpg',1),
(8,'Sticky Piston 2','stickypiston2.jpg',1);
INSERT INTO `Recipes_Ingredients`
(`RecipeID`,`MaterialID`,`Quantity`)
VALUES
(6,1,1),
(6,2,1),
(6,3,3),
(6,4,4),
(7,6,1),
(7,5,1),
(8,6,2),
(8,5,1);
Query 1:
SELECT
a.`MaterialID`,
c.`MaterialID`,
a.`Material` as `Recipe`,
a.`MaterialImage` as `RecipeImage`,
c.`Material` as `Ingredient`,
b.`Quantity` as `FirstIngredientQuantity`,
c.`MaterialImage` as `IngredientImage`,
IF(d.`Quantity` IS NULL,SUM(b.`Quantity`),COALESCE(d.`Quantity`,0)*b.`Quantity`) as `Quantity`,
e.`Material` as `Ingredient`,
e.`MaterialImage` as `MaterialImage`
FROM `Ingredients` a
LEFT JOIN `Recipes_Ingredients` b
ON b.`RecipeID` = a.`MaterialID`
LEFT JOIN `Ingredients` c
ON c.`MaterialID` = b.`MaterialID`
LEFT JOIN `Recipes_Ingredients` d
ON d.`RecipeID` = c.`MaterialID`
LEFT JOIN `Ingredients` e
ON e.`MaterialID` = d.`MaterialID` AND c.`IsRecipe` = 1
WHERE a.`IsRecipe` = 1 AND a.`MaterialID` in (7,8)
GROUP BY a.`MaterialID`,c.`MaterialID`,e.`MaterialID`
Results:
| MaterialID | MaterialID | Recipe | RecipeImage | Ingredient | FirstIngredientQuantity | IngredientImage | Quantity | Ingredient | MaterialImage |
|------------|------------|-----------------|-------------------|------------|-------------------------|-----------------|----------|-------------|----------------|
| 7 | 5 | Sticky Piston | stickypiston.jpg | Slimeball | 1 | slimeball.jpg | 1 | (null) | (null) |
| 7 | 6 | Sticky Piston | stickypiston.jpg | Piston | 1 | piston.jpg | 1 | Redstone | redstone.jpg |
| 7 | 6 | Sticky Piston | stickypiston.jpg | Piston | 1 | piston.jpg | 1 | Iron Ingot | ironingot.jpg |
| 7 | 6 | Sticky Piston | stickypiston.jpg | Piston | 1 | piston.jpg | 3 | Wood Planks | woodplanks.jpg |
| 7 | 6 | Sticky Piston | stickypiston.jpg | Piston | 1 | piston.jpg | 4 | Stone | stone.jpg |
| 8 | 5 | Sticky Piston 2 | stickypiston2.jpg | Slimeball | 1 | slimeball.jpg | 1 | (null) | (null) |
| 8 | 6 | Sticky Piston 2 | stickypiston2.jpg | Piston | 2 | piston.jpg | 2 | Redstone | redstone.jpg |
| 8 | 6 | Sticky Piston 2 | stickypiston2.jpg | Piston | 2 | piston.jpg | 2 | Iron Ingot | ironingot.jpg |
| 8 | 6 | Sticky Piston 2 | stickypiston2.jpg | Piston | 2 | piston.jpg | 6 | Wood Planks | woodplanks.jpg |
| 8 | 6 | Sticky Piston 2 | stickypiston2.jpg | Piston | 2 | piston.jpg | 8 | Stone | stone.jpg |
Query 2:
SELECT
a.`MaterialID`,
c.`MaterialID`,
a.`Material` as `Recipe`,
a.`MaterialImage` as `RecipeImage`,
c.`Material` as `Ingredient`,
c.`MaterialImage` as `MaterialImage`,
SUM(b.`Quantity` + COALESCE(d.`Quantity`,0)) as `Quantity`,
COALESCE(GROUP_CONCAT(CONCAT(e.`Material`,' (',b.`Quantity` * d.`Quantity`,') [',e.`MaterialImage`,']')),'None') as `Ingredients`
FROM `Ingredients` a
LEFT JOIN `Recipes_Ingredients` b
ON b.`RecipeID` = a.`MaterialID`
LEFT JOIN `Ingredients` c
ON c.`MaterialID` = b.`MaterialID`
LEFT JOIN `Recipes_Ingredients` d
ON d.`RecipeID` = c.`MaterialID`
LEFT JOIN `Ingredients` e
ON e.`MaterialID` = d.`MaterialID` AND c.`IsRecipe` = 1
WHERE a.`IsRecipe` = 1
GROUP BY a.`MaterialID`,c.`MaterialID`
Results:
| MaterialID | MaterialID | Recipe | RecipeImage | Ingredient | MaterialImage | Quantity | Ingredients |
|------------|------------|-----------------|-------------------|-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------|
| 6 | 1 | Piston | piston.jpg | Redstone | redstone.jpg | 1 | None |
| 6 | 2 | Piston | piston.jpg | Iron Ingot | ironingot.jpg | 1 | None |
| 6 | 3 | Piston | piston.jpg | Wood Planks | woodplanks.jpg | 3 | None |
| 6 | 4 | Piston | piston.jpg | Stone | stone.jpg | 4 | None |
| 7 | 5 | Sticky Piston | stickypiston.jpg | Slimeball | slimeball.jpg | 1 | None |
| 7 | 6 | Sticky Piston | stickypiston.jpg | Piston | piston.jpg | 13 | Redstone (1) [redstone.jpg],Iron Ingot (1) [ironingot.jpg],Wood Planks (3) [woodplanks.jpg],Stone (4) [stone.jpg] |
| 8 | 5 | Sticky Piston 2 | stickypiston2.jpg | Slimeball | slimeball.jpg | 1 | None |
| 8 | 6 | Sticky Piston 2 | stickypiston2.jpg | Piston | piston.jpg | 17 | Redstone (2) [redstone.jpg],Iron Ingot (2) [ironingot.jpg],Wood Planks (6) [woodplanks.jpg],Stone (8) [stone.jpg] |

Related

TypeScript: How to specify a reference column and copy its value to remaining columns?

I have a table that looks like this:
+-------+-------+-------+-------+-------+
| Week1 | Week2 | Week3 | Week4 | Week5 |
+-------+-------+-------+-------+-------+
| AAAAA | BBBBB | CCCCC | DDDDD | EEEEE |
+-------+-------+-------+-------+-------+
I would like to write a function that:
Input: (1) reference column name (2) a list of impacted column name
Output: impacted column values changed to the same as the reference column
For example:
function(1,2) means: copy Week 1 value to Week 2
+-------+-------+-------+-------+-------+
| Week1 | Week2 | Week3 | Week4 | Week5 |
+-------+-------+-------+-------+-------+
| AAAAA | AAAAA | CCCCC | DDDDD | EEEEE |
+-------+-------+-------+-------+-------+
For another example:
function(1,[2,3]) means: copy Week 1 value to Week 2 and Week 3
+-------+-------+-------+-------+-------+
| Week1 | Week2 | Week3 | Week4 | Week5 |
+-------+-------+-------+-------+-------+
| AAAAA | AAAAA | AAAAA | DDDDD | EEEEE |
+-------+-------+-------+-------+-------+
For another example:
function(4,[2,3,5]) means: copy Week 4 value to Week 2, Week 3, and Week 5
+-------+-------+-------+-------+-------+
| Week1 | Week2 | Week3 | Week4 | Week5 |
+-------+-------+-------+-------+-------+
| AAAAA | DDDDD | DDDDD | DDDDD | DDDDD |
+-------+-------+-------+-------+-------+
For another example:
function(2,[1,3,5]) means: copy Week 2 value to Week 1, Week 3, and Week 5
+-------+-------+-------+-------+-------+
| Week1 | Week2 | Week3 | Week4 | Week5 |
+-------+-------+-------+-------+-------+
| BBBBB | BBBBB | BBBBB | DDDDD | BBBBB |
+-------+-------+-------+-------+-------+
There could be only 1 reference column, but there could be multiple impacted columns.
I am very new to TypeScript and can't wrap my head around on how to do this in TypeScript.
I am not sure if thinking this way makes sense:
Concatenate all of 5 Weeks' of value into a long string (25 digits)
Then I am able to have the index of each week (e.g. Week 1 value = str[0:4], Week 2 value = str[5:9] etc.)
Then just take the reference column value and replace to the impacted columns
Much appreciation for any help!

From a join table in MySQL, how to find all the foreign_key1 ids which have foreign_key2 assigned to them ONLY from given list?

I have two tables. Table A and Table B. Both are connected with a many-to-many relationship.
Table A:
ID
---
1
2
3
Table B:
ID
---
4
5
6
7
Table AB:
ID | A_ID | B_ID
----------------
8 | 1 | 4
9 | 1 | 5
10 | 1 | 6
11 | 1 | 7
12 | 2 | 5
13 | 2 | 6
14 | 3 | 6
I want to find all Ids from table A, which have assigned Ids from table B from only given B Ids. For example here, we pass Ids in our queries - [5,6]
So it should return all A ids which have either all values from array [5,6] assigned or any no. of values but only from array. If other than array id is also assigned then don't include in result. Here result will be 2 and 3. ( 1 is not because it also has 4,7 assigned to it).
I am using Sequelize with typescript.
You need a sub select to omit unwanted A_ID. Playground: http://sqlfiddle.com/#!9/524468/3
SELECT DISTINCT A_ID
FROM AB
WHERE B_ID IN (5, 6)
AND A_ID NOT IN (
SELECT DISTINCT A_ID
FROM AB
WHERE B_ID NOT IN (5, 6)
)

Remove everything after a certain character in txt file

hello ı m new to coding , ı need your help about something , ı m trying to filter a txt file and make a list in a format i like
here is what original txt file looks like :
pepitbeng:davy141089 | LV: 5 | BE: 1017 | RP: 400 | Refunds: 3 | Champs: 1 | Skins: 0 | Email Verified: true | Lastplay: Error
korvin918:M5al3elu2z6k | LV: 41 | BE: 2065 | RP: 23 | Refunds: 1 | Champs: 57 | Skins: 23 | Email Verified: true | Lastplay: 1/11/2019 7:02:15 PM
monkeyshadowtms:apolo2002 | LV: 21 | BE: 6795 | RP: 0 | Refunds: 3 | Champs: 10 | Skins: 0 | Email Verified: true | Lastplay: 7/25/2019 5:00:15 PM
and there are thousands of them
what ı like to do is line by line delete everything after space so only id and password left , end result will looks like this
pepitbeng:davy141089
korvin918:M5al3elu2z6k
monkeyshadowtms:apolo2002
ı try few things but can only get first line ,
var fs = require('fs');
var textByLine = fs.readFileSync('1.txt').toString().split(" ");
console.log(textByLine[0]);
this way ı can get pepitbeng:davy141089 but cant get to second line because everything is deleted after them so how can ı get 0 array of every line
ı also try this
var fs = require('fs');
var textByLine = fs.readFileSync('1.txt').toString().split("\n");
console.log(textByLine[0]);
this way ı can get line by line but whole part result of above code
pepitbeng:davy141089 | LV: 5 | BE: 1017 | RP: 400 | Refunds: 3 | Champs: 1 | Skins: 0 | Email Verified: true | Lastplay: Error
ı feel like ı should use forEach() function but ı dont know how to implement to this waiting for your response thanks.
sorry to bother ı manage to solve
var fs = require('fs');
var textByLine = fs.readFileSync('1.txt').toString().split("\n");
console.log(textByLine[0].split(" ")[0]);
hopefully help someone else
You can use a regular expression to match non-spaces at the beginning of the line:
var fs = require('fs');
var lines = fs.readFileSync('1.txt')
.toString()
.split("\n")
.map(line => line.match(/\S*/)[0]);
\S matches a non-space character, and the * repeater matches as many of those characters in a row as it can.
const text = `pepitbeng:davy141089 | LV: 5 | BE: 1017 | RP: 400 | Refunds: 3 | Champs: 1 | Skins: 0 | Email Verified: true | Lastplay: Error
korvin918:M5al3elu2z6k | LV: 41 | BE: 2065 | RP: 23 | Refunds: 1 | Champs: 57 | Skins: 23 | Email Verified: true | Lastplay: 1/11/2019 7:02:15 PM
monkeyshadowtms:apolo2002 | LV: 21 | BE: 6795 | RP: 0 | Refunds: 3 | Champs: 10 | Skins: 0 | Email Verified: true | Lastplay: 7/25/2019 5:00:15 PM`;
var lines = text
.split("\n")
.map(line => line.match(/\S*/)[0]);
console.log(lines);
If your output needs to be a string instead, then replace everything past a space with the empty string:
const text = `pepitbeng:davy141089 | LV: 5 | BE: 1017 | RP: 400 | Refunds: 3 | Champs: 1 | Skins: 0 | Email Verified: true | Lastplay: Error
korvin918:M5al3elu2z6k | LV: 41 | BE: 2065 | RP: 23 | Refunds: 1 | Champs: 57 | Skins: 23 | Email Verified: true | Lastplay: 1/11/2019 7:02:15 PM
monkeyshadowtms:apolo2002 | LV: 21 | BE: 6795 | RP: 0 | Refunds: 3 | Champs: 10 | Skins: 0 | Email Verified: true | Lastplay: 7/25/2019 5:00:15 PM`;
const newText = text.replace(/ .*/g, '');
console.log(newText);

A definition of the ^ operation in javascript [duplicate]

This question already has an answer here:
What does this symbol mean in JavaScript?
(1 answer)
Closed 3 years ago.
Can't seem to find a definition anywhere.
Typing into the console I can see...
5^4 = 1
5^3 = 6
5^2 = 7
Any ideas why?
It's a bitwise operation, ^ specifically does a XOR operation on the numbers.
XOR truth table
+-------------------+
| a | b | a ^ b |
+-------------------+
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
+-------------------+
00001001 -> 5
00001000 -> 4
--------
00000001 -> 1
00001001 -> 5
00000011 -> 3
--------
00001010 -> 6
00001001 -> 5
00000010 -> 2
--------
00001011 -> 7

Google chart NumberRangeFilter SUM instead of filter?

Is there a way to force NumberRangeFilter to display the summed results of other rows in range?
As an example I have this table:
A | B | C
----------
1 | 1 | 1
2 | 1 | 3
3 | 2 | 1
4 | 0 | 1
5 | 0 | 0
If I implement a RangeFilter and set it from 2 to 4, I would like a chart to display the summed values of B2:B4 and C2:C4 fields.
This is default functionality:
This is what I want to achieve:
How can I achieve such a thing ?

Categories