How can I write this code in more elegant way. I have looked on lodash etc. but couldn't actually find the best way to destructure object for my needs.
Because I will write those properties on mongo I also tried to verify if they are exist or not.
const { _id, name, bio, birth_date, photos, instagram, gender, jobs, schools } = element
let myPhotos = photos.map((photo) => photo.id)
let insta = {}
if (instagram) {
insta.mediaCount = instagram.media_count
insta.profilePicture = instagram.profile_picture
insta.username = instagram.username
insta.photos = instagram.photos.map((photo) => photo.image)
}
const doc = {}
doc._id = ObjectId(_id)
doc.name = name
doc.birthDate = new Date(birth_date)
if (bio.length) {
doc.bio = bio
}
if (myPhotos.length) {
doc.photos = myPhotos
}
if (Object.keys(insta).length) {
doc.instagram = insta
}
doc.gender = gender
if (jobs.length) {
doc.jobs = jobs
}
if (schools.length) {
doc.schools = schools
}
try {
await collection.insertOne(doc)
} catch (error) {
console.log("err", error)
}
You could define the doc all at once using the ternary operator to test conditions. If the undefined properties need to be removed, then you can remove them via reduce afterward.
const { _id, name, bio, birth_date, photos, instagram, gender, jobs, schools } = element
const myPhotos = photos.map(({ id }) => id)
const insta = !instagram ? undefined : (() => {
const { media_count, profile_picture, username, photos } = instagram;
return {
mediaCount: media_count,
profilePicture: profile_picture,
username,
photos: photos.map(({ image }) => image)
}
})();
const docWithUndef = {
_id: ObjectId(_id),
name,
gender,
birthDate: new Date(birth_date),
bio: bio.length ? bio : undefined,
photos: myPhotos.length ? myPhotos : undefined,
instagram: insta,
jobs: jobs.length ? jobs : undefined,
schools: schools.length ? schools : undefined,
}
const doc = Object.entries(docWithUndef)
.reduce((accum, [key, val]) => {
if (val !== undefined) accum[key] = val;
return accum;
});
try {
await collection.insertOne(doc)
} catch (error) {
console.log("err", error)
}
Note the destructuring of the arguments to reduce the syntax noise, and the use of const rather than let (improves code readability).
Related
i have a query "details" which is like below
query details(
$id: ID!
) {
something(id: $id) {
id
itemId
typesId
itemDetails {
id
name
}
typesDetails {
id
name
}
}
}
i have defined the types like below
type itemDetails {
id: String,
name: String,
}
type typesDetails {
id: String,
name: String,
}
type something {
id: ID!
itemId: ID
typesId: [ID!]
itemDetails: itemDetails
typesDetails: [typesDetails]
}
on the resolvers side (graphql) i have to field resolve the itemDetails (with the itemId i recieve from backend). this itemId can be null or can have some string value like example '1'.
and typesDetails with the typesId i receive from backend. typesId can be null or array of ids like example ['1','2',...]
const resolvers: Resolvers = {
something: {
itemDetails: async(parent, args, { dataSources: { itemsAPI} }) => {
const itemId = get (parent, 'itemId'); //can be null or string value
if(itemId) {
const { data } = await itemsAPI.getItems();
const item = data.filter((item: any) =>
itemId === item.id
); //filter the data whose id is equal to itemId
return {
id: item[0].id,
name: item[0].name,
}
}else { // how to rewrite this else part
return {}:
}
},
typesDetails: async (parent, args, { dataSources: {assetTypesAPI} }) => {
const typesId = get(parent, 'typesId');
if (typesId) {
const allTypes = await typesAPI.getTypes();
const res = typesId.map((id: any) => allTypes.find((d) => d.id === id)); //filter
//allTypes that match typesId
const final = res.map(({id, name}: {id:string, name:string}) => ({id,name}));
//retreive the id and name fields from res array and put it to final array
return final;
} else { // how to rewrite this else part
return [{}];
}
}
}
The above code works. but the code looks clumsy in the way i return empty array if no itemId and typesId returned from backend.
how can i handle the case for itemDetails field if itemId is null from backend and typesDetails field if typesId is null from backend.
could someone help me with this. thanks.
The question is more about graphql than it is react and typescript. You do well to change the tag 🏷 of the question for proper matching.
The perfect solution for me would be validating the itemId in the case of itemDetails. If the value is null, throw an error or return an empty object like you're doing in the else section of the itemDetails.
I don't think itemId is coming from the backend in the case above, it should be coming from the argument passed to the query itemDetails
The same applies to the typesDetails.
Using try and catch can help catch any error during the async operations to the API (database).
const resolvers: Resolvers = {
something: {
itemDetails: async(parent, args, { dataSources: { itemsAPI} }) => {
try {
const itemId = get (parent, 'itemId'); //can be null or string value
if(itemId) {
const { data } = await itemsAPI.getItems();
const item = data.filter((item: any) =>
itemId === item.id
); //filter the data whose id is equal to itemId
return {
id: item[0].id,
name: item[0].name,
}
}else { // how to rewrite this else part
return {}:
}
} catch(error) {
throw new Error('something bad happened')
}
},
typesDetails: async (parent, args, { dataSources: {assetTypesAPI} }) => {
try {
const typesId = get(parent, 'typesId');
if (typesId) {
const allTypes = await typesAPI.getTypes();
const res = typesId.map((id: any) => allTypes.find((d) => d.id === id)); //filter
//allTypes that match typesId
const final = res.map(({id, name}: {id:string, name:string}) => ({id,name}));
//retreive the id and name fields from res array and put it to final array
return final;
} else { // how to rewrite this else part
return [{}];
}
} catch (error) {
throw new Error('something happened')
}
}
}
I have type definitions for A and B defined in the schema.graphql file. Resolvers for these are auto-generated and work well (in other places).
To create a scoring mechanism that ranks nodes with label B in relation to the node with label A, I am writing a CustomResolver query that executes a cypher query and returns a collection of bs and a computed score as defined in the ScoredBs type.
The schema.graphql file looks like this.
type Query {
CustomResolver(idA: ID!, idsB: [ID!]!): [ScoredBs]
}
type A {
id: ID! #id
# and some more stuff of course
}
type B {
id: ID! #id
# more fields that use the #relation or #cypher decorator
}
type ScoredBs {
bs: [B]
score: Float
}
This is where the custom resolver is defined:
const resolvers = {
Query: {
CustomResolver: async (
parent,
{ idA, idsB },
context,
info
) => {
const cypher = `
MATCH (a:A {id: $idA})
MATCH (a)<--(b:B) WHERE b.id IN $idsB
WITH
b
RETURN DISTINCT collect(b) AS bs, rand() AS score
ORDER BY score DESC
`
const session = context.driver.session()
const results = await session
.run(cypher, { idA, idsB })
.then((result) => {
return result.records.map((record) => {
return {
bs: record.get('bs'),
score: record.get('score')?.low || null,
}
})
})
.catch(console.log)
.then((results) => {
session.close()
return results
})
return results
},
},
}
When I run the query in the apollo-server graphql playground i am receiving an error:
"message": "Resolve function for "B.id" returned undefined",
query {
CustomResolver(
idA:"2560886f-654b-4047-8d7a-386cd7d9f670",
idsB: ["01ec8367-a8ae-4600-9f88-ec141b2cae2c", "032f9a88-98c3-4968-8388-659ae26d63b3"]
) {
bs {
id
}
score
}
}
I solved this by rewriting part of the code:
const results = await session
.run(cypher, { idA, idsB })
.then((result) => {
return result.records.map((record) => {
// diff start
const obj = record.toObject()
const bs = obj.bs.map((b) => b.properties)
return {
bs, // diff end
score: record.get('score')?.low || null,
}
})
})
.catch(console.log)
.then((results) => {
session.close()
console.log(results)
return results
})
return results
Hello guys I have the following query but it returns only an array with skills instead the object with array inside
To make it clearer
EndorsedSkill response
{
_id
userId
skills: [
{_id},
{_id}
]
}
Skill response
{
_id
name
}
What I want to receive is
{
_id
userId
skills: [
{_id, name},
{_id, name}
]
}
The query looks like this
getUsersSkills: async (_, { u_id, ...args }, { user }) => {
try {
await requireAuth(user);
const p1 = Skill.find({}).sort({ createdAt: -1 });
const p2 = EndorsedSkill.findOne({ userId: u_id });
const [skills, endorsed] = await Promise.all([p1, p2]);
const checkEndorsedSkills = await skills.reduce((arr, skill) => {
const s = skill.toJSON();
if (endorsed.skills.some(s => s.equals(skill._id))) {
arr.push({
...s,
endorsed: true
});
} else {
arr.push({
...s,
endorsed: false
});
}
return arr;
}, []);
const endorsedSkills = checkEndorsedSkills.filter(
skill => skill.endorsed === true
);
return endorsedSkills;
} catch (error) {
throw error;
}
},
Can you please mention that how you connected to mongoDB?
I have the following data:
data = {
id: 10012,
name: "abc",
hobby: ["cricket", "football"]
}
My table structure is:
id int64,
name string(25),
petName string(20),
hobby string(25)
How to insert an array of data into the spanner table?
async insert(data) {
const result;
const params = {
id: data.id,
name: data.name,
petName: null
}
await data.hobby.map(async h => {
params.hobby = h;
const query = 'INSERT INTO my_table (id, name, petName, hobby) VALUES (#id, #name, #petName, #hobby)';
result = await db.RunTrans(query, params);
});
return result;
}
The above code doesn't insert the values into the table. Kindly help me with this..
Destructure out the wanted values (to avoid unnecessary repeated dot notation) then use template literals:
async insert(data) {
const result;
const params = {
id: data.id,
name: data.name,
petName: null
}
await data.hobby.map(async h => {
params.hobby = h;
const { id, name, petName, hobby } = params;
const query = `INSERT INTO my_table (id, name, petName, hobby) VALUES (${id}, ${name}, ${petName}, ${hobby})`;
result = await db.RunTrans(query, params);
});
return result;
}
You aren't handling any promise issues, so it is possible a few different things.
I am not certain your columns are nullable. If they aren't petName being NULL isn't valid for the schema.
#google-cloud/spanner doesn't have a RunTrans method. it has a runTransaction and a runTransactionAsync method though.
The way this is authored, the code would result in multiple rows with the same id. Typically id would be set to a primary key. Since I assume this isn't in error, I made a primary key that is the composite of hobby and id as this would be unique.
The documentation for runTransactionAsync is here:
https://cloud.google.com/nodejs/docs/reference/spanner/3.1.x/Database.html#runTransactionAsync
const {Spanner} = require('#google-cloud/spanner');
const spanner = new Spanner();
const instance = spanner.instance('testinstance');
const db = instance.database('people');
async function insert(data) {
const params = {
id: data.id,
name: data.name,
petName: null
}
const query = 'INSERT INTO my_table (id, name, petName, hobby) VALUES (#id, #name, #petName, #hobby)';
await Promise.all(data.hobby.map(async h => {
await db.runTransactionAsync(async (transaction) => {
params.hobby = h;
await transaction.run({
sql: query,
params: params,
types: {
petName: {
type: 'string'
},
}
});
await transaction.commit();
});
}));
};
insert({id: 0, name: 'foo', hobby:['a','b','c'], extra: 'blah'}).catch((r) => console.log(r))
Here I validate if my users status is true, and if they are, I put them in an array. The thing here is that next time it will validate, all those who already was true will be added to the same array. Can it be solved by filter instead of push, or should I take the validation in any other way?
import {
UPDATE_LIST_SUCCESS
} from './types'
var arr = []
export const fetchList = () => {
return (dispatch) => {
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
snapshot.forEach(function (child) {
var data = child.val()
if (child.val().profile.status === true) {
arr.push(data)
}
})
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
You can do it like this:
import {
UPDATE_LIST_SUCCESS
} from './types'
export const fetchList = () => {
return (dispatch) => {
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
var arr = snapshot.filter(function (child) {
return child.val().profile.status === true
}).map(function (child) {
return child.val();
});
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
So here is my not so pretty way of solving it, but it works.
import {firebaseRef} from '../firebase/firebase'
import {
UPDATE_LIST_SUCCESS
} from './types'
export const fetchList = () => {
return (dispatch) => {
const arrayToFilter = []
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
let snap = snapshot.val()
// Get acces to the keys in the object i got from firebase
let keys = Object.keys(snap)
// iterate the keys and put them in an User object
for (var i = 0; i < keys.length; i++) {
let k = keys[i]
let name = snap[k].profile.name
let age = snap[k].profile.age
let status = snap[k].profile.status
let profile_picture = snap[k].profile.profile_picture
let users = {name: '', age: '', status: Boolean, profile_picture: ''}
users.name = name
users.age = age
users.status = status
users.profile_picture = profile_picture
// adding the user object to an array
arrayToFilter.push(users)
}
// filter and creates a new array with users depending if their status is true
let arr = arrayToFilter.filter(child => child.status === true)
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}