How to add new field to mongodb schema? - reactjs

I have a react application that performs CRUD operations on data stored in mongodb in the cloud.mongodb.com.
The schema of the data in my react looks like this:
const restaurantSchema = new Schema({
"uuid": {
"type": "string"
},
"name": {
"type": "string"
},
"city": {
"type": "string"
}
}, {timestamps: true});
I would like to add a new field called "preference" of type number.
My questions are:
How do I add this new field of "preference"?
Can I give it a default value of say 1 when I add this new field? (There are 900 entries in the mongodb.)?
Can I give the "preference" value based on the order of the "name" field in ascending order?
thanks.

You can add and remove fields in the schema using option { strict: false }
option: strict
The strict option, (enabled by default), ensures that values passed to our model constructor that were not specified in our schema do not get saved to the db.
var thingSchema = new Schema({..}, { strict: false });
And also you can do this in update query as well
Model.findOneAndUpdate(
query, //filter
update, //data to update
{ //options
returnNewDocument: true,
new: true,
strict: false
}
)
You can check the documentation here

Related

React - setState doesn't refresh component as expected

I might be experiencing either a bug, or I misunderstand something about general javascript syntax.
Using ServiceNow UI Builder, I'm trying to refresh the datasource of a specific data visualization component. Which requires me to use setState and send in an entire JSON blob.
The following works as expected:
api.setState('intAssignedDonut', {
"header": "Interactions assigned to one of my groups",
"datasource": [{
"isDatabaseView": false,
"allowRealTime": true,
"sourceType": "table",
"label": {
"message": "Interaction"
},
"tableOrViewName": "interaction",
"filterQuery": "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744",
"id": "intAssigned",
"elm": {}
}],
"metric": [{
"dataSource": "intAssigned",
"id": "intAssignedMetric",
"aggregateFunction": "COUNT",
"numberFormat": {
"customFormat": false
},
"axisId": "primary"
}],
"groupBy": [{
"maxNumberOfGroups": "ALL",
"numberOfGroupsBasedOn": "NO_OF_GROUP_BASED_ON_PER_METRIC",
"showOthers": false,
"groupBy": [{
"dataSource": "intAssigned",
"groupByField": "state",
"isRange": false,
"isPaBucket": false
}]
}]
});
However, I only need to alter a few properties, not the whole thing.
So I thought I'd just clone the thing into a temp object, change what I need, then pass the cloned object back.
let clientState_intAssignedDonut = api.state.intAssignedDonut;
clientState_intAssignedDonut.header = 'Interactions assigned to one of my groups';
clientState_intAssignedDonut.datasource[0].filterQuery = 'active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744';
api.setState("intAssignedDonut", clientState_intAssignedDonut);
This seems to update the header properly, but the component doesn't refresh the datasource.
Even if I console.log api.state.intAssignedDonut it looks identical to the whole JSON blob.
EDIT: I also tried using spread operators, but I can't figure out how to target the datasource[0]
api.setState("intAssignedDonut", {
...api.state.intAssignedDonut,
header: "Interactions assigned to one of my groups",
datasource[0].filterQuery: "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744"
});
Javascript objects are passed by reference values, and react state is immutable:
let clientState_intAssignedDonut = api.state.intAssignedDonut;
api.setState("intAssignedDonut", clientState_intAssignedDonut);
This is mutating the state directly, and React will ignore your update if the next state is equal to the previous state, which is determined by an Object.is comparison to check if both objects are of the same value, see docs
Your second attempt is heading to the right direction using spread operator:
Update method one: first copy the nested object using: JSON.parse(JSON.stringify(object)), or you can find other method in this question: What is the most efficient way to deep clone an object in JavaScript?
let copied = JSON.parse(JSON.stringify(api.state.intAssignedDonut)); // copy a nested object
copied.header = "Interactions assigned to one of my groups";
copied.datasource[0].filterQuery = "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744";
setState("intAssignedDonut",copied);
Update method two:
setState("intAssignedDonut",{
...api.state.intAssignedDonut,
header: "Interactions assigned to one of my groups",
datasource: [{ ...state.datasource[0], filterQuery: "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744" }]
});
Check out sandbox

Delete many items from array of documents in mongodb that match this value

I am trying to delete all documents from an array of documents that match this value but cannot figure it out,
This is my delete query that is not working, courses is the array I need to be in and code is the document I need to check to see its value for deletetion. So if courses.code == 123, then delete and keep going sort of thing.
result = await mongoClient.db(DB_NAME).collection("technologies").deleteMany({ 'courses': {$in: 'courses.code'} });
That is what it looks like in my collection.
"name": "Sass",
"description": "Sass (Syntactically awesome style sheets) is a preprocessor scripting language that is interpreted or compiled into Cascading Style Sheets (CSS).",
"difficulty":2,
"courses":[
{"code":"PROG2700","name":"Client Side Programming"},
{"code":"PROG3017","name":"Full Stack Programming"}
]
},
Any help would be great, thanks!
You can filter documents that contain the field you want to update
Do tests before updates on the original collection.
After some searches, I've found this method:
const filter = { }
db.collection.update(filter,
{
$pull: {
"courses": {
code: {
$in: [
"PROG3017",
"PROG2700"
]
}
}
}
},
{
multi: true
})
This pulls out any element in the array where the value of code is $in the array.

Mongoose - Remove several objects from an array (not exact match)

I have a collection Playlist that contains an array of items
{
userId: {
type : String,
required : true,
index : true,
unique : true
},
items: [
{
id: { // do not mix up with _id, which is the autogenerated id of the pair {id,type}. ID is itemId
type : Schema.Types.ObjectId
},
type: {
type : String
}
}
]
}
Mongo automatically adds the _id field to the items when I push a pair {id,type} to items (but I don't care about it).
Now I would like to remove several "pairs" at once from the items array.
I have tried using $pullAll but it requires an exact match, and I do not know the _id, so it does not remove anything from items
playlistModel.update({userId:userId},{$pullAll:{items:[{id:"123",type:"video"},{id:"456",type:"video"}]}},null,function(err){
I have tried using $pull with different variants, but it removed ALL objects from items
playlistModel.update({userId:userId},{$pull:{items:{"items.id":{$in:["123","456"]}}}},null,function(err){
playlistModel.update({userId:userId},{$pull:{items:{$in:[{id:"123",type:"video"},{id:"456",type:"video"}]}}},null,function(err){
Am I missing something or am I asking something that isn't implemented?
If the latter, is there a way I can go around that _id issue?
OK I found a way that works using $pull:
playlistModel.update({userId:userId},{$pull:{items:{id:{$in:["123","456"]}}}},null,function(err){
It doesn't take the type into account but I can't see any issue with that since the id is unique across all types anyway.
Although I will wait a bit to see if someone has a better solution to offer
EDIT
With Veeram's help I got to this other solution, which IMO is more elegant because I don't have _ids that I don't need in the database, and the $pullAll option seems more correct here
var playlistItemSchema = mongoose.Schema({
id: { // do not mix up with autogenerated _id. id is itemId
type : Schema.Types.ObjectId
},
type: {
type : String
}
},{ _id : false });
var schema = new Schema({
userId: {
type : String,
required : true,
index : true,
unique : true
},
items: [playlistItemSchema]
});
playlistModel.update({userId:userId},{$pullAll:{items:[{id:"123",type:"video"},{id:"456",type:"video"}]}},null,function(err){
tips:
you can use _id field to handle your playlistModel data.
mongoose api : new mongoose.Types.ObjectId to generate an Object_id
let _id=new mongoose.Types.ObjectId;
playlistModel.updateMany({_id:_id},{ $set: { name: 'bob' }}).exec(data=>{console.log('exec OK')});

Custom data unwrapping in ampersand.js model

I have a model - Configuration:
var Configuration = Model.extend({
props: {
name: 'string'
}
});
In the database, configuration model / table has 3 columns -> id, name and fields. The latter stores site config as a serialized array. When retrieving the entry from the database, I unserialize it and then pass it to the front end, so the front end receives this:
{
"id": 1,
"name": 'global',
"fields": {
"enabled": true,
"site_name": "Test"
}
};
What I want to do is to set whatever is inside fields object as properties on my model, or maybe session so that things get triggered throughout the site when they are updated. To visualize it, I want to achieve something like this:
var Configuration = Model.extend({
props: {
enabled: 'boolean',
site_name: 'string'
}
});
So basically, is there are a way to 'unwrap' stuff in fields object somehow?
The parse method is what you're looking for in this case. See https://github.com/AmpersandJS/ampersand-state/blob/master/ampersand-state.js#L93-L98 It allows you to transform incoming props.

I try to implement a connection using relay and all the node's IDs are the same

I write a really simple schema using graphql, but some how all the IDs in the edges are the same.
{
"data": {
"imageList": {
"id": "SW1hZ2VMaXN0Og==",
"images": {
"edges": [
{
"node": {
"id": "SW1hZ2U6",
"url": "1.jpg"
}
},
{
"node": {
"id": "SW1hZ2U6",
"url": "2.jpg"
}
},
{
"node": {
"id": "SW1hZ2U6",
"url": "3.jpg"
}
}
]
}
}
}
}
I posted the specific detail on github here's the link.
So, globalIdField expects your object to have a field named 'id'. It then takes the string you pass to globalIdField and adds a ':' and your object's id to create its globally unique id.
If you object doesn't have a field called exactly 'id', then it wont append it, and all your globalIdField will just be the string you pass in and ':'. So they wont be unique, they will all be the same.
There is a second parameter you can pass to globalIdField which is a function that gets your object and returns an id for globalIdField to use. So lets say your object's id field is actually called '_id' (thanks Mongo!). You would call globalIdField like so:
id: globalIdField('Image', image => image._id)
There you go. Unique IDs for Relay to enjoy.
Here is the link to the relevant source-code in graphql-relay-js: https://github.com/graphql/graphql-relay-js/blob/master/src/node/node.js#L110
paste the following code in browser console
atob('SW1hZ2U6')
you will find that the value of id is "Image:".
it means all id property of records fetched by (new MyImages()).getAll()
is null.
return union ids or I suggest you define images as GraphQLList
var ImageListType = new GraphQL.GraphQLObjectType({
name: 'ImageList',
description: 'A list of images',
fields: () => ({
id: Relay.globalIdField('ImageList'),
images: {
type: new GraphQLList(ImageType),
description: 'A collection of images',
args: Relay.connectionArgs,
resolve: (_, args) => Relay.connectionFromPromisedArray(
(new MyImages()).getAll(),
args
),
},
}),
interfaces: [nodeDefinition.nodeInterface],
});

Resources