How to return and update a table in bookshelf knex - postgresql

I am using postgresql, knex, and bookshelf to make queries to update my users table. I would like to find all users who didn't sign in during a specific time and then update their numAbsences and numTardies field.
However it appears that when running a raw sql query using bookshelf.knex the result that I get for users is an array of objects rather than an array of bookshelf objects of objects because I can't save the objects directly to the database when I try to use .save(). I get the exception user.save is not a function.
Does anyone know how I can update the values in the database for the users? I've seen the update function but I need to also return the users in absentUsers so I select them currently.
// field indicates whether the student was late or absent
var absentUsers = function(field){
// returns all users who did not sign in during a specific time
if (ongoingClasses){
return bookshelf.knex('users')
.join('signed_in', 'signed_in.studentId', '=', 'users.id')
.where('signed_in.signedIn', false)
.select()
.then(function(users){
markAbsent(users, field);
return users;
});
}
}
var markAbsent = function(users, field){
users.forEach(function(user){
user[field]++;
user.save();
})
}

I've solved my problem by using another sql query in knex. It seemed there was no way to use a sql query and then use standard bookshelf knex methods since the objects returned were not bookshelf wrapper objects.
var absentUsers = function(field){
// returns all users who did not sign in during a specific time
if (ongoingClasses){
return bookshelf.knex('users')
.join('signed_in', 'signed_in.studentId', '=', 'users.id')
.where('signed_in.signedIn', false)
.select()
.then(function(users){
markAbsent(users, field);
});
}
}
var markAbsent = function(users, field){
users.forEach(function(user){
var updatedUser = {};
updatedUser[field] = user[field]+1;
bookshelf.knex('users')
.where('users.id', user.id)
.update(updatedUser).then(function(){
});
});
}

With your code bookshelf.knex('users') you leave the "Bookshelf world" and are in "raw knex world". Knex alone doesn't know about your Bookshelf wrapper objects.
You may use Bookshelf query method to get the best of both worlds.
Assuming your model class is User, your example would look approximately like
User.query(function(qb) {
qb.join('signed_in', 'signed_in.studentId', 'users.id')
.where('signed_in.signedIn', false);
})
.fetchAll()
.then(function(bookshelfUserObjects) {
/*mark absent*/
return bookshelfUserObjects.invokeThen('save'); // <1>
});
<1> invokeThen: Call model method on each instance in collection

Related

how to get data from existing mongo collection using parse server?

I am new to Parse Server.
I am having an existing collection "users" in "employee" db in Mongodb.
I need to get the users data using Parse Server.
Below is the code:
var query = new Parse.Query(users);
query.find().then((data) => {
return data;
}).catch((error) => {
return error;
});
But I am getting the error "users" is not defined.
Need some valuable help.
if your Class really is called users (which is different to the built-in Parse Server Class called User), then use :
var query = new Parse.Query('users'); //note the quotation around 'users'
If you are in fact trying to query the built-in User class, use :
var query = new Parse.Query(Parse.User);
or
var query = new Parse.Query('_User');

Update an already tracked entities

I want to update the itemsToUpdate collection.
This collection is already used in a query thus the resulting entities are already tracked in the context local property.
What is the most efficient way of overriding properties of the context.items.Local property from the itemsToUpdate collection?
private async Task<IEnumerable<item>> GetitemsAsync(IEnumerable<item> itemIds)
{
return await context.items.Where(t => itemIds.Select(x => x.Id).Contains(t.Id)).ToListAsync();
}
public async Task Update(...)
{
// Update
var queryUpdateitems = await GetitemsAsync(itemsToUpdate);
bool canUpdate = queryUpdateitems.All(t => t.UserId == userId);
if (!canUpdate)
{
throw new NotAuthorizedException();
}
else
{
// update here the itemsToUpdate collection
}
context.SaveChangesAsync();
}
In your case, you know that you have to update all these items, you just want to make sure that current user can update all items (by comparing Item.UserId). Instead of fetching all the existing items from database to make the check, you can query database to give result of the check and then you can just send update to database if check is true.
var itemIds = itemsToUpdate.Select(x => x.Id).ToList();
var canUpdate = await db.Blogs.Where(b => itemIds.Contains(b.Id)).AllAsync(t => t.UserId == userId);
if (canUpdate)
{
db.UpdateRange(itemsToUpdate);
}
else
{
throw new NotSupportedException();
}
await db.SaveChangesAsync();
Here, you have to make list of itemIds first because EF cannot inline list of items in a query and will do evaluation on client otherwise. That means EF is fetching whole table. Same is true for your GetitemsAsync method. It also queries whole table. Consider creating itemIds locally in that method too.
Once you pass in List<int> in the method EF will be happy to inline it in query and for query of canUpdate it will sent single query to database and fetch just true/false from database. Then you can use UpdateRange directly since there are nomore tracking records. Since it does not fetch all items from database, it will be faster too.

create personal collections for logged in users

import {favRestaurants} from '/lib/collections';
import {Meteor} from 'meteor/meteor';
import {check} from 'meteor/check';
export default function () {
Meteor.methods({
'favRestaurants.create' (id, name, rating, priceLevel, type) {
check(id, String);
check(name, String);
check(rating, Number);
check(priceLevel, Number);
check(type, String);
const createdAt = new Date();
const restaurant = {id, name, rating, priceLevel, type, createdAt};
if(check(Meteor.user()) == null){
console.log('onlye logged in users can data');
}else{
FavRestaurants.insert(restaurant);
}
}
});
}
This is my insert method for adding data to the restaurants collections. When i console log the 'check(Meteor.user())' in the console i get null as output. By that logic you shouldn't be able to add data to the collection, although this is still possible.
I would also like to make the FavResaurants collection personal for each user. Iv'e tried to check if there is a user and then adding a collection in the main.js file on the client side.
Meteor.loggingIn(() => {
console.log('check for user method');
var restId = 0;
if(Meteor.user() != null){
console.log('created new collection for the user');
const FavRestaurants = new Mongo.Collection('favRestaurants' + restId);
}
restId++;
});
I dont get any output to console using this method, which i found in the meteor docs. Is the code in the wrong place? Any help is much appriciated.
According to the docs the Accounts.ui.config is the method i should use. But I'm not sure in code i should put it. So far the placement of this method has resulted in my application crashing.
Answering your first question, to allow only logged-in clients to access a method, you should use something like:
if (!Meteor.userId()) {
throw new Meteor.Error('403', 'Forbidden');
}
Now, I see you want a collection to store favorite restaurants for each user in client side. But as I see it, there'd be only one logged in user per client, so you don't need a separate collection for each user (as the collection is in each client), you can just refer the user with it's id, and then fetch a user's favorite restaurants by a query like:
FavRestaurants.find({user: Meteor.userId()});
Moreover, as the docs suggest, Meteor.loggingIn is a method which tells you if some user is in the process of logging in. What you are doing is over-riding it, which doesn't make sense.
You should do something like:
if (Meteor.loggingIn()) {
// Do your stuff
}
Hope it gives you more clarity.
Creating a collection per user is a bad approach.
Define your favRestaurants collection once and add a owner field in the restaurant document before insert.
Create a publish method to publish to client side the userid favrestaurant only.
One more thing, check your userid first in your Meteor method, it will avoid unnecessary proces.
Regs

ReferenceError: db is not defined while trying to find distinct entries in database

I am getting db is not defined when trying to use mongodb's distinct in meteor.
Template.displayinbox.helpers({
inboxlistings: function() {
itemscount = db.Messages.distinct( "fromUsername" ).count;
return db.Messages.distinct( "fromUsername" );
}
});
I want to be able to return only distinct documents in my collection from the username field and count all those documents that is posted by the fromUsername. How would I go about doing this in Meteor?
When you're querying anything in the Meteor code itself, you don't need to write db first. You have to use the variable name that used to instantiate the Mongo Object. Let's say you defined your Mongo db like this.
example = new Mongo.Collection('Messages');
then within your helper you just use the typical query using this object.
Template.displayinbox.helpers({
inboxlistings: function() {
var items = example.find();
return _uniq(items,function(i){return i.fromUserName;});
}
});

bookshelf js save() command not updating rows in postgresql db

JS beginner trying to get a PostgreSQL DB talking to express.js through bookshelf.js.
github: https://github.com/duskyshelf/bookers-academy/blob/master/booker.js
var knex = require('knex')({
client: 'pg',
connection: "postgres://localhost/bookers"
});
var bookshelf = require('bookshelf')(knex);
var User = bookshelf.Model.extend({
tableName: 'users'
});
var bob = new User({id: 2});
bob.save()
bookshelf.js seems unable to add any content to the db.
Current error message is: "Unhandled rejection CustomError: No Rows Updated'
When you create your model providing your own id, like in
var bob = new User({id: 2});
Bookshelf assumes it is an update operation, not an insertion. It sets the internal isNew attribute to false, and when save() is invoked, instead of INSERT INTO user(id, ...) VALUES (2, ...);, it executes UPDATE user ... WHERE id = 2;.
If there is no user with id = 2 the update will almost silently DO NOTHING.
To force an insert you must change the save() to:
bob.save(null, {method: 'insert'});
Bookshelf save() documentation describes this behavior.
Did you create a User table using knex? One potential problem I can think of is that you do not have any logic that actually creates the table for your Postgres DB. Here is some sample code that will create a table in your database if it doesn't yet exist.
bookshelf.knex.schema.hasTable('User').then(function(exists) {
if(!exists) {
bookshelf.knex.schema.createTable('User'), function(user) {
user.increments('id').primary();
user.timestamps();
}).then(function(table){
console.log('Created Table:', table);
});
}
});
new User({id: 2}).
save().
then((model) => {
res.json({ success: true });
});
No no! new User returns a PROMISE! You can't use it like that.
Try
new User().save()