Specifying key with $add? - angularfire

I would like to index customers in the data store using their email address as key, to enforce uniqueness.
I'm initialising my connection as follows:
var ref = new Firebase("https://[myApp].firebaseio.com/");
$scope.customers = $firebase(ref.child("customers")).$asArray();
And here's my controller:
function customerCtrl($scope) {
$scope.addNewMember = function (firstName, lastName, email) {
$scope.customers.$add({
firstName: firstName,
lastName: lastName,
email: email
});
}
}
Of course, this generates a unique key like -Jbd4KJ_CrFqlpnqbGVu. Is there a way to set the key manually instead, so that my data will look like this?
{
'bob.smith#example.com': {
firstName: 'Bob',
lastName: 'Smith'
},
'jane.doe#example.com': {
firstName: 'Jane',
lastName: 'Doe'
},
...
}
Or, am I thinking about this the wrong way?

You don't need a collection (to use $asArray) if you are working with a specific path with a key you already know. You'd probably be ahead to work directly with a Firebase reference in that case.
var ref = new Firebase("https://[myApp].firebaseio.com/");
$scope.customers = $firebase(ref.child("customers")).$asArray();
$scope.addNewMember = function(firstName, lastName, email) {
var escapedEmail = escapeEmailAddress(email);
ref.child('customers/'+escapedEmail).set({firstName: firstName, lastName: lastName, email: email});
};
function escapeEmailAddress(email) {
return (email||'').replace(/\./g, ',');
}
The data will automatically be synchronized into $scope.customers in real-time.
If you're set on doing this through the synchronized array, then manually declare the $id and use $save instead:
$scope.addNewMember = function (firstName, lastName, email) {
var rec = {
firstName: firstName,
lastName: lastName,
email: email,
$id: escapeEmailAddress(email)
};
$scope.customers.push(rec);
$scope.customers.$save(rec);
};

Related

Prisma how can I update only some of the models fields in update()

I have a Prisma model with lets say 10 fields.
Think User model with firstname, lastname, address, e-mail , phone, mobile, age etc.
I am trying to write a update method for this, where I most of the times only want to update some or only 1 of the fields. Not the whole User. If the field is not sent with the request, I want to keep the value from the db.
What would the best practice be for this. Should I check for all fields to be in the req object?
How could I write this for prisma?
Example on how I would like it to work:
req = {firstname: 'Bob', email: 'bob#bob.bob', etc}
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: {
req.firstname ? (email: req.firstname) : null,
req.email ? (email: req.email) : null,
req.address? (email: req.address) : null,
},
})
Or should I check for values to be present in req and build the data object in 10 versions:
let customDataObject = {}
if (req.firstname) {
customDataObject.firstname = req.firstname
}
if (req.email) {
customDataObject.email= req.email
}
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: customDataObject,
})
The undefined property is used in Prisma to do exactly what you're trying to achieve. Basically, when a field is assigned undefined it means ignore this and do nothing for this field. You can learn more about this in the article about null and undefined in the docs.
This is what your update query should look like.
// assuming email, firstname and address fields exist in your prisma schema.
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: {
firstname: req.firstname || undefined, // if req.firstname is falsy, then return undefined, otherwise return it's value.
email: req.email || undefined,
address: req.address || undefined
},
})

Why is my SQL data returning undefined in my array methods?

The First array returned when I console log person
The second array that returns when I console log person
I am trying to create a function that pulls the data from 2 tables in my SQL database. I am using async/await, which is still new to me. The issue is somewhere in the two array methods; for some reason they are returning data as undefined.
async function updateRole() {
console.log('hi');
//cycle through both arrays and create new arrays with same information using maps
//return object with employee and role info
const allEmployees = await db.promise().query(`SELECT * FROM employees`);
const allRoles = await db.promise().query(`SELECT * FROM roles`);
console.log(allEmployees);
const employeeChoices = allEmployees.map((person) => {
return {
name: `${person.first_name} ${person.last_name}`,
value: person.id
}
})
const roleChoices = allRoles.map((role) => {
return {
name: role.title,
value: role.id
}
})
const { employeeId, roleId } = await inquirer.prompt([
{
type: 'list',
name: 'employeeId',
message: 'Which employee would you like to update?',
choices: employeeChoices
},
{
type: 'list',
name: 'roleId',
message: 'What is their new role?',
choices: roleChoices
}])
await db.promise().query(`UPDATE employees SET role_id = ? WHERE id = ?`, [roleId, employeeId])
console.log('Successfully updated employee!');
askQuestions();
Update: I added screenshots fo the console log for person. role returns the same format, but obviously different data. I am unsure what the array of ColumnDefinition objects does, or why it's there.

Sequelize mysql order by concat of two fields

My DB has a users table that has a first_name,last_name columns, either of them maybe empty.
I also have a UI dashboard that displays a table with a full_name column that is a concatenation of the two fields, this column is sortable.
I am using sequelize with a mysql database to fetch the users, how can I order by full_name (And trim the spaces of the concatenated field)
The SQL solution
You can order by multiple columns
SELECT * FROM user ORDER BY last_name ASC, first_name ASC
OR you first concatenate the two fields and order by full_name (but keep in mind this might be slow)
SELECT *, TRIM(CONCAT(first_name,' ', last_name)) full_name FROM user ORDER BY full_name ASC
The Sequelize solution
const { Sequelize, DataTypes } = require('sequelize')
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
first_name: {
type: DataTypes.STRING(100)
},
last_name: {
type: DataTypes.STRING(100)
},
//virtual attribute for concatenation of first_name and last_name
full_name: {
type: DataTypes.VIRTUAL,
get() {
const firstName = this.getDataValue('first_name');
const lastName = this.getDataValue('last_name');
return [firstName, lastName].join(' ').trim();
}
},
});
User.findAll({
/** Order by multiples columns **/
//order: [['first_name', 'DESC'], ['last_name', 'ASC']],
/** Order by the two fields concatenated and trimed */
order: Sequelize.literal("trim(concat(first_name,' ', last_name)) ASC")
}).then(users => {
users.forEach(user=> {
console.log(user.get());
})
})

Transaction with Sequelize doesn't work

I want to build a simple webform where you can enter a persons firstname, lastname and select multiple groups for this person (but one for now)
I'm using node.js and sequelize to store the person in a MariaDB -Database.
Sequelize created the tables Persons, Groups and GroupsPersons according to the defined models.
var Sequelize = require("sequelize");
var sequelize = new Sequelize(config.database, config.username, config.password, config);
var Group = sequelize.define("Group", {
name: {
type: DataTypes.STRING,
allowNull: false
}
}
var Person = sequelize.define("Person", {
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING,
allowNull: false
}
}
Person.belongsToMany(Group, {as: 'Groups'});
Group.belongsToMany(Person, {as: 'Persons'});
Because creating the person and assigning it into a group should be handled atomically in one step I decided to use a transaction, shown in the docs here:
http://sequelize.readthedocs.org/en/latest/docs/transactions/#using-transactions-with-other-sequelize-methods
var newPerson = {
firstName: 'Hans',
lastName: 'Fischer'
}
var id = 3 // group
sequelize.transaction(function (t) {
return Person.create(newPerson, {transaction: t}).then(function (person) {
return Group.find(id, {transction: t}).then(function(group){
if (!group) throw Error("Group not found for id: " + id);
return person.setGroups( [group], {transction: t});
})
});
}).then(function (result) {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback is
console.log(result);
}).catch(function (err) {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback is
console.error(err);
});`
But for some reason neither function (result) {.. for success nor the function in catch gets called. However, the complete SQL queries of the transaction were generated except COMMIT, so nothing was inserted into the db.
If I put it like this
return person.setGroups( [], {transction: t});
the transactions succeeds, but with no inserts into GroupsPersons of course.
Any ideas or suggestions?
Thanks for help!
{transaction: t} was misspelled, it works now

Waterline ORM equivalent of insert on duplicate key update

I have a table user_address and it has some fields like
attributes: {
user_id: 'integer',
address: 'string' //etc.
}
currently I'm doing this to insert a new record, but if one exists for this user, update it:
UserAddress
.query(
'INSERT INTO user_address (user_id, address) VALUES (?, ?) ' +
'ON DUPLICATE KEY UPDATE address=VALUES(address);',
params,
function(err) {
//error handling logic if err exists
}
Is there any way to use the Waterline ORM instead of straight SQL queries to achieve the same thing? I don't want to do two queries because it's inefficient and hard to maintain.
The answer above is less than ideal. It also has the method as part of the attributes for the model, which is not correct behavior.
Here is what the ideal native solution looks like that returns a promise just like any other waterline model function would:
module.exports = {
attributes: {
user_id: 'integer',
address: 'string'
},
updateOrCreate: function (user_id, address) {
return UserAddress.findOne().where({user_id: user_id}).then(function (ua) {
if (ua) {
return UserAddress.update({user_id: user_id}, {address: address});
} else {
// UserAddress does not exist. Create.
return UserAddress.create({user_id: user_id, address: address});
}
});
}
}
Then you can just use it like:
UserAddress.updateOrCreate(id, address).then(function(ua) {
// ... success logic here
}).catch(function(e) {
// ... error handling here
});
Make a custom model method that does what you want using Waterline queries isntead of raw SQL. You will be doing two queries, but with Waterline syntax.
Example below (if you don't know about deferred objects then just use callback syntax, but the logic is the same):
var Q = require('q');
module.exports = {
attributes: {
user_id: 'integer',
address: 'string',
updateOrCreate: function (user_id, address) {
var deferred = Q.defer();
UserAddress.findOne().where({user_id: user_id}).then(function (ua) {
if (ua) {
// UserAddress exists. Update.
ua.address = address;
ua.save(function (err) {deferred.resolve();});
} else {
// UserAddress does not exist. Create.
UserAddress.create({user_id: user_id, address: address}).done(function (e, ua) {deferred.resolve();});
}
}).fail(function (err) {deferred.reject()});
return deferred.promise;
}
};
#Eugene's answer is good but it will always run 2 operations: findOne + update or create. I believe we can optimize it further because if the record exists we just need to run update. Example:
module.exports = {
attributes: {
user_id: 'integer',
address: 'string'
},
updateOrCreate: function (user_id, address) {
return UserAddress.update({user_id: user_id}, {address: address})
.then(function(ua){
if(ua.length === 0){
// No records updated, UserAddress does not exist. Create.
return UserAddress.create({user_id: user_id, address: address});
}
});
}
}
BTW, there is an open request to implement .updateOrCreate in waterline: #790