How to modify an object regarless of inexistence value - javascript

I'm currently learning Graphql and I'm messing around with it,
I've this mutation resolver where links is just an array of objects links = [{ ... }]
Mutation : {
update(parent, args) => {
links[args.id] = {
description: args.description,
url: args.url,
}
}
}
In this current configuration if i update the id=0 which contains the following:
{
id: 0,
description: "test",
url: "www.test.com",
}
The problem would be that if in the argsparameter there is no description, then its value would be wiped out:
mutation {
update(id:0, url:"hello.com") {
url
}
}
results in
{
id: 0,
url: "hello.com"
}
Desired Behavior would be that if one of the two fields is not declared that would not affect its current value
What i tried:
Mutation : {
update(parent, args) => {
links[args.id] = {
description: (!!args.description ? void(0) : args.description ), // Check if description is null; if so, do nothing, else update the value
url: args.url,
}
}
}
I've to clarify that, assigning undefined to description is not what i want, I'm looking for a way to implement this using a inline if, and avoid idempotency, to keep things clean, i don't want something like this:
if (!!args.description)
...
else
...

Use the spread operator:
const links = [{
id: 0,
description: "test",
url: "www.test.com",
}]
function update({id, ...args}) {
links[id] = { ...links[id], ...args }
}
update({id: 0, url: 'www.wikipedia.org'})
console.log(links)

Related

Setting the property of one specific key in an object in a reduce is setting it for all properties

Okay, I have literally no idea whats going on here. I'm assuming its some kind of reference issue? But I dont know how to get around it or whats causing it.
To sum it up, I have a list of objects, as well as an object that gets prepopulated to make sure I have data for all keys in the object.
I need to iterate over this list of objects, and by using the timeframeId in the metadata object, and the id in the data object, I want to assign the entire data object to the corresponding timeframeId and id hierarchy in the prepopulated object.
For some reason, all data properties are being overwritten to whatever the last row data is.
I've linked a repl so you can see for yourself: https://repl.it/#ThomasVermeers1/UnwrittenNoisyFirm#index.js
But my code is as follows:
const buildSegmentsFromRows = (rows, timeframeMetadata, defaultSegmentData) => {
// Prepopulate object to make sure every timeframe has a 'hello' key in it with some data
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
segments: defaultSegmentData,
};
return segmentMap;
}, {});
// Now simply just loop over the rows, and set [row.metadata.timeframeId].segments[row.data.id] to row.data
const segments = rows.reduce((partialSegments, row) => {
const { timeframeId } = row.metadata;
const { id } = row.data;
/**
* This is the line where everything goes wrong
*/
partialSegments[timeframeId].segments[id] = row.data;
return partialSegments;
}, emptySegments);
return segments;
};
const rows = [
{
metadata: { timeframeId: '20202_01' },
data: {
'id': 'hello', 'value': 15
}
},
{
metadata: { timeframeId: '20202_02' },
data: {
'id': 'hello', 'value': 10
}
}
]
const timeframemetadata = [
{ timeframeId: '20202_01'},
{ timeframeId: '20202_02'}
]
const defaultSegmentData = {
'hello': {
'id': 'hello',
}
}
console.log(JSON.stringify(buildSegmentsFromRows(rows, timeframemetadata, defaultSegmentData), null, 2))
I'm expecting the end result to be:
{
"20202_01": {
"metadata": {
"timeframeId": "20202_01"
},
"segments": {
"hello": {
"id": "hello",
"value": 15
}
}
},
"20202_02": {
"metadata": {
"timeframeId": "20202_02"
},
"segments": {
"hello": {
"id": "hello",
"value": 10
}
}
}
}
But instead, value is getting set to 10 in all instances. I'm thinking its because we're setting the property to row.data, which is a reference, and gets updated on every call? But I'm at a complete loss here.
The problem is that you are referring to the same object for every segments in the list.
Therefore, changing the value of segments[id] will update defaultSegmentData, causing every reference to defaultSegmentData to change as well.
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
segments: defaultSegmentData, // Everything goes wrong here.
};
return segmentMap;
}, {});
A simple solution to this problem is to avoid using the same reference to the object when creating the segmentMap:
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
/** Or whatever default value you want.
* Just make sure to create a new instance of it for each call.
*/
segments: {},
};
return segmentMap;
}, {});

How is it possible to increase value for dynamic property in mongoDB

I'm trying to icrease a dynamic value on my database which depends on user's mark, lets say there is a "vote" object with 3 parameters:
//data structure :
_id : someObjectId,
gameId : someObjectId,
votes : {
a: 0,
b: 0,
c: 0
}
userVote = { vote: 'b', gameId: '5cf3c5cc1c9d44000053defb' }
function addVote(userVote) {
userVote.gameId = new ObjectId(userVote.gameId)
return mongoService.connect()
.then(db => {
const collection = db.collection('totalVotes');
return collection.updateOne(
{ gameId: userVote.gameId },
{ $inc: { "votes[userVote[vote]":1 } }
)
})
}
So, of course, the "inc" line isn't working that way, how is it possible to do this?
Thanks.
MongoDB has no way of knowing what votes[userVote[vote] even means (syntax error aside). It has no access to your client-side JavaScript. If you want to set this dynamically, change your $inc line to the following:
{ $inc: { ["votes." + userVote.vote] : 1 } }
If the bracket notation above is not supported, you can try something like the following instead:
let incVote = {};
incVote["votes." + userVote.vote] = 1;
return collection.updateOne(
{ gameId: userVote.gameId },
{ $inc: incVote }
);

GraphQL - passing an object of non specific objects as an argument

I am very new to GraphQL. I'm trying to pass an object like this one as an argument:
{
filters: {
status: 'approved',
id: {
LESS_THAN: 200
}
}
}
Or this object can be like this either;
{
filters: {
status: ['approved', 'pending'],
id: 200
}
}
I know all properties that can be in this object, but all of these properties can be a string/int or an object.
I tried to define it like this but it obviously didn't work:
args: {
filters: { type: new GraphQLNonNull(new GraphQLNonNull(GraphQLString)) },
},
I'm trying to define the argument with a GraphQL type GraphQLInputObjectType.
const OffersFiltersType = new GraphQLInputObjectType({
name: 'Filters',
description: '...',
fields: () => ({})
id: {
type: new GraphQLNonNull({
name: 'Id',
description: '...',
fields: {
}
}),
resolve: (offer) => offer.id
},
}),
});
But how can i specify to this type that my id can be either a int or an object?
This is my Query definition:
const QueryType = new GraphQLObjectType({
name: 'Query',
description: '...',
fields: () => ({
offers: {
type: OffersType,
args: {
limit: { type: GraphQLInt },
page: { type: GraphQLInt },
sort: { type: GraphQLString },
filters: { [HERE] }
},
resolve: (root, args, context, info) => {
const gqlFields = graphqlFields(info);
const fields = Object.keys(gqlFields.offer);
const queryArgs = args;
queryArgs.fields = fields;
return getOffers(queryArgs);
}
},
}),
});
And this is my request with superagent
const getOffers = (args) => {
const queryArgs = args;
if (typeof queryArgs.limit !== 'undefined') {
queryArgs.limit = args.limit;
} else {
queryArgs.limit = Number.MAX_SAFE_INTEGER;
}
return new Promise((fulfill, reject) => {
request
.get(API_URL)
.query(qs.stringify(args))
.end((err, res) => {
if (err) {
reject(err);
}
fulfill(res);
});
});
};
I need this object to construct a query in my resolve function. Thank you all for your help! I only need simple advices!
This is not allowed, by design: https://github.com/graphql/graphql-js/issues/303
GraphQL does not support unknown property names, largely because it would make the schema meaningless. The example given is a simple typo:
If you have the query query ($foo: String) { field(arg: $foo) } and the variables { "fooo": "abc" }, we currently flag this as an error, but we could potentially miss this typo if we did not raise errors.
The schema is meant to ensure compatibility between servers and clients, even across versions, and allowing unknown properties would break that.
There is a merge request open for this in the GraphQL-JS repo, but it is still being debated and has the same problems with typos and general inconsistency.
The idea of returning a primitive or object runs into a similar problem. When accepting an object, you need to list the properties you're expecting and the query will validate those against the schema. The properties, and their types and null-ness, must be known ahead of time for you (and the parser) to build the query and definitely need to be known when you validate.
If you could accept a primitive or object, you would have to specify the fields on that object, but those could not possibly exist on the primitive. That's a problem.

Change property in array with Spread Operator returns object instead of array

I want to change the property of an object similar to this, this is a simplified object with a few properties of the original:
state = {
pivotComuns: [
{
id: 1,
enabled : true
},
{
id: 2,
enabled : true
}
],
otherProperties : "otherProperties"
}
I'm changing the state of enabled like this:
state = {
...state,
pivotColumns: {
...state.pivotColumns,
[2]: {
...state.pivotColumns[2], enabled: !state.pivotColumns[2].enabled
}
}
}
It works, but instead of return an array like I is the pivotComuns property it returns an object, "notice that I change [] for {}":
state = {
pivotComuns: {
{
id: 1
enabled : true
},
{
id: 2,
enabled : true
}
},
otherProperties : "otherProperties"
}
What I'm doing wrong, I need to keep that property an array.
Very late post, but for future reference, you could do the following:
state = {
...state,
pivotColumns: state.pivotColumns.map(pc =>
pc.id === 2 ? {...pc, enabled:!pc.enabled} : pc
)
}
The advantage is that you will not change the object referenced in the "old array", you will instead insert a new object in its place. So if you would like to go back and forth in the state you can now do so.
example:
https://codepen.io/anon/pen/JyXqRe?editors=1111
I don't believe you can use the spread operator in such a way and in fact wouldn't recommend it if you could because it creates very hard to read code. There is a much simpler solution that I use on a daily basis when it comes to updating a key/value on an object where the value is an array:
var state = {
pivotColumns: [
{
id: 1,
enabled : true
}, {
id: 2,
enabled : true
}
],
otherProperties : "otherProperties"
}
var clonedPivotColumns = state.pivotColumns.slice();
clonedPivotColumns[1].enabled = !state.pivotColumns[1].enabled;
state = {
...state,
pivotColumns: clonedPivotColumns
}
this will get you the right results and will not cause any mutations.
working pen
http://codepen.io/finalfreq/pen/ggdJgQ?editors=1111

mongoose findOneAndUpdate appends objects only

I have an object like follows;
var exObj = { 'title' : 'name1'};
some of them have a data property of objects (not an array as I want to reference by name) and look like
exObj = {
title : 'name2',
data : {
prop1 : 'prop1',
prop2 : 'prop2'
}
}
Now I want to add another property to data, sometimes the data property will exist, and sometimes not, but I want to append a property (addedProp) and save it so I will end up with this;
exObj = {
title : 'name2',
data : {
prop1 : 'prop1',
prop2 : 'prop2',
addedProp : 'value'
}
}
When using findOneAndUpdate I can only seem to pass in a whole object which is then appended; something like this is what I currently do.
var data = {};
data.addedProp = value;
Collection.findOneAndUpdate({
title: 'name2'
}, {
data
}, {
upsert: true
})
.exec(function(err) {
if (err) {
console.log(err);
} else {
console.log('Updated');
}
});
But obviously this overides the data object that exists; what is the correct way to do a findOneAndUpdate and make more meaningful changes to the object? I tried casting to toObject() but then I don't have a proper mongoose object to do a .save() on.
To add further clarification; which this worked for simple properties (and worked well) which I know are set; there are some values I wish to add which I need to check if they have a value for the property before adding the property.
So something like this;
Collection.findOneAndUpdate({
field: val
}, {
if (tObj.title) {
title = tObj.title;
}
if (tObj.date) {
release_date = tObj.date;
}
if (tObj.name) {
name = tObj.name;
}
}, { upsert: true
})
.exec(function(err) {
if (err) {
//handler
} else {
//handler
}
});
Your question first seem daunting, but solution is quite simple, you don't need to use $ or upsert, because you are not using any array , so don't need of positional operator or upsert. You can use below code.
Collection.findOneAndUpdate({
title: 'name2'
},{
$set:{"data.prop3":"new prop3"}
})
.exec(function(err) {
if (err) {
console.log(err);
} else {
console.log('Updated');
}
});
It will add prop3 if not exists, or if it exists it will update it. I have checked the code on my local db with update and findOneAndUpdate
You need to use dot notation to target specific fields within an embedded object, building up your update object based on the supplied fields:
var update = {};
if (tObj.title) {
update.title = tObj.title;
}
if (tObj.data) {
if (tObj.data.prop1) {
update['data.prop1'] = tObj.data.prop1;
}
if (tObj.data.prop2) {
update['data.prop2'] = tObj.data.prop2;
}
if (tObj.data.addedProp) {
update['data.addedProp'] = tObj.data.addedProp;
}
}
Collection.findOneAndUpdate({
title: 'name2'
}, {
$set: update
}, {
upsert: true
})
.exec(function(err) {

Categories