mobx-state-tree: How to clone a model that includes references? - mobx

I'm trying to clone a model that references another model, but I get: Error: [mobx-state-tree] Failed to resolve reference 'H1qH2j20z' to type 'AnonymousModel' (from node: /usualCustomer)... in the clone. The original resolves okay.
Here are my models:
const Job = types.model({
id: types.optional(types.identifier(types.string), shortid.generate()),
jobNumber: types.optional(types.string, ''),
description: '',
usualCustomer: types.maybe(types.reference(Customer)),
})
const Customer = types.model({
id: types.optional(types.identifier(types.string), shortid.generate()),
name: types.optional(types.string, 'New customer'),
})
This function shows the problem:
editJob = job => {
console.log('Original', job)
var newClone = clone(job)
console.log('Clone', newClone)
}

Did you try also to change the identifier of the node you want to clone ?
i'm not sure if it will work. but when you clone the job node, i think the new created job newJob act as a reference to the cloned job, give it a try anyway :
let jobFromSnap = getSnapshot(job);
let newJob = Job.create({...jobFromSnap, id : "NEW_ID_HERE" }) ;

Related

VUEJS and Vuetify - Push the results from a localbase into a data table

How do i push my results into a data object please
I would like to push the :key to the id field and the :data to map across to my other data fields
my data object is called orgs: []
I have the following data returned from my get query
[{…}]
0:
data: {person: 'John', orgName: 'test', due: 'this is due'}
key: "11ed25ccf7548160bff2ab4c1d56ee40"
I have tried
this.$db.collection('orgs').get({ keys: true })
.then
(response => {
console.log('orgs: ', response) <--- this displays the results
this.orgs.forEach(response => {
const data = {
'person': response.data.person,
'orgName': response.data.orgName,
'due': response.data.due,
'id': response.key
}
this.orgs.push(data)
})
});
I get no errors but my this.orgs data item remains empty so assume I am not updating the array for some reason
Thanks
I seem to be able to populate it in a simple way as follows
this.$db.collection('orgs').get({ keys: true })
.then
(response => {
this.orgs = response
console.log('response:', this.orgs)
})
Is there any issue with this approach for just getting my results or should I be using push?

TypeORM cannot delete row with ManyToOne / OneToMany relation

I have this problem right now that I don't know how to fix honestly. I spent hours on this already and cannot find the solution. I am using MS-SQL on Azure.
The way I have set up my entities is the following:
Customer and Visits: OneToMany (Primary)
Visits and Customers: ManyToOne (Inverse)
I am soft-deleting my customers, so that the information for the visits can be retrieved regardless of whether or not the user wants to see the customer data specifically. The data is still getting resolved correctly using the relationship. That's also why I don't want to use "Cascade DELETE" here.
However, since I want to delete visits completely (not soft-deleting like the customers) I am facing issues probably regarding foreign key constraints (not sure, because I don't get any error output from TypeORM). The DeleteResult.affected property however returns 0, which is what I see in my DataGrip queries as well, where I check the actual table data.
Whats important as well is that I am able to manually delete the row using a simple SQL statement like the following:
DELETE FROM visits
WHERE uuid = 'f0ea300d-...-656a'
My entities are set up like this (left unimportant information out):
#Entity({ name: 'customers' })
export class Customer {
#PrimaryColumn()
uuid: string
#OneToMany(() => Visit, (visit) => visit.customer)
visits?: Visit[]
}
#Entity({ name: 'visits' })
export class Visit {
#PrimaryColumn()
uuid: string
#ManyToOne(() => Customer, (customer) => customer.visits)
customer: Customer
}
My GraphQL resolver:
#Mutation(() => Boolean)
async deleteVisitsByUuid(
#Arg('uuid') uuid: string,
#Ctx() { conn }: AppContext,
): Promise<boolean> {
const repo = conn.getRepository(Customer)
const result = await repo.delete(uuid)
const affected = result.affected
if (affected === undefined || affected == null) {
return false
} else {
return affected > 0
}
}
The problem was conn.getRepository(Customer). I have replaced it with conn.getRepository(Visit).

Use Ramda.js to pull off items from object

This question is about how to perform a task using RamdaJS.
First, assume I have an object with this structure:
let myObj = {
allItems: [
{
name: 'firstthing',
args: [
{
name: 'arg0'
},
{
name: 'arg1'
}
],
type: {
name: 'type_name_1'
}
},
{
name: 'otherthing',
args: [
{
name: 'arg0'
}
]
}
]
}
I am trying to create an object that looks like:
{
arg0: 'arg0', // myObj.allItems[0].args[0].name
typeName: 'type_name_1' // myObj.allItems[0].type.name
}
(I know the names are stupid, arg0, typeName. It's not important)
So if we weren't using Ramda, this is how I'd do it imperatively:
// The thing I'm searching for in the array (allItems)
let myName = 'firstthing';
// Here's how I'd find it in the array
let myMatch = myObj.allItems.find(item => item.name === myName);
// Here is the desired result, by manually using dot
// notation to access properties on the object (non-functional)
let myResult = {
arg0: myMatch.args[0].name,
typeName: myMatch.type.name
};
// Yields: {"arg0":"arg0","typeName":"type_name_1"}
console.log(myResult)
Finally, just for good measure, this is as far as I've gotten so far. Note that, I'd really like to accomplish this in a single compose/pipe.
(An object goes in, and an object with the desired data comes out)
const ramdaResult = R.compose(
R.path(['type', 'name']),
R.find(
R.propEq('name', myName)
)
)(R.prop('allItems', myObj))
Thanks
A combination of applySpec and path should work:
const transform = applySpec ({
arg0: path (['allItems', 0, 'args', 0, 'name']),
typeName: path (['allItems', 0, 'type', 'name'])
})
const myObj = {allItems: [{name: 'firstthing', args: [{name: 'arg0'}, {name: 'arg1'}], type: {name: 'type_name_1'}}, {name: 'otherthing', args: [{name: 'arg0'}]}]}
console .log (
transform (myObj)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {applySpec, path} = R </script>
But depending upon your preferences, a helper function might be useful to make a slightly simpler API:
const splitPath = useWith (path, [split('.'), identity] )
// or splitPath = curry ( (str, obj) => path (split ('.') (str), obj))
const transform = applySpec({
arg0: splitPath('allItems.0.args.0.name'),
typeName: splitPath('allItems.0.type.name'),
})
const myObj = {allItems: [{name: 'firstthing', args: [{name: 'arg0'}, {name: 'arg1'}], type: {name: 'type_name_1'}}, {name: 'otherthing', args: [{name: 'arg0'}]}]}
console .log (
transform (myObj)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {applySpec, path, useWith, split, identity} = R </script>
splitPath is not appropriate for Ramda, but it's a useful function I often include, especially if the paths are coming from a source outside my control.
Update
Yes, I did miss that requirement. Serves me right for looking only at the input and the requested output. There's always multiple incompatible algorithms that give the same result for a specific input. So here's my mea culpa, an attempt to break this into several reusable functions.
Lenses are probably your best bet for this. Ramda has a generic lens function, and specific ones for an object property (lensProp), for an array index(lensIndex), and for a deeper path(lensPath), but it does not include one to find a matching value in an array by id. It's not hard to make our own, though.
A lens is made by passing two functions to lens: a getter which takes the object and returns the corresponding value, and a setter which takes the new value and the object and returns an updated version of the object.
An important fact about lenses is that they compose, although for technical reasons the order in which you supply them feels opposite to what you might expect.
Here we write lensMatch which find or sets the value in the array where the value at a given path matches the supplied value. And we write applyLensSpec, which acts like applySpec but takes lenses in place of vanilla functions.
Using any lens, we have the view, set, and over functions which, respectively, get, set, and update the value. Here we only need view, so we could theoretically make a simpler version of lensMatch, but this could be a useful reusable function, so I keep it complete.
const lensMatch = (path) => (key) =>
lens
( find ( pathEq (path, key) )
, ( val
, arr
, idx = findIndex (pathEq (path, key), arr)
) =>
update (idx > -1 ? idx : length (arr), val, arr)
)
const applyLensSpec = (spec) => (obj) =>
map (lens => view (lens, obj), spec)
const lensName = (name) => lensMatch (['name']) (name)
const transform = (
name,
nameLens = compose(lensProp('allItems'), lensName(name))
) => applyLensSpec({
arg0: compose (nameLens, lensPath (['args', 0, 'name']) ),
typeName: compose (nameLens, lensPath (['type', 'name']) )
})
const myObj = {allItems: [{name: 'firstthing', args: [{name: 'arg0'}, {name: 'arg1'}], type: {name: 'type_name_1'}}, {name: 'otherthing', args: [{name: 'arg0'}]}]}
console .log (
transform ('firstthing') (myObj)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {lens, find, pathEq, findIndex, update, length, map, view, compose, lensProp, lensPath} = R </script>
While this may feel like more work than some other solutions, the main function, transform is pretty simple, and it's obvious how to extend it with additional behavior. And lensMatch and applyLensSpec are genuinely useful.

BookshelfJS - 'withRelated' through relational table returns empty results

I've been trying to structure the relations in my database for more efficient querying and joins but after following the guides for '.belongsToMany', '.through' and '.belongsTo' I'm now getting empty results.
I've got a Sound model and a Keyword model which I want to model with a many-to-many relationship (each Sound can have multiple Keywords, and each Keyword can be related to multiple sounds). Based on the documentation '.belongsToMany' would be the relation to use here.
I've set up my models as follows, using a 'sound_keyword' relational table/SoundKeyword relational model (where each entry has it's own unique 'id', a 'soundID', and a 'keywordID'):
var Sound = bookshelf.Model.extend({
tableName: 'sounds',
keywords: function () {
return this.belongsToMany(Keyword, 'sound_keyword', 'id', 'id').through(SoundKeyword, 'id', 'soundID');
},
});
var Keyword = bookshelf.Model.extend({
tableName: 'keywords',
sounds: function () {
return this.belongsToMany(Sound, 'sound_keyword', 'id', 'id').through(SoundKeyword, 'id', 'keywordID');
}
});
where:
var SoundKeyword = bookshelf.Model.extend({
tableName: 'sound_keyword',
sound: function () {
return this.belongsTo(Sound, 'soundID');
},
keyword: function () {
return this.belongsTo(Keyword, 'keywordID');
}
});
From what I've read in the docs and the BookshelfJS GitHub page the above seems to be correct. Despite this when I run the following query I'm getting an empty result set (the Sound in question is related to 3 Keywords in the DB):
var results = await Sound
.where('id', soundID)
.fetch({
withRelated: ['keywords']
})
.then((result) => {
console.log(JSON.stringify(result.related('keywords')));
})
Where am I going wrong with this? Are the relationships not set up correctly (Possibly wrong foreign keys?)? Am I fetching related models incorrectly?
Happy to provide the Knex setup as needed.
UPDATED EDIT:
I had been using the Model-Registry Plugin from the start and had forgotten about it. As it turns out, while the below syntax is correct, it prefers syntax similar to the following (i.e. lowercase 'model', dropping the '.extends' and putting model names in quotes):
var Sound = bookshelf.model('Sound',{
tableName: 'sounds',
keywords: function () {
return this.belongsToMany('Keyword', 'sound_keyword', 'soundID', 'keywordID');
},
});
var Keyword = bookshelf.model('Keyword',{
tableName: 'keywords',
sounds: function () {
return this.belongsToMany('Sound', 'sound_keyword', 'keywordID', 'soundID');
}
});
Hope this can be of help to others.
Seems like removing the '.through' relation and changing the IDs in the '.belongsToMany' call did the trick (as below), though I'm not entirely sure why (the docs seem to imply belongsToMany and .through work well together - possibly redundant?)
var Sound = bookshelf.Model.extend({
tableName: 'sounds',
keywords: function () {
return this.belongsToMany(Keyword, 'sound_keyword', 'soundID', 'keywordID');
},
});
var Keyword = bookshelf.Model.extend({
tableName: 'keywords',
sounds: function () {
return this.belongsToMany(Sound, 'sound_keyword', 'keywordID', 'soundID');
}
});
I did try my original code with soundID and keywordId instead of 'id' (as below), but without the .through relation and that gave the same empty results.

Finding the origin of Postgres/Sequelize duplicate alias error (code 42712)

I'm using Sequelize to manage by Postgres SQL database in a node.js project. I'm running into namespace errors but can't for the life of me figure out why.
The code that works:
// db variable holds initialized database connection
const link = name => ( {
through: db.define( name, {
amount: Sequelize.FLOAT,
unit: Sequelize.STRING,
dataSource: Sequelize.STRING
} ),
as: name + 's'
} )
// All model variables (Material, Component etc) have model names
// identical to their variable names (bit without capitals)
Material.belongsToMany( Material, link( 'materialchildmaterial' ) )
Component.belongsToMany( Component, link( 'componentchildcomponent' ) )
Input.belongsToMany( Input, link( 'inputchildinput' ) )
Component.belongsToMany( Component, link( 'componentchilddependency' ) )
The moment I add the following things break:
Input.belongsToMany( Component, link( 'componentchildinput' ) )
The error:
{ name: 'SequelizeDatabaseError',
parent:
{ name: 'error',
length: 177,
severity: 'ERROR',
code: '42712',
file: 'parse_relation.c',
line: '420',
routine: 'checkNameSpaceConflicts' },
original:
{ name: 'error',
length: 177,
severity: 'ERROR',
code: '42712',
file: 'parse_relation.c',
line: '420',
routine: 'checkNameSpaceConflicts' } }
From the postgres docs code 42712 stands for 'DUPLICATE ALIAS'.
My confusion: I am using unique names for everything (afaik). When I change the Input <--> Component link's name to 'randomthing' the error does not appear.
I've been stuck at this for a good day now. Any clues appreciated.