Jaydata when there are sequential add operations not working - jaydata

I'm using Jaydata with it's Indexeddbprovider, I've a problem in adding scenario.
When there are multiple adds, just the first one works!
$data.Entity.extend("Person", {
Id: { type: "int", key: true, computed: false },
Task: { type: String, required: true, maxLength: 200 },
DueDate: { type: Date },
Completed: { type: Boolean },
University: { type: "int"},
Degree: { type: "int" }
});
$data.EntityContext.extend("ClientDatabase", {
People: { type: $data.EntitySet, elementType: Person }
});
var db = new ClientDatabase({
provider: 'indexedDb', databaseName: 'ClientDB', version: 1
});
var newEntity = {
Id: 1,
Task: 'task1',
DueDate: new Date(),
Completed: false,
University: 1,
Degree: 1
};
var newEntity2 = {
Id: 4,
Task: 'task4',
DueDate: new Date(),
Completed: false,
University: 4
Degree: 4
};
add(db, newEntity, entity1AddedSuccessfully);
function entity1AddedSuccessfully(){
add(db, newEntity2);
}
function add(db, entity, callback){
db.onReady({
success: function () {
db["_People"].add(entity);
db.saveChanges(function () {
if (callback !== undefined) {
callback(entity);
}
});
}
});
}
The problem is in this scenario, newEntity is just added to ClientDB and there is no newEntity2!
Any help would be appreciated.

I made many changes in the code, you can check it out on JSFiddle.
Important things:
use the typed db.People collection to query and insert the records
(not db["_People"])
use typed entites - new Person()
if you use auto-generated Id, it's better to not set it manually :)
after I modified all of these above, your logic passed the same newEntity to
the callback, so I had 3 records instead of 2. I simplified the code
by removing the callback function definitions
Check out the JSFiddle code and share your feedback if you wanted to achieve this behavior
$data.Entity.extend("Person", {
Id: { type: "int", key: true, computed: true },
Task: { type: String, required: true, maxLength: 200 },
DueDate: { type: Date },
Completed: { type: Boolean },
University: { type: "int"},
Degree: { type: "int" }
});
$data.EntityContext.extend("ClientDatabase", {
People: { type: $data.EntitySet, elementType: Person }
});
var db = new ClientDatabase({
provider: 'indexedDb', databaseName: 'ClientDB', version: 1
});
db.onReady(function(){
var newEntity = new Person( {
Task: 'task1',
DueDate: new Date(),
Completed: false,
University: 1,
Degree: 1
});
var newEntity2 = new Person({
Task: 'task4',
DueDate: new Date(),
Completed: false,
University: 4,
Degree: 4
});
db.People.add(newEntity);
db.saveChanges(function() {
db.People.add(newEntity2);
db.saveChanges(function() {alert(newEntity2.Id);});
});
});

I'm afraid that you've mixed it up a little bit.
$data.Entity and $data.EntityContext are for model definition
var db = new ClientDatabase(...)
db is a database context and it has no EntityContext method or property
so your code should look like:
db.onReady()
db.People.add(entity)
db.saveChanges(...)

I hope this helps someone… it did the trick for me…
http://jaydata.org/forum/viewtopic.php?f=3&t=184

Related

Jest toMatchObject with MongoDB confusing

I have a test on my mongoose model and while one models' tests are running completly fine, another one which is basically a copy - does not work. And honestly I don't understand the problem. I tried to remove some of the properties or add hardcoded values to really match 100% exactly - but somehow i always get a similar error.
The error. What drives me crazy is that I tried to remove/add the "_id" and the "createdOn" field but at least the "_id" always appear in the error. As said above in another model the test does not complain about the "_id" because I do not validate it there...I just don't get it.
insert › Should save a channel
expect(received).toMatchObject(expected)
- Expected - 1
+ Received + 2
## -1,8 +1,9 ##
Object {
+ "_id": "5e962f1dc133d8b92891ddaf",
"createdBy": "5e962f1dc133d8b92891ddae",
- "createdOn": Anything,
+ "createdOn": 2020-04-14T21:46:05.907Z,
"hasAcceptedLegal": false,
"isActive": true,
"isPublic": false,
"members": Array [
"5e962f1dc133d8b92891ddae",
48 | expect(spy).toHaveBeenCalled();
49 |
> 50 | expect(channel).toMatchObject({
| ^
51 | name: mockName,
52 | createdBy: mockCreatedById,
53 | createdOn: expect.anything(),
at Object.it (test/model/channel.model.test.ts:50:21)
The respective model
import mongoose, { Schema, Document } from "mongoose";
import { IUser } from "./user.model";
export interface IChannel extends Document {
name: string;
createdBy: IUser["id"];
createdOn: Date;
isActive: boolean;
isPublic: boolean;
hasAcceptedLegal: boolean;
members: [IUser["id"]];
}
const ChannelSchema: Schema = new Schema({
name: { type: String, required: true },
createdBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
createdOn: { type: Date },
isActive: { type: Boolean, default: true },
isPublic: { type: Boolean, default: false },
hasAcceptedLegal: { type: Boolean, default: false },
members: [
{
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
],
});
export default mongoose.model<IChannel>("Channel", ChannelSchema);
The test:
it("Should save a channel", async () => {
expect.assertions(2);
let mockName = Faker.company.companyName();
let mockCreatedById = Types.ObjectId();
let mockCreatedOn = Date.now();
const channel: IChannel = new Channel({
name: mockName,
createdBy: mockCreatedById,
createdOn: mockCreatedOn,
members: [mockCreatedById],
});
const spy = jest.spyOn(channel, "save");
channel.save();
expect(spy).toHaveBeenCalled();
expect(channel).toMatchObject({
name: mockName,
createdBy: mockCreatedById,
createdOn: expect.anything(),
isActive: true,
isPublic: false,
hasAcceptedLegal: false,
members: [mockCreatedById],
});
});
just for reference, I found sth. that the returned mongoose object should be converted with ".toJSON()" and then it works, but even then it was having a problem with the "createdOn" field as it seems to be a formatted Date or sth. like this (no string, parenthesis missing).
What I finally did now was the following and now it works:
it("Should save a channel (required+defaults)", async () => {
expect.assertions(2);
let mockName = Faker.company.companyName();
let mockCreatedById = Types.ObjectId();
let mockCreatedOn = new Date();
const channel: IChannel = new Channel({
name: mockName,
createdBy: mockCreatedById,
createdOn: mockCreatedOn,
members: [mockCreatedById],
});
const spy = jest.spyOn(channel, "save");
channel.save();
expect(spy).toHaveBeenCalled();
expect(channel.toJSON()).toMatchObject({
_id: expect.anything(),
name: mockName,
createdBy: mockCreatedById,
createdOn: expect.anything(),
isActive: true,
isPublic: false,
hasAcceptedLegal: false,
members: expect.arrayContaining([mockCreatedById]),
});
});

Sequelize associations: Can't add associations, throws TypeError

I am a newbie to postgres and Sequelize. I come from NoSQL background, so this is a bit daunting to me.
First of all this is my project directory:
server/
components/
center/
center.model.js
center.controller.js
center.routes.js
So, I am following a modules based architecture.
Here's my center.models.js:
const Sequelize = require('sequelize');
const sequelize = require('../../config/sequelize');
const Inventory = require('../inventory/inventory.model');
const Center = sequelize.define('center', {
name: {
type: Sequelize.TEXT,
allowNull: false,
validate: {
notEmpty: true,
},
},
zone: {
type: Sequelize.TEXT,
allowNull: false,
validate: {
notEmpty: true,
},
},
lat: {
type: Sequelize.DOUBLE,
},
long: {
type: Sequelize.DOUBLE,
},
nearest_metro: {
type: Sequelize.TEXT,
},
metro_distance: {
type: Sequelize.DOUBLE,
},
address: {
type: Sequelize.TEXT,
},
google_map: {
type: Sequelize.TEXT,
},
landmark: {
type: Sequelize.TEXT,
},
landmark_directions: {
type: Sequelize.TEXT,
},
active: {
type: Sequelize.BOOLEAN,
defaultValue: true,
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
Center.hasMany(Inventory);
module.exports = Center;
And this is my SpacePartner model:
const Sequelize = require('sequelize');
const sequelize = require('../../config/sequelize.js');
const Center = require('../center/center.model.js');
const SpacePartner = sequelize.define('space_partner', {
name: {
type: Sequelize.TEXT,
allowNull: false,
validate: {
notEmpty: true,
},
},
active: {
type: Sequelize.BOOLEAN,
defaultValue: true,
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
SpacePartner.hasMany(Center);
module.exports = SpacePartner;
And this is how I am using the two models in my space_partner.controller.js:
const CreateSpacePartner = async (req, res) => {
try {
const {
spacePartner,
center,
inventory,
} = req.body;
const [createdSP, createdCenter, createdInventory] = await Promise.all([SpacePartner.create(spacePartner), Center.create(center), Inventory.create(inventory)]);
createdCenter.addSpacePartner(createdSP);
createdInventory.addCenter(createdCenter);
createdInventory.addInventoryType(inventory.inventory_type_id);
const [updatedCenter, updatedInventory] = await Promise.all([createdCenter.save({
fields: ['space_partner_id']
}), createdInventory.save({
fields: ['center_id', 'inventory_type_id']
})]);
return res.status(200).json({
spacePartner: createdSP,
center: updatedCenter,
inventory: updatedInventory,
});
} catch (e) {
console.log(e);
return res.status(500).json({
message: 'Sorry, we are facing some issue right now. Please, try again later.',
});
}
};
Now, the issue is that I am getting TypeError on addSpacePartner addCenter and addInventoryType.
I was following the official docs.
I did go through other tutorials, but they had more of MVC architecture, I don't want to go back to restructuring my code.
Any help would this would be great.
For this to work createdCenter.addSpacePartner(createdSP);, to work, we need to define the relationship between Center and SpacePartner. So it should be
Center.hasMany(SpacePartners)
Once you have this, these functions should be available to you.
You can read more about association here
The code for Inventory is not posted, so its hard to say about that.

How to work with many to few mongodb relationship with feathersjs?

I have two models, with a many-to-few relationship, that I'm modelling as follows:
// Portfolio
const portfoliosSchema = new Schema({
name: { type: String, required: true },
description: { type: String },
user: { type: Schema.Types.ObjectId, ref: 'User', required: true },
positions: [{
stock: { type: Schema.Types.ObjectId, ref: 'Stock', required: true },
cost: number,
amount: number,
}]
});
// Stock
const stocksSchema = new Schema({
exchange: { type: String, required: true },
symbol: { type: String, required: true },
company: { type: String },
description: { type: String }
});
Without writing a custom service / in a feathers friendly way, how would I:
Query portfolios and populate the relevant records from the stocks
collection
Simplify insert/updates to the nested positions within the portfolio schema (ie without updating the entire record)
Is this supported / should I write a custom service and/or normalize the relationship?
Edit - Solved #1 the first issue of getting extra data using a before hook:
function(hook) {
const query = hook.params.query;
hook.params.query = Object.assign({},query,{
$populate: {
path: 'positions.stock',
select: 'exchange symbol'
}
});
}
Sample of populate code, adjust to your needs:
Portfolio.find({ some: 'params' })
.populate({ path: 'stock', select: 'name -_id' })
.exec((err, portfolios) => {
res.json({ portfolio: portfolios });
});
Updating a nested array item:
Position.update({ 'position._id': 'h43q9h94945d948jh098j6h0' }, {
'$set': { 'position.$.whatever': 'your-updated-stuff' }
}, err => {
if (err) return console.log(err));
res.json({ success: true });
});

Get single attribute in model using Mongoose

I have 2 Schemas : StepSchema, StepRelationshipSchema
var StepSchema = new Schema(
{
name: { type: String, required: true },
description: { type: String, default: '' },
isVisible: { type: Boolean, default: true }
}, options
);
var StepRelationshipSchema = new Schema(
{
workflowId: { type: String, required: true },
stepId: { type: String, required: true },
prevSteps: [ Schema.Types.Mixed ] ,
nextSteps: [ Schema.Types.Mixed ] ,
gotoStep: { type: String, default: '' }
}, options
);
In StepSchema, I want to create a static method to get nextSteps in StepRelationshipSchema.
Can I use this, thank you so much.
StepSchema.statics.getNextSteps = function(workflowId, currStepId) {
return StepRelationship.findOne({
workflowId: workflowId,
stepId: currStepId
}).nextSteps
};
As #JohnnyHK suggested in his comments, findOne() is async thus you need to use a callback function as follows:
// create a query for next stepswith a blogpost _id matching workflowId and currStepId
schema.statics.getNextSteps = function (workflowId, currStepId, callback) {
return this.model('StepRelationship').findOne({
workflowId: workflowId,
stepId: currStepId
}, callback);
}

Eager Loading : How to disable specific fields of included table

I am trying to do Eager Loading in Sequelize with PostgreSQL where I need to find the Users which have a given specific Mail id or basically, i am performing find operation on Mail model while using include to include User model
UserModel :
module.exports = function (sequelize, Sequelize) {
var User = sequelize.define('User', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstname: {
type: Sequelize.STRING,
require: true
},
lastname: {
type: Sequelize.STRING,
require: true
},
age: {
type: Sequelize.INTEGER,
require: true
},
phone: {
type: Sequelize.STRING,
require: true
},
location: {
type: Sequelize.STRING,
require: true
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
return User;
};
MailModel :
module.exports = function (sequelize, Sequelize) {
var User = require('./User.js')(sequelize, Sequelize)
var Mail = sequelize.define('Mail', {
name: {
type: Sequelize.STRING,
require: true
}
});
Mail.belongsTo(User);
return Mail;
};
MailController :
var db = require('../services/db.js');
module.exports = {
create: function (req, res, next) {
var Mailm = db.MailModel;
var name = req.body;
try {
db.sequelize.sync().then(function () {
Mailm.create(name).then(function (found) {
return res.json({
success: true,
message: found.get({
plain: true
})
});
})
});
} catch (ex) {
res.json({
success: false,
exception: ex
});
return;
}
},
query: function (req, res, next) {
var Mailm = db.MailModel;
var Userm = db.UserModel;
var name = req.body;
var option = {};
option.where = name;
option.include = [{
model: Userm
}];
try {
Mailm.findAll(option).then(function (found) {
console.log(found);
return res.json({
success: true,
message: found
});
});
} catch (ex) {
res.json({
success: false,
exception: ex
});
return;
}
}
};
It is returning me the records of both User and Mail table in exactly the right way .
Output :
{
"success": true,
"message":[
{
"id": 2,
"name": "Mailb2",
"createdAt": "2015-07-30T07:32:51.807Z",
"updatedAt": "2015-07-30T07:32:51.807Z",
"UserUserId": 2,
"User":{
"userId": 2,
"firstname": "Prerna",
"lastname": "Jain",
"age": 20,
"phone": "9812123456",
"location": "Sirsa",
"createdAt": "2015-07-30T07:30:48.000Z",
"updatedAt": "2015-07-30T07:30:48.000Z"
}
}
]
}
But I want to disable createdAt and updatedAt fields of User table so that it does not give me these two fields in the output for User.
I have tried a lot as of how to do this but still in vain.Can anyone please help me out.
I bet this is coming late, add attribute/properties to your models called timestamps, it accepts a boolean as a value. For example:
module.exports = function (sequelize, Sequelize) {
var User = require('./User.js')(sequelize, Sequelize)
var Mail = sequelize.define('Mail', {
name: {
type: Sequelize.STRING,
require: true
}
},
{
// This does the magic
timestamps: false,
});
Mail.belongsTo(User);
return Mail;
};
Also, add it to the User model:
var User = sequelize.define('User', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstname: {
type: Sequelize.STRING,
require: true
},
lastname: {
type: Sequelize.STRING,
require: true
},
age: {
type: Sequelize.INTEGER,
require: true
},
phone: {
type: Sequelize.STRING,
require: true
},
location: {
type: Sequelize.STRING,
require: true
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
},
{
timestamps: false
});
return User;
};
You can use
Model.findAll({
attributes: { exclude: ['baz'] }
});
more examples with attributes - http://docs.sequelizejs.com/en/latest/docs/querying/#attributes