I've looked at the documentation and read through some issues related to this on the sequelize github repo but so far haven't found any solutions to this, IMHO, simple operation.
I have two models: Users and Cards. They are associated with a one-to-one relationship.
Orders.create({
date: req.body.date,
...
card: req.body.card //FK to a card ID
})
req.body.card is the id of the card. When I get the created object back though card is null.
I also tried creating the order first and then adding an association after but haven't had any luck with that either. I found some stuff about a set function that would set the association but I only saw examples of it with hasMany relationships.
I'm not trying to do anything fancy - just want to set the card id in the card field on an Order.
There are a few issues with your model definition:
Orders.hasOne(models.Cards, { foreignKey: 'id', as: 'card', });
In your definition, the foreign key will be placed on the cards table.
Naming the foreignKey as id will collide with the models own id field.
Try the following:
const Card = sequelize.define('card', {
name: { type: Sequelize.STRING }
});
const Order = sequelize.define('order', {
name: { type: Sequelize.STRING }
});
Order.hasOne(Card, { foreignKey: 'order_id' }); // if foreignKey is not included, will default to orderId
const order = await Order.create({
name: 'first order'
})
const card = await Card.create({
name: 'first card',
order_id: order.id
})
Related
Let's define for example these entities:
#Entity()
export class Author {
#OneToMany(() => Book, book => book.author)
books = new Collection<Book>(this);
}
#Entity()
export class Book {
#Property()
name: string;
#ManyToOne()
author: Author;
}
const authors = await authorRepository.find({ books: {name: 'Test'} }, {
limit: 10
});
As you can see i want to select all authors that have books with name 'Test', but this will generate the following query:
select `e0`.* from `author` as `e0`
left join `book` as `e1` on `e0`.`id` = `e1`.`author_id`
where `e1`.`name` = 'Test' limit 10
The problem is when i have more than 2 authors and each of them has more than 10 books with name 'Test', this query will return only the first author because of the limit clause.
I am not sure if this is a bug in the ORM or it's a expected behavior.
One way to fix that is to select all rows without the limit clause and do the pagination in memory like they are doing in hibernate, but i am not sure how much memory will be used with very large tables and this might block the event loop in NodeJS while processing them.
You could fallback to query builder here to apply group by clause:
const qb = await authorRepository.createQueryBuilder('a');
qb.select('a.*').where({ books: { name: 'Test' } }).limit(10).groupBy('a.id');
const authors = await qb.getResult();
Will think about how to support this use case directly via EM/repository API.
I switched over to a Redux + Immutable JS project from Ember a few months ago and am overall enjoying the experience.
One problem I still have not found a nice solution for when working with Records is storing meta data for that Record.
For example, let's say I have a User record:
const userRecord = Immutable.Record({
id: null,
name: '',
email: ''
});
For the User, I may also wish to store properties like isLoading or isSaved. The first solution would be to store these in the userRecord. Although this would be the easiest solution by far, this feels wrong to me.
Another solution might be to create a User Map, which contains the User Record, as well as meta data about the User.
Ex.
const userMap = Immutable.Map({
record: Immutable.Record({
id: null,
name: '',
email: ''
}),
isLoading: false,
isSaved: true
});
I think this is more elegant, but I don't like how all the user properties become even more deeply nested, so accessing User properties becomes very verbose.
What I miss most about Ember is being able to access Model properties easily.
Ex. user.get('isSaved') or user.get('name')
Is it possible to recreate something like this with Redux and Immutable? How have you approached this situation before?
I might be misunderstanding the problem, because
What I miss most about Ember is being able to access Model properties easily.
user.get('isSaved') or user.get('name')
This does work for Immutable records.
If you don't want to add too many properties to your record, you could have a single status property and add some getters (assuming your statuses are mutually exclusive):
const STATUS = {
INITIAL: 'INITIAL',
LOADING: 'LOADING',
SAVING: 'SAVING
};
class UserRecord extends Immutable.Record({
id: null,
name: '',
email: '',
status: STATUS.INITIAL}) {
isLoading() {
return this.get('status') === STATUS.LOADING;
}
isSaving() {
return this.get('status') === STATUS.SAVING;
}
}
new UserRecord().isLoading()); // returns false
new UserRecord({status: STATUS.LOADING}).isLoading(); // returns true
new UserRecord().set('status', STATUS.LOADING).isLoading(); // returns true
I have a game built on Meteor framework. One game document is something like this:
{
...
participants : [
{
"name":"a",
"character":"fighter",
"weapon" : "sword"
},
{
"name":"b",
"character":"wizard",
"weapon" : "book"
},
...
],
...
}
I want Fighter character not to see the character of the "b" user. (and b character not to see the a's) There are about 10 fields like character and weapon and their value can change during the game so as the restrictions.
Right now I am using Session variables not to display that information. However, it is not a very safe idea. How can I subscribe/publish documents according to the values based on characters?
There are 2 possible solutions that come to mind:
1. Publishing all combinations for different field values and subscribing according to the current state of the user. However, I am using Iron Router's waitOn feature to load subscriptions before rendering the page. So I am not very confident that I can change subscriptions during the game. Also because it is a time-sensitive game, I guess changing subscriptions would take time during the game and corrupt the game pleasure.
My problem right now is the user typing
Collection.find({})
to the console and see fields of other users. If I change my collection name into something difficult to find, can somebody discover the collection name? I could not find a command to find collections on the client side.
The way this is usually solved in Meteor is by using two publications. If your game state is represented by a single document you may have problem implementing this easily, so for the sake of an example I will temporarily assume that you have a Participants collection in which you're storing the corresponding data.
So anyway, you should have one subscription with data available to all the players, e.g.
Meteor.publish('players', function (gameId) {
return Participants.find({ gameId: gameId }, { fields: {
// exclude the "character" field from the result
character: 0
}});
});
and another subscription for private player data:
Meteor.publish('myPrivateData', function (gameId) {
// NOTE: not excluding anything, because we are only
// publishing a single document here, whose owner
// is the current user ...
return Participants.find({
userId: this.userId,
gameId: gameId,
});
});
Now, on the client side, the only thing you need to do is subscribe to both datasets, so:
Meteor.subscribe('players', myGameId);
Meteor.subscribe('myPrivateData', myGameId);
Meteor will be clever enough to merge the incoming data into a single Participants collection, in which other players' documents will not contain the character field.
EDIT
If your fields visibility is going to change dynamically I suggest the following approach:
put all the restricted properties in a separated collection that tracks exactly who can view which field
on client side use observe to integrate that collection into your local player representation for easier access to the data
Data model
For example, the collection may look like this:
PlayerProperties = new Mongo.Collection('playerProperties');
/* schema:
userId : String
gameId : String
key : String
value : *
whoCanSee : [String]
*/
Publishing data
First you will need to expose own properties to each player
Meteor.publish('myProperties', function (gameId) {
return PlayerProperties.find({
userId: this.userId,
gameId: gameId
});
});
then the other players properties:
Meteor.publish('otherPlayersProperties', function (gameId) {
if (!this.userId) return [];
return PlayerProperties.find({
gameId: gameId,
whoCanSee: this.userId,
});
});
Now the only thing you need to do during the game is to make sure you add corresponding userId to the whoCanSee array as soon as the user gets ability to see that property.
Improvements
In order to keep your data in order I suggest having a client-side-only collection, e.g. IntegratedPlayerData, which you can use to arrange the player properties into some manageable structure:
var IntegratedPlayerData = new Mongo.Collection(null);
var cache = {};
PlayerProperties.find().observe({
added: function (doc) {
IntegratedPlayerData.upsert({ _id : doc.userId }, {
$set: _.object([ doc.key ], [ doc.value ])
});
},
changed: function (doc) {
IntegratedPlayerData.update({ _id : doc.userId }, {
$set: _.object([ doc.key ], [ doc.value ])
});
},
removed: function (doc) {
IntegratedPlayerData.update({ _id : doc.userId }, {
$unset: _.object([ doc.key ], [ true ])
});
}
});
This data "integration" is only a draft and can be refined in many different ways. It could potentially be done on server-side with a custom publish method.
I recently started working with SailsJS, and I found the process of defining attributes and associations very elegant. However, there is one thing I cannot find out:
I have a model Couple(person1, person2). I'd like to create an association in Person, that lists every couple they are a member of. Something along the lines of:
attributes{
//other attributes,
couples:{
collection: 'couple',
via: 'person1, person2'
}
}
My current method is:
attributes{
//other attributes,
couples1:{
collection: 'couple',
via: 'person1'
},
couples2:{
collection: 'couple',
via: 'person2'
},
getCouples: function() {
return this.couples1 + this.couples2;
}
}
But this doesn't seem very pretty, is there a way to do it more elegantly?
If i get your question correctly, you want a person and its couple, i'm assuming you have a monogamous relationship in your couples (meaning one person belongs to the other and vice-versa) you can make an association with the same model, like this.
var Person = {
attributes: {
name: {type:'string'},
couple: {model: 'Person', via: 'couple'}
}
};
module.exports = Person
Then you can create and associate them like this. (Assuming sails console)
Person.create({name: 'John Doe', couple: {name: 'Jane Doe'} }).exec(console.log)
Person.update(2, {couple:1}).exec(console.log)
And retrieving them is as easy as
Person.find({name:'John Doe'}).populate('couple').exec(console.log)
You can try to associate them with .add and .save but i'm not sure if it works when the association points to the same model, so check that out.
Another option is to have it like this:
var Person = {
attributes: {
name: {type:'string'},
couple: {model: 'Couple', via: 'persons'}
}
};
module.exports = Person;
Couple model holds a collection of persons, so you can add persons to the
couple model, and associate them later with the person.couple attribute.
var Couple = {
attributes: {
persons: {collection: 'Person', via: 'couple'}
}
};
module.exports = Couple;
Or you could use what you have, which is not ideal in this case, but it works.
If I do this, all is good with my itemRef:
itemRef.child('appreciates').set(newFlag);
itemRef.child('id').set(newId);
other properties of itemRef remain BUT child_changed is called twice
If I do this:
itemRef.set({appreciates:newFlag,id:newId});
child_changed is called only once but my other properties are destroyed.
Is there a workaround besides the clumsy one of repopulating the entire reference object?
Thanks,
Tim
The Firebase update() function will allow you to modify some children of an object while leaving others unchanged. The update function will only trigger one "value" event on other clients for the path being written no matter how many children are changed.
In this example, you could do:
itemRef.update({appreciates:newFlag,id:newId});
Documentation for update() is here.
You can create a rule that will prevent overwrites if data already exists.
Reproduced here from Firebase docs Existing Data vs New Data
// we can write as long as old data or new data does not exist
// in other words, if this is a delete or a create, but not an update
".write": "!data.exists() || !newData.exists()"
Now .update takes care of it, you can change existing data or add new one without affecting the rest of data you already had there.
In this example, I use this function to set a product as sold, the product has other variables with data and may or may not have sold or sellingTime but it doesn't matter cos if it doesn't exist will create them and if it does, will update the data
var sellingProduct = function(id){
dataBase.ref('product/'+id).update({
sold:true,
sellingTime: Date.now(),
}).then (function(){
alert ('your product is flaged as sold')
}).catch(function(error){
alert ('problem while flaging to sold '+ error)
})
}
Though you can use update, you can also use set with merge option set to true:
itemRef.set({ appreciates:newFlag, id:newId }, { merge: true });
This will create a new document if it doesn't exists and update the existing if it does.
I've been trying to do this having a structure like the following:
The problem I was having was when running say set on specific fields such as name, description and date all of the other child nodes would then be removed with the following:
return (dispatch) => {
firebase.database().ref(`/gigs/${uid}`)
.set({ name, description, date })
.then(() => {
dispatch({ type: GIG_SAVE_SUCCESS });
Actions.home({ type: 'reset' });
});
};
Leaving only the name, description and date nodes but using the following the specific nodes are updated without removing the other child nodes i.e. members, image etc:
return (dispatch) => {
var ref = firebase.database().ref(`/gigs/${uid}`);
ref.child('name').set(name)
ref.child('description').set(description)
ref.child('date').set(date)
.then(() => {
dispatch({ type: GIG_SAVE_SUCCESS });
Actions.home({ type: 'reset' });
});
};