How to nest data from a join table in a one (or many) to many relationship in knex.js? - sql

Let's say I have users, skills and user_skill tables.
How can I query so that if I have 3 users, I get 3 objects (in an array, obviously) and for each user's object there should be a nested array with all the skills?
Something like this:
{
"users": [
{
"name": "dusan",
"id": 1,
"facebook": "dusan's facebook",
"skills": [
{"name": "skill1", "id": 1}
{"name": "skill2", "id": 2}
]
},
{
"name": "jenny",
"id": 2,
"facebook": "jenny's facebook",
"skills": [
{"name": "skill1", "id": 1}
{"name": "skill2", "id": 2}
]
},
{
"name": "michael",
"id": 3,
"facebook": "michael's facebook",
"skills": [
{"name": "skill1", "id": 1}
{"name": "skill2", "id": 2}
]
},
]
}
My code:
await db("users")
.join("user_skill", "users.id", "skills.user_id")
.join("skills", "skills.id", "user_skill.id")
.select(
"users.name as name",
"users.id as id",
"skills.name as skill",
"skills.id as skill_id"
);
The above code returns a completely flat structured JSON, and I want to have skills nested inside the user object.
{
"users": [
{
"name": "dusan",
"id": 1,
"skill": "node.js",
"skill_id": 1
},
{
"name": "dusan",
"id": 1,
"skill": "php",
"skill_id": 2
},
{
"name": "dusan",
"id": 1,
"skill": "mongodb",
"skill_id": 3
},
{
"name": "jaca",
"id": 2,
"skill": "angular",
"skill_id": 4
},
{
"name": "jaca",
"id": 2,
"skill": "reactjs",
"skill_id": 5
},
]
}
I understand this is how SQL is designed, to return flat structure, but is there a way I could accomplish the nesting of related data?

A fellow developer has come up with this solution, so I've decided to share it in hope that someone would find it helpful:
returnedUsers = {
"users": [{
"name": "dusan",
"id": 1,
"skill": "node.js",
"skill_id": 1
},
{
"name": "dusan",
"id": 1,
"skill": "php",
"skill_id": 2
},
{
"name": "dusan",
"id": 1,
"skill": "mongodb",
"skill_id": 3
},
{
"name": "jaca",
"id": 2,
"skill": "angular",
"skill_id": 4
},
{
"name": "jaca",
"id": 2,
"skill": "reactjs",
"skill_id": 5
},
]
};
const groupBy = (array, key) =>
array.reduce((a, c) => ({
...a,
[c[key]]: [...a[c[key]] || [], c]
}), {});
const uniques = (...values) =>
Array.from(new Set([].concat(...values).filter(Boolean)));
const singularize = array =>
array.length == 1 ? array[0] : array;
const singularizedUniques = (...values) =>
singularize(uniques(...values));
const mergeCollect = array =>
array.reduce((mergedObject, curentObject) =>
Object.entries(curentObject).reduce((newObject, [k, v]) => ({
...newObject,
[k]: singularizedUniques(newObject[k], v)
}), mergedObject), {});
const groupByKey = (array, key) =>
Object.fromEntries(Object.entries(groupBy(array, key)).map(([k, v]) => [k, mergeCollect(v)]));
console.log(groupByKey(returnedUsers.users, 'id'));

Related

How to add a specific product variant to the shopping cart

The task is to add a product with selection options to the cart. For example, take product with id:2, which has different color and size options. By clicking on the add product button, the product of the selected option should be added to the cart. Product options are predefined. You can see them in the products array below. At the moment, I have created a cart entity in the global store, where I can add a product without selection options and render it on the cart page.
"products" : [
{
"type": "simple",
"id": 1,
"sku": "s1",
"title": "Product 1",
"regular_price": {
"currency": "USD",
"value": 27.12
},
"image": "/images/1.png",
"brand": 9
},
{
"type": "configurable",
"id": 2,
"sku": "c1",
"title": "Product 2",
"regular_price": {
"currency": "USD",
"value": 54.21
},
"image": "/images/conf/default.png",
"configurable_options": [
{
"attribute_id": 93,
"attribute_code": "color",
"label": "Color",
"values": [
{
"label": "Red",
"value_index": 931,
"value": "#ff0000"
},
{
"label": "Blue",
"value_index": 932,
"value": "#0000ff"
},
{
"label": "Black",
"value_index": 933,
"value": "#000"
}
]
},
{
"attribute_code": "size",
"attribute_id": 144,
"position": 0,
"id": 2,
"label": "Size",
"values": [
{
"label": "M",
"value_index": 1441,
"value": 1
},
{
"label": "L",
"value_index": 1442,
"value": 2
}
]
}
],
"variants": [
{
"attributes": [
{
"code": "color",
"value_index": 931
},
{
"code": "size",
"value_index": 1441
}
],
"product": {
"id": 2001,
"sku": "c1-red-m",
"image": "/image/conf/red.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 931
},
{
"code": "size",
"value_index": 1442
}
],
"product": {
"id": 2002,
"sku": "c1-red-l",
"image": "/image/conf/red.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 932
},
{
"code": "size",
"value_index": 1441
}
],
"product": {
"id": 2003,
"sku": "c1-blue-m",
"image": "/image/conf/blue.png"
}
},
{
"attributes": [
{
"code": "color",
"value_index": 933
},
{
"code": "size",
"value_index": 1442
}
],
"product": {
"id": 2004,
"sku": "c1-black-l",
"image": "/image/conf/black.png"
}
}
],
"brand": 1
}]
My store:
import {createStore} from 'vuex'
import apiRequests from "#/store/actions/apiRequests";
import commonActions from './actions/actions'
import mutations from './mutations/mutations'
import getters from './getters/getters'
const actions = {...commonActions, ...apiRequests}
export default createStore({
state: {
products: [],
brands: [],
cart: []
},
mutations,
actions,
getters
})
actions:
export default {
ADD_TO_CART({commit}, product) {
commit('SET_CART', product)
},
INCREMENT_CART_ITEM({commit}, index) {
commit('INCREMENT', index)
},
DECREMENT_CART_ITEM({commit}, index) {
commit('DECREMENT', index)
},
DELETE_FROM_CART({commit}, index) {
commit('REMOVE_ITEM_FROM_CART', index)
}
}
mutations:
export default {
SET_PRODUCTS_TO_STATE: (state, products) => {
state.products = products
},
SET_BRANDS_TO_STATE: (state, brands) => {
const includesBrands = {}
for (const brand of brands) {
for (const product of state.products) {
if (product.brand === brand.id) {
includesBrands[brand.id] = brand
}
}
}
state.brands = Object.values(includesBrands)
state.brands.unshift({title: "All brands"})
},
SET_CART: (state, product) => {
if (state.cart.length) {
let isProductExists = false
state.cart.map((item) => {
if (item.id === product.id) {
isProductExists = true
item.quantity++
}
})
if (!isProductExists) {
state.cart.push({...product, quantity: 1})
}
} else {
state.cart.push({...product, quantity: 1})
}
},
REMOVE_ITEM_FROM_CART: (state, index) => {
state.cart.splice(index, 1)
},
INCREMENT: (state, index) => {
state.cart[index].quantity++
},
DECREMENT: (state, index) => {
if (state.cart[index].quantity > 1) {
state.cart[index].quantity--
}
}
}
and getters:
export default {
BRANDS(state) {
return state.brands
},
CART(state) {
return state.cart
},
PRODUCTS(state) {
return state.products = state.products.map((product) => {
const brand = state.brands.find((b) => b.id === product.brand)
return {...product, brandName: brand?.title || 'no brand'}
})
},
}
Now I am faced with the fact that I do not understand how I can add a certain product variant to the cart and draw it on the cart page.

aggregate in mongodb left join with $lookup

I have three collections
posts=[
{
"id": "p1",
"title": "title 1"
},
{
"id": "p2",
"title": "title 2"
}]
users = [
{
"id": "u1",
"name": "name1"
},
{
"id": "u2",
"name": "name2"
}]
comments = [
{
"userId": "u1",
"postId": "p1",
"comment": "comment 1"
}]
I want to get all collection posts and comments in each post by userId(u1) as:
posts=[
{
"id": "p1",
"title": "title 1",
"comments":[
"userId": "u1",
"comment": "comment 1"
]
},
{
"id": "p2",
"title": "title 2",
"comments":[]
}]
I used aggregate function and $lookup operator but I don't know using the $match operator to filter userId. I used aggregate bellow:
self.db.posts.aggregate([
{
"$lookup":{
"from": "comments",
"localField": "id",
"foreignField": "postId",
"as": "comments",
}
},
{
"$match":{
"comments.userId": {"$eq": param.objectUserId}
},
},
{"$skip": (param.page - 1) * param.pageSize},
{"$limit": param.pageSize},
{"$sort": {"unixDate": pymongo.DESCENDING}}
])
It only return one post in array corresponding with userId="u1"
Please help me!
Thank all!
You have to make use of the pipeline option of $lookup stage and pass the additional conditions that you want to apply.
db.posts.aggregate([
{
"$lookup": {
"from": "comments",
"let": {
"pId": "$id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$postId",
"$$pId"
],
},
"userId": "u1",
},
},
{
"$project": {
"_id": 0,
"userId": 1,
"comment": 1,
},
},
],
"as": "comments"
}
}
])
Mongo Playground Sample Execution
self.db.posts.aggregate([
{
"$lookup": {
"from": "comments",
"let": {
"pId": "$id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$postId",
"$$pId"
],
},
"userId": param.objectUserId,
},
},
{
"$project": {
"_id": 0,
"userId": 1,
"comment": 1,
},
},
],
"as": "comments"
}
},
{"$skip": (param.page - 1) * param.pageSize},
{"$limit": param.pageSize},
{"$sort": {"unixDate": pymongo.DESCENDING}}
])

RamdaJS groupBy and tranform object

I want to transform this array of objects using RamdaJS. From this array of objects
let children = [
{ "name": "Bob", "age": 8, "father": "Mike" },
{ "name": "David", "age": 10, "father": "Mike" },
{ "name": "Amy", "age": 2, "father": "Mike" },
{ "name": "Jeff", "age": 11, "father": "Jack" }
]
into this array of objects
let fatherAndKids = [
{
"father": "Mike",
"count" : 3,
"kids": [
{ "name": "Bob", "age": 8 },
{ "name": "David", "age": 10 },
{ "name": "Amy", "age": 2
}
]
},
{
"father": "Jack",
"count" : 1,
"kids": [
{ "name": "Jeff", "age": 11 }
]
}
]
Here's what i did so far. But i failed to remove the father keys from kids's array
R.pipe(
R.groupBy(R.prop('father')),
R.map(kids => ({
father: R.head(kids)["father"],
count: kids.length,
kids: kids
})),
R.values()
)(children)
Use R.applySpec to create the object, and use R.map with R.dissoc to remove the 'father' property:
const { pipe, groupBy, prop, applySpec, head, length, map, dissoc, values } = R
const fn = pipe(
groupBy(prop('father')),
map(applySpec({
father: pipe(head, prop('father')),
count: length,
kids: map(dissoc('father'))
})),
values
)
const children = [
{ "name": "Bob", "age": 8, "father": "Mike" },
{ "name": "David", "age": 10, "father": "Mike" },
{ "name": "Amy", "age": 2, "father": "Mike" },
{ "name": "Jeff", "age": 11, "father": "Jack" }
]
const result = fn(children)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

Lodash find array in array

I have an array that looks like this:
var roles = [
{ "label": "Super Auditor", "value": 4 },
{ "label": "Super Finance Officer", "value": 3 },
{ "label": "Super Manager", "value": 2 },
{ "label": "Super Admin", "value": 1 }
]
I need to find if it is in array and get that object.
var needToFind = [4, 1]
Expected Results:
var results =[
{ "label": "Super Auditor", "value": 4 },
{ "label": "Super Admin", "value": 1 }
]
I just don't know how to do it. TY
You can use _.intersectionWith():
var roles = [
{ "label": "Super Auditor", "value": 4 },
{ "label": "Super Finance Officer", "value": 3 },
{ "label": "Super Manager", "value": 2 },
{ "label": "Super Admin", "value": 1 }
]
var needToFind = [4, 1]
var result = _.intersectionWith(roles, needToFind, (a, b) => a.value === b)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Here's an answer using VanillaJS
const roles = [
{ "label": "Super Auditor", "value": 4 },
{ "label": "Super Finance Officer", "value": 3 },
{ "label": "Super Manager", "value": 2 },
{ "label": "Super Admin", "value": 1 }
];
const needToFind = [4, 1];
const results = roles.filter(obj => needToFind.includes(obj.value))
console.log(results)
Basically you apply filter on roles and using includes to see if value exist in needtoFind

Select object from array of objects in Postgres

I have a table questions where options is jsonb which is an array of objects.
"questions": [
{
"id": 76,
"text": "What is the capital of Telangana ?",
"options": [
{
"id": 1,
"text": "Hyderabad",
"correct": true
},
{
"id": 2,
"text": "Bangalore",
"correct": false
},
{
"id": 3,
"text": "Amaravathi",
"correct": false
},
{
"id": 4,
"text": "Chennai",
"correct": false
}
],
"position": 1
}
Can someone help how can I select an option object from options by a given id using PostgreSql?
you can select it. unnest and filter, eg:
t=# with c(j) as (values('{"options": [
{
"id": 1,
"text": "Hyderabad",
"correct": true
},
{
"id": 2,
"text": "Bangalore",
"correct": false
},
{
"id": 3,
"text": "Amaravathi",
"correct": false
},
{
"id": 4,
"text": "Chennai",
"correct": false
}
]}'::jsonb))
, m as (select jsonb_array_elements(j->'options') a from c) select a from m where a->>'id' = '3';
a
---------------------------------------------------
{"id": 3, "text": "Amaravathi", "correct": false}
(1 row)