Which is the better way of relating these database tables? - postgresql

I have a table Users and Matters. Ordinarily only admins can create a matter so I did a one to many relationship between users and matters(User.hasMany(Matter)) and (Matter.belongsTo(User))
Now on the frontend, Matter is supposed to have a multi-select field called assignees where users gotten from the User table can be selected.
My current approach is to make assignees a column on Matter which will be an array of user emails selected on the frontend but the frontend developer thinks I should make it an array of user ids instead but I think that won't be efficient because when getting all matters or updating them, one will need to run a query each time to get the associated assignees using the array of ids stored in the assignees column(and I am not entirely sure on how to go about that).
Another option is having a UserMatters join table but I don't think it will be performant-friendly to populate two tables(Matter and UserMatters) on creation of a matter while updating and getting all matters will involve writing lots of code.
My question is, is there a better way to go about this or should I just stick with populating the assignees field with user emails since it looks like a better approach as far as I can see?
N.B: I am using sequelize(postgres)

So what I did was instead of creating a through/join table, the frontend sent an array of integers containing the IDs of the assignees. Since the assignees are also users on the app, when I get a particular matter, I just loop through the IDs in the assignees column which is an array and get the user details from the user table then I reassign the result to that column and return the resource.
try {
// get the resource
const resource = await getResource(id);
/*looping through the column containing an array of IDs and converting it to numbers(if they are strings)**/
let newArr = resource.assignees.map(id => Number(id));
let newAssignees;
/**fetch all users with the corresponding IDs and wait for that process to be complete**/
newAssignees = await Promise.all(newArr.map(id => getUserById(id)));
/**Returns only an array of objects(as per sequelize)**/
newAssignees.map(el => el.get({ raw: true }));
/**reassign the result**/
resource.assignees = newAssignees;
if(resource)
return res.status(200).json({
status: 200,
resource
})
} else {
return res.status(404).json({
status: 404,
message: 'Resource not found'
});
}
} catch(error){
return res.status(500).json({
status: 500,
err: error.message
})
}

Related

Parse DB/Mongo compound index with an OR and an Object

I am working on a project where the data is stored in a very weird shape that's causing the query for finding an user on first login attempt to TAKE A LONG TIME. I'm having a hard time wrapping my mind around how I can create an index for this. I want to find a single user. Any of these attributes can undefined, and I need to find a single user that match any of these (OR)
query.equalTo('firebaseUid', uid);
query.equalTo('emails.apple', email);
query.equalTo('emails.google', email);
query.equalTo('phoneNumber', phoneNumber);
query.equalTo('emails.facebook', email);
query.equalTo('emails.password', email);
Is this index correct or makes sense? I'm specifically lost on the fact that we are using find within an emails Object (Don't ask me why we are keeping them separate...). It's most likely an user has an UID than not (After first login). And from there I ordered from most likely login method based on user status (Mostly apple users, then google...)
db.userCollection.createIndex({firebaseUid: 1, emails.apple: 1, emails.google: 1, phoneNumber: 1, emails.facebook: 1, emails.password: 1})

How to design model for storing day wise timings?

I am designing a schema for doctor's appointments. The doctor will be given the option to update his/her timings for individual days of the month. Also there is no limit to months. For instance, the doctors will be able to update the timings for any days of future or current month. (Only previous dates will be disabled). The front end part has been done but what I fail to understand is how to create the mongo model for it. Of course I can not have dates for all months stored in the model. What is the method to address this problem? TIA
If it was me that had such project, I would start with 5 collections:
one for users (so you know who did what)
one for patients (recording all about the patient, including mobile number)
one for doctors (so you can show a list while registering time)
one for time registrations (with all details of the registration)
one for logging everything a user does
so you can go back in time and to know how did what... it's never to point the finger to the person that made the mistake but look at it as a very easy way to find out what it happened and how can you prevent it from happening again.
the content of each document is entirely up to you as that will change with exactly what and how you are doing
I would think about something between these lines:
// Users
{
username, hashedPassword, // credentials
permissions: [], // for when you start using permissions
active, // never delete data, just set the flag to "false", if, by GDPR rules you need to delete, you can change the name to "DELETED BY GDPR" and still maintain all references
}
// Patients
{
name,
address: { street, street2, city, zipcode, country }, // for invoicing proposes
mobile, // so you send them an SMS 24h before saying their appointment is "tomorrow"
}
// Doctors
{
name,
weekAvailability: [], // days of the week that a doctor is available as they normally work in more than one clinique
active, // never delete data, just set the flag to "false", if, by GDPR rules you need to delete, you can change the name to "DELETED BY GDPR" and still maintain all references
}
// Logs
{
action, // for example, "save", "add", "change"...
entity, // the collection name that the change happened
originalEntry, // the full document before changes
newEntry, // the full document after changes
timestamp, // the exact time of the change
user, // ref to users
}
// TimeRegistrations
{
user, // ref to users
patient, // ref to patients
doctor, // ref to doctors
title, description, appointmentStart, durationInMinutes,
}
regarding the infrastructure ... create an API (REST or GRAPHQL, the one you're most comfortable with) so you can separate the business logic from the frontend right from the start
your frontend (maybe React, Angular, VueJs) should call a proxy (nodeJs server running aside the frontend) to make authentications and call the API so all you should do in the frontend to be something like
fetch('/api/doctors')
.then(res => res.toJson())
.then(json => {
this.doctorsList = json
})
same as for authentication os a user where you can easily make use of a library to provide you with a JWT and easily maintain user logged in and with the right set of permissions
First approach but not good in your case i.e. One collection,
//doctors
{
_id: "",
appointments: [] // all appointments there
}
Second will be better but note in NoSql collection totally depends on how you want to get the data. Two collections:
//doctors
{
_id: "SOMETHING",
name: "SOMETHING"
}
//appointments
{
_id: "SOMETHING",
doctorId: "", // ref of doctor collection
appointmentAt: "",
appointmentAtMilli: "",
}

Issue failing to get multiple results in CK query where the same record may be returned more than once

I've got a table in CloudKit (UserCourse) which holds a reference to a user, a reference to a course and a variety of summary data for that User+Course combo (e.g., date starte, status, etc). A user could choose to take the same course multiple times.
In one view of my app, I show the count of courses a user has taken - this query works fine and shows the total count, including duplicate versions of a user+course combination.
I then have a subsequent view, where I want to list out all of the courses the logged in user has taken, with course details. My issue is that I'm only able to get a single copy of the Course that has been duplicated.
First I have a query that gets all records from the UserCourses table for that user. From the results, I get the references to all of the courses and pass that as an input to a subsequent query that attempts to get all the courses from CK so that I can display the view to the user of all the courses they have taken.
The issue is that I'm unable to get two copies of the same course (say one in progress and one completed, as an example). I've tried querying by recordName, but I understand that isn't possible. However, when I try to convert from recordName to Record ID (below) - I get different record IDs for the same record name and my query only returns one instance of the Course, not two.
recordIDs.append(CKRecord.ID(recordName: recordName))
and the relevant code below. Open to suggestions on how to address it. I'm guessing I'm going about it the wrong way and should be designing my queries differently!
func getCoursesByReference(for references: [CKRecord.Reference],
_ completion: #escaping ([Course]?,Error?) -> Void) {
var recordIDs: [CKRecord.ID] = []
for each in references { //loop through the references being passed in
// and because you can't query by recordName in cloudkit, then convert to record ID
var recordName = each.recordID.recordName
recordIDs.append(CKRecord.ID(recordName: recordName))
}
//my predicate now has two different record IDs for the same recordName and as such my results set is 1 item instead of two
var predicate = NSPredicate(format: "recordID IN %#", recordIDs)
let query = CKQuery(recordType: "Course", predicate: predicate)
....remainder of code

Use Firestore rules to limit list fails on array key

I'm trying to use Firestore rules to return only documents where the current user has some sort of rights, following the advice given in https://firebase.google.com/docs/firestore/solutions/role-based-access.
However, when I implement the rule I get the dreaded "[code=permission-denied]: Missing or insufficient permissions" error message which obviously tells me nothing, I was wondering if anyone can spot what is going wrong.
My rules:
//Specific project rules - authorised users who appear in the project list
match /documents/{document} {
function isSignedIn() {
return request.auth.uid != null;
}
function getUser(rsc) {
return rsc.data.users[request.auth.uid];
}
function isOneOfUsers(rsc, array) {
return isSignedIn() && (getUser(rsc) in array);
}
allow list: if isOneOfUsers(resource, ['user','admin']);
The data stores the users information in a field on the document (12345 in the example below. The field is of type Object which allows me to put a key (the userid, 76544 in the example below) and a value against it, such as "admin".
My data:
documents/12345/users{76544:"admin"}
Now when I log on and try to get a list of the documents, I'm expecting to see this document coming back, but I get the error. I can change the function getUser to return "user" and that works, so the problem is somewhere in the evaluation of
rsc.data.users[request.auth.uid]
I would normally accept that I'm trying something that can't be done but it is a near direct copy of the official docs so I must be missing something!
Thanks in advance for your help
Here's what I think is going on: LIST is explicitly not checking every document that it is picking up, rules are not filters etc.
When writing queries to retrieve documents, keep in mind that security
rules are not filters—queries are all or nothing. To save you time and
resources, Cloud Firestore evaluates a query against its potential
result set instead of the actual field values for all of your
documents. If a query could potentially return documents that the
client does not have permission to read, the entire request fails.
Therefore, LIST will always return the full list of /documents/, there is nothing you can write in there beyond authorisation rules that will stop it from returning all of them. If this is true, if any malicious actor gets hold of an authentication, they can download the full list of all of your /documents/.
The only sensible approach to this is to lock down any attempt to use LIST (deny all) and keep your list accessible /documents/ against an individual user instead. This seems onerous, but may be the only way of doing it.

How to save one value of Parse object without overwriting entire object?

I have two users accessing the same object. If userA saves without first fetching the object to refresh their version, data that userB has already successfully saved would be overwritten. Is there any way(perhaps cloud code?) to access and update one, and only one, data value of a PFObject?
I was thinking about pushing the save out to the cloud, refreshing the object once it gets there, updating the value in the cloud, and then saving it back. However that's a pain and still not without it's faults.
This seems easy enough, but to me was more difficult than it should have been. Intuitively, you should be able to filter out the fields you don't want in beforeSave. Indeed, this was the advice given in several posts on Parse.com. In my experience though, it would actually treat the filtering as deletions.
My goal was a bit different - I was trying to filter out a few fields and not only save a few fields, but translating to your context, you could try querying the existing matching record, and override the new object. You can't abort via response.failure(), and I don't know what would happen if you immediately save the existing record with the field of interest and null out the request.object property - you could experiment on your own with that:
Parse.Cloud.beforeSave("Foo", function(request, response) {
// check for master key if client is not end user etc (and option you may not need)
if (!request.master) {
var query = new Parse.Query("Foo");
query.get(request.object.id).then(function(existing) {
exiting.set("some_field", request.object.get("some_field"));
request.object = exiting; // haven't tried this, otherwise, set all fields from existing to new
response.success();
}, function(error) {
response.success();
});
}
});