Set Mongoose model instance properties with "extend" util - javascript

I have Mongoose model instance:
var s = new Song({
song_id: '123',
title: 'sample song',
artist: {
name: 'sample artist',
id: '456'
}
});
Now I'd like to set/update its properties but using extend (e.g. from nodejs util._extend)
s = extend(s, {
title: 'changed title',
artist: {
name: 'changed artist',
id: '789'
}
});
s.save();
And while title (as a top-level property) gets set ok, changes in artist are not visible.
I know I can just set it via:
s.artist.name = 'changed artist';
but is there something I'm missing or is this way of updating properties not usable?
EDIT
Gah... Looks like someone defined schema the wrong way. Instead of artist field defined as
artist: {
name: String,
id: String
}
it was defined like
artist.name: String,
artist.id: String
When I redefined it it works now. Thanks

What you can do is to use Mongoose's update method:
Song.update({
song_id: '123',
}, {
$set: {
title: 'changed title',
artist: {
name: 'changed artist',
id: '789',
},
},
}, function(err, numAffected) {
...
});
However, I don't actually see why your extend attempt failed. It seems to work for me, I usually use underscore's _.extend function.

Related

React-Redux - How to update the Id

I have the following state:
users: {
'user-0': {
id: 'user-0',
title: 'Bob'
},
'user-1': {
id: 'user-1',
title: 'John'
},
...
I would like to change user-1's id by user-12 for example:
users: {
'user-0': {
id: 'user-0',
title: 'Bob'
},
'user-12': {
id: 'user-12',
title: 'John'
},
...
What is the easiest way to do that with the reducer ?
I was thinking about copying the user-1 data to a new entry by changing the ids, but is there a better way?
Without mutating data:
let data = {
users : {
'user-0': {
id: 'user-0',
title: 'Bob'
},
'user-1': {
id: 'user-1',
title: 'John'
},
'user-2': {
id: 'user-2',
title: 'Bohn'
}
}
}
let dataCopy = {
...data,
'users' :{
...data.users,
'user-12' : {
...data.users['user-1'],
id: 'user-12'
}
}
}
delete dataCopy.users['user-1'];
You need to make a deep copy of your object and only change that copy's properties. Do not mutate the original object. I am using spread operator and delete
Remember this is assuming your users follow the same structure.
First Copy user-1 data and add a new key user-12 and set its value as user-1. then update the id property of user-12 and remove user-1 key from the state.
users["user-12"] = {...users["user-1"], id:"user-12"};
// remove user-1 key from users state
delete users["user-1"]

write json to realmdb schema

I need to save a json in a property of my schema, my schema:
export default class ListSchema {
static schema = {
name: 'Listas',
primaryKey: 'id',
properties: {
id: 'string',
name: 'string',
contents: 'string', // I need to save here, currently I left string
}
}
}
code that saves the list:
async function SaveList() {
const data = {
id: String(Date.now()),
name: List,
contents: JSON.stringify(AllTask) // I need to save json here
}
const realm = await getRealm()
realm.write(() => {
realm.create('Listas', data)
})
}
my json n has a definite size, and it goes something like this:
[{id: 'value', name:'value'}, {id: 'value', name:'value'} ...]
what should my schema look like?
I'm using Realmdb in production with React Native and had the same doubt some time ago.
Basically you have two ways to do it.
The first and the more simple is convert your schema using JSON.stringify and save it in your field with type string, (Like you did) but when you wish get this value again, you will to convert again using JSON.parse, is boring but works well you have just to remeber.
The second option is create another schema and use it to insert values with object format.
const CarSchema = {
name: 'Car',
properties: {
make: 'string',
model: 'string',
miles: {type: 'int', default: 0},
}
};
const PersonSchema = {
name: 'Person',
properties: {
name: 'string',
cars: {type: 'list', objectType: 'Car'},
}
};
Above, we have two schema and if we insert a new data we have to do something like this.
realm.write(() => {
realm.create('Person', {
name: 'Joe',
// nested objects are created recursively
car: {make: 'Honda', model: 'Accord', drive: 'awd'},
});
});
This way is more eficient but is more hard to maintain the code.
Today we use the option 1, because is more simple and just one schema.
I hope that it help you.

Using ACF with GraphQL and Gatsby, flexible content is not returning the order of it's child blocks

I have a Gatsby site that is hooked up to use Wordpress as a headless CMS. I am using ACF to create dynamic layouts in Wordpress then rendering the modules I add to page to the frontend(Gatsby). I am using the flexible content feature in ACF to dynamically create layouts in the cms that can be easily rendered in React. Normally I do this with next.js and Wordpress's REST API which returns the "flexible content" data as an array of objects I can easily iterate over and render the appropriate content with.
My issue is that with using GraphQL and Gatsby, my "flexible content" data I get it back from the API is just a bunch of objects within the parent page's object body. This means there is no relationship to what order the modules/children were placed in the flexible content block since they aren't part of an array. My workaround, for the time being, is to add an extra field to the child modules that specify its' order on the page based on a number value.....but this is gross and would be a horrible experience for a client so I am not happy with that.
Is there a way to either return a value for each item in an AFC flexible content block that directly relates to its index/position. Or is there a way to return the children items in an array when using the Gatsby plugin for returning data.
Currently, my query looks a little like this:
allWordpressPage {
edges {
node {
id
slug
status
template
title
childWordPressAcfFeaturedShowcaseCarousel {
id
title
other_stuff
}
childWordPressAcfIphonesAndContent {
id
title
other_stuff
}
childWordPressAcfContentAndImageSlideShow {
id
title
other_stuff
}
}
}
}
That will return something like:
{
id: 123,
slug: 'hello',
template: 'default',
title: 'Help Me',
childWordPressAcfFeaturedShowcaseCarousel: {
id: 1232
title: 'Bonjour'
other_stuff: {....}
},
childWordPressAcfIphonesAndContent: {
id: 1232
title: 'Bonjour'
other_stuff: {....}
},
childWordPressAcfContentAndImageSlideShow: {
id: 1232
title: 'Bonjour'
other_stuff: {....}
}
}
But instead, I would want something like:
{
id: 123,
slug: 'hello',
template: 'default',
title: 'Help Me',
childWordPressAcfFeaturedShowcaseCarousel: {
id: 1232,
index: 1,
title: 'Bonjour',
other_stuff: {....}
},
childWordPressAcfIphonesAndContent: {
id: 1232,
index: 2,
title: 'Bonjour',
other_stuff: {....}
},
childWordPressAcfContentAndImageSlideShow: {
id: 1232,
index: 3,
title: 'Bonjour'
other_stuff: {....}
}
}
Or even better:
{
id: 123,
slug: 'hello',
template: 'default',
title: 'Help Me',
module: [
childWordPressAcfFeaturedShowcaseCarousel: {
id: 1232,
title: 'Bonjour',
other_stuff: {....}
},
childWordPressAcfIphonesAndContent: {
id: 1232,
title: 'Bonjour',
other_stuff: {....}
},
childWordPressAcfContentAndImageSlideShow: {
id: 1232,
title: 'Bonjour'
other_stuff: {....}
}
]
}
So I just figured it out. Turns out when querying data for a flexible content block there are a few things to remember. To access flexible content fields, instead of using their field name, you need to use [field_name]_[post_type] (if you have field named page_builder in your WordPress pages you would need to use page_builder_page). Once you do that everything will return in an array in the exact order they are in the ACF block.
So now my query looks like this:
allWordpressPage {
edges {
node {
id
slug
status
template
title
acf {
modules_page {
... on WordPressAcf_featured_showcase_carousel {
__typename
id
title
other_stuff
}
... on WordPressAcf_iphones_and_content {
__typename
id
title
other_stuff
}
... on WordPressAcf_content_and_image_slide_show {
__typename
id
title
other_stuff
}
}
}
}
}
}

How to count number of ObjectId occurrences in an array of ObjectIds' and attach the result to each document that is being returned?

I have a Tag schema:
const TagSchema = new Schema({
label: {type: String, unique: true, minlength: 2},
updatedAt: {type: Date, default: null},
createdAt: {type: Date, default: new Date(), required: true},
createdBy: {type: mongoose.Schema.Types.ObjectId, ref: 'Account', default: null}
});
const Tag = mongoose.model('Tag', TagSchema);
Then I have a Page schema:
const PageSchema = new Schema({
tags: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'Tag'}], default: [], maxlength: 5}
});
const Page = mongoose.model('Page', PageSchema);
As you can see it contains a Tag reference in its tags array.
Now what I need to do is when I fetch all tags via /tags, I also need to count the number of times each tag is used in all Pages.
So if a Tag is used 2times across all Pages, it should set an .occurrences property on the returned tag. For example this would be a response from /tags:
[
{_id: '192398dsf7asdsdds8f7d', label: 'tag 1', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 2},
{_id: '19239asdasd8dsf7ds8f7d', label: 'tag 2', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 1},
{_id: '192398dsf7zxccxads8f7d', label: 'tag 1', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 5},
]
I would have imagined that I could achieve this pretty easily in a mongoose pre('find') hook like this:
TagSchema.pre('find', function() {
Page.count({tags: this._id}, (err, total) => {
this.occurrences = total;
});
});
However, there are two problems here:
Page.count throws an error saying it's not a function, which I don't understand why because I use it somewhere else perfectly fine. And Page has been imported properly. Suggesting that you can't use count in a hook or something similar.
this is not the Tag document.
So I guess that the way I am going about this is completely wrong.
Since I am a novice in MongoDB maybe someone can provide me with a better, working, solution to my problem?
db.Page.aggregate([
{
$unwind:"$tags"
},
{
$group:{
_id:"$tags",
occurrences:{$sum:1}
}
},
{
$lookup:{ //get data for each tag
from:"tags",
localField:"_id",
foreignField:"_id",
as:"tagsData"
}
},
{
$project:{
tagData:{$arrayElemAt: [ "$tagsData", 0 ]},
occurrences:1
}
}
{
$addFields:{
"tagData._id":"$_id",
"tagData.occurrences":"$occurrences"
}
},
{
$replaceRoot: { newRoot: "$tagData" }
}
])
Ok first, you need getQuery() this allows you to access the properties that you are finding the tag with. so if you find the tag by _id, you will have access to it in the pre hook by this.getQuery()._id
Second, you are going to need to use the "Parallel" middleware, can be found in the docs. This allows the find method to wait until the done function is called, so it is gonna wait until the tag is updated with the new occurrences number.
As the object can no be accessed inn the pre hook as it is yet to be found, you are going to have to use findOneAndUpdate method in the pre hook.
So the code should look like :
The find method :
Tag.find({ _id: "foo"}, function (err, foundTag) {
console.log(foundTag)
})
The pre hook :
TagSchema.pre('find', true, function (next, done) {
mongoose.models['Page'].count({ tags: this.getQuery()._id }, (err, total) => {
mongoose.models['Tag'].findOneAndUpdate({ _id: this.getQuery()._id }, { occurrences: total }, function () {
next()
done()
})
});
});

Redux: local state id's and/or api uuid's

I'm using Redux with a REST api that uses UUID's. The usual pattern for storing state is using id's as a key for objects:
entities: {
articles: {
1: {
id: 1,
title: 'Some Article',
author: 1
},
2: {
id: 2,
title: 'Other Article',
author: 1
}
},
users: {
1: {
id: 1,
name: 'Dan'
}
}
}
How would I use the UUID's from the api in this? I'd like to be able to create a new entity without having to request the UUID from the server first (for offline capabilities).
Should I:
Use local id's, keep the UUID in a _id property of the entity, and only use it when making an API request? This seems the easiest way, although it feels redundant and I will probably have to search through entities for a certain _id in some cases.
entities: {
articles: {
1: {
_id: 'UUID',
title: 'Some Article',
author: 1
},
2: {
id: 'UUID',
title: 'Other Article',
author: 1
}
},
users: {
1: {
_id: 'UUID',
name: 'Dan'
}
}
}
Use only UUID's from the API, and when creating a new item use a sort if temporary id until the API call is resolved? This seems the best way, although I'm not sure how I would go about changing the id's, which also feels wrong (as they're id's).
entities: {
articles: {
'UUID': {
_id: 'UUID',
title: 'Some Article',
author: 'UUID'
},
'UUID': {
_id: 'UUID',
title: 'Other Article',
author: 'creating'
}
},
users: {
'UUID': {
_id: 'UUID',
name: 'Dan'
},
'creating': {
name: 'Stan'
}
}
}
Do it some other way?
I wouldn't add it to the Redux store until the API returns a response.
In Vue, in my data for a given component I usually have two (sometimes more) root keys, one of which points to the part of my Redux store that handles the data, and the other that is usually form or something of that sort, and the Vue data changes due to binding.
After the user initiates the action to add the resource (POST /resources), and the server returns a successful response, I dispatch an action addedResource. And prior to that I'd dispatch something like addingResource, etc.
Is there any reason this wouldn't work? There shouldn't be a difference using an auto incremented, integer id field vs. a UUID. Your data normalization should still work the same way.

Categories

Resources