Merge activities in activity feed - php

I'm building an app with activity feed, just like Facebook or Instagram. When for instance a user uploads a picture, this picture automatically creates an entry in activities table. There is a polymorphic relationship between the picture uploaded and the activity. The activity model will handle follows, likes and other things. The Activity model looks like that
Activity
model_id 12
model_type picture
user_id 1313
activity_type picture_upload
If the user decides to upload again another picture in a short period of time, I would like to merge these similar activities related to the same model type into one just like Instagram does.
This activity would have a relation with both pictures uploaded.
For this I created an additional field in activities table called multiple_ids, and there I was inserting the consequent picture id's
Activity
model_id 12
model_type picture
user_id 1313
activity_type picture_upload
multiple_ids 13,14,15
This is completely working, I created methods for the activity in order to get those "multiple models". But for example if the user decides to remove a picture included in the multiple_ids field I would have to make a LIKE query in multiple_ids. I think that there should be better ways of doing this. How would you do that? A scope merging "similar activities" when retrieving activities would be a good practice?

Personally, I'd have a cross-reference table that simply stores the relationship of ID's.
parent | related
----------------
12 | 13
12 | 14
12 | 15
Then if the user deleted 13, 14 or 15, you'd simply delete this relationship from the table.
The tricky part comes if someone deletes 12. Here, you'll have to make a decision on how you want to handle it. You could delete all with a parent of 12, essentially levaing you with orphan activities. OR, you could with a little bit of work assosicate 14 and 15 with activity 13 instead.

I think its good idea to generate group id for activity, and if activity repeats you could assign same group id to it.
Activity
model_id 12
model_type picture
user_id 1313
activity_type picture_upload
group_id picture_upload_1313_14505049334
created_at 2017-02-01 11:55:00
When activity is creating, you could subscribe on event and check if in last five minutes this user did this activity - then you assign same group id
it could look like this on Laravel 4
Activity::creating(function($activity) {
$originatingActivity = Activity::where('created_at', '>', Date::subMinutes(5))->where('activity_type', $activity->activity_type)->where('user_id',$activity->user_id)->first();
if ($originatingActivity) {
$activity->group_id = $originatingActivity->group_id;
}
else {
$activity->group_id = $activity->activity_type . $activity->user_id . time()
}
})
And then in code you could get activities by group_id, so if you will delete one activity it doesn't break anything, you can write methods with groupBy statement to easily count them.
Also this way, with some code changes, you can group different relation types if needed

Related

Storing related data from another table

I have a forum website that I'm building myself. I'm planing to add group section into my website.
Users will be able to create groups. And manage by themselves. and then the creator should add moderators to the group.
How should I store this moderators? Should I create new table like this:
group_moderators
ID - GroupID - UserID
or should I directly insert into group table
ID - GroupName - Moderaters
1 - Tech - 5, 7, 9 (These are User IDs) then I can separate them with PHP.
It depends on your plan:
if you are planning to have multiple moderators per group then you
have to create a new table for moderators
if you are planning for only 1 moderator for each group then you can add a new column to the group table
UPDATE
Multiple IDs in 1 field is not a good idea at all, it will cause a lot of headache if you want to select, update, join, delete moderators.
First option because it'll be easier to delete or update the mods and it''ll be eaiser to update the table if you intend to give different powers to different mods in future ..
For Ex
ID - GroupID - UserID - Power
1 14 1 Mod
2 14 3 Super-Mod

How to structure a user activity table in MySQL?

I'm currently having a single activity table that references other tables depending on the type of activity.
id | type | user_id | source_id | misc_id | date
The type column tells me what kind of activity the user has performed (follow, liked, befriend, status etc.) and the source id contains the table id relative to the type of action.
This is working well for a user activity stream, but the only problem is, I can't figure out what to do about rows that no longer exist in the relative tables?
E.g. a user creates a status and then deletes it, or a user becomes friends with somebody that is later deleted from the database.
If the activity was relative to a single type, then I would be able to add a foreign key constraint, which would remove the row; but as it's relative to different tables, how else could I go about doing this?
You will have to take either of these approaches.
When the user deletes, just do the soft delete on the backend by marking them as deleted instead of hard deleting from the table. You will have to introduce a new column "delete_flag" in this approach.
Archive the tables and move the records to a different table when deleted. But this would be complex coding wise as well as the performance might not be as expected.
Here are my thoughts.
If users can delete something, you can record this also in your activity table.
I you want to hide that activity, you can add ReversedOn field and update it with the relevant date. Then you will just have to filter out activities that don't exist.
If that does not cause any user experience problems, then you can just let it be.

School management MySQL database design

I'm designing a database for a school management system and am facing couple of problems and I thought of throwing it here and someone might help.
I've a STUDENTS table that holds students details and CLASS table that holds class information. In the application one will need to know to which class a student should be promoted if he or she passes the exam. So the class table looks like this in my design:
+----------------------+
|id | name | parent_id |
+----------------------+
where the parent_id is the id of the previous class. Now each class has more than one stream (e.g., class form 1 can have 3 streams: form 1A, form 1B, form 2B etc) and each stream has students say form 1A has 40 students and so forth. So i have a stream table with this design:
+---------------------------------------------+
| id | student_id | class_id | stream_name |
+---------------------------------------------+
so for each stream with 40 students I will have 40 rows in the stream table and the stream name will contain the name like A, B, C or whatever the user wants it to be named. Is this the best design regarding my problem? Will this design affect performance of the system in any way? What is the best database design approach with the problem in question?
STUDENTS table carries information about the student like their name and parents information.
EDIT: The stream table is updated every-time a students is registered or when the new academic year has been registered for-instance if i were in stream A of form 1 then all the student in that class (form 1) should be promoted to class form 1 retaining their stream so if i was in form 1A then the next academic year i will be in form 2A. There is an ACADEMIC_YEAR table that holds the information of academic year such as when does it start and end and all sort of information, also there is a EXAM_RESULTS table which stores results for each student in particular stream in an academic year. For records i will need to know all stream and classes that the student has studied.
Thanks in advance
Your approach is on the right track. I would like to recommend the following:
a) Add a start date and end date to the stream table so that you can calculate which period the student was in the stream (will be able to handle students who start mid way the year or who leave before the year ends). This can also handle students who repeat because they will be in the same class for a new year
b) In the exam results table, add stream_id, class_id, student_id fields so that you can associate the exam with a class, student and stream. There seems to be a duplication to have the stream_id in addition to student_id and class_id but from experience it speeds up queries when you do not have to join to the stream to find out which class and student the exams belong to.

Aggregating many user created items into one "general" item

I'm building a site where users can inventory items and apply various attributes to it, eg. photos, urls, comments, etc.
I have a database structure of three tables:
users, entries, associations.
The tables have the following fields:
users
id | joined | email | salt | password
entries
id | created | creator | type | value
associations
id | created | creator | type | node1 | node2
Here's a breakdown of the site function:
Users adding items to their inventory
All user-created items go in entries with a type of 'item'. A row is added to associations with type 'possession', node1 users.id and node2 entries.id. This associations row is how I would (using INNER JOIN entries) pull and display a user's inventory (not just pulling all entries where creator = users.id, because a user may create an item they don't own).
Adding attributes to items
This part is what seems to throw off everyone I explain things to. An "attribute" is really just another item. In this way, it basically renders a user-created free-form hierarchy. E.g., You may 'tag' For Whom The Bell Tolls with "Book", and Book is another item (whether or not it's in the user's inventory matters not). To makes this work, I just add another row to the associations table with type 'tag' and node1 entries.id (parent item) and node2 entries.id ('tag' or child item). Remember than an entry may also be a url, comment, photo, etc, it would just depend on entries.type. Now I can pull all an items attributes. Eg, all photos: ($item_id = page I'm looking at) "SELECT * FROM entries INNER JOIN associations ON associations.node1 = $item_id AND associations.node2 = entries.id AND entries.type = 'photo'.
I can use a similar query to pull all an item's comments, it's url, whatever. This allows me to create a fluid system of associations between items, items and their owners, items and comments, comments and comments (replies).
My question is, once I have many user created entries of an item eg., "MacBook", what would be the best way to merge, aggregate, amalgamate or however else you like to call it, all those individual items into one general item, so that all these pieces of data created by users can be one knowledge chunk, if you will.
Again, I'm not so worried about users entering "mac book" "Apple Macbook" etc. En masse, those users are just doing it wrong and won't effect the community.
Basically, if a user that didn't own "MacBook" did a search and landed on the MacBook page, they would see the most popular tags, some photos (random, popular, whatever, that's trivial), comments about it, most popular URL, etc.
Also, thanks so much for taking time to read my confusing and elaborate description! :)
Have a table called "Tags", which would have a unique Name field. Whenever a user enters a new Name, it's added there.
Every Item should be linked to that table. This sounds like a many items to one tag arrangement, so you wouldn't need an Item_Tags table, just a foreign key Tag_Id in the Items table.
For Comments, you just have a Comments table which links to that table.
To display only 4 photos, you do a SELECT of photos that are joined to that name (presumably, they're photos of Items that have that Name) and LIMIT 4
For other/similar design patterns, do a search for questions related to tags, like this one.

Querying data for a Facebook-like news feed

I have a social networking site where users update their moods, profiles and add photos.
I'm currently logging all updates in a table called "update_log" with the following structure:
update_id int (auto),
userid int,
update_type int (1=profile, 2=photo, 3=mood)
pictureid int
mood_a int
mood_b int
mood_c int
update_time int
Profile update record:
(auto), 1, 1, 0, 0, 0, 0, 1239003781
Photo update record:
(auto), 1, 2, 11544, 0, 0, 0, 1239003781
Mood update record:
(auto), 1, 3, 0, 1, 490, 70, 1239003781
For the photo record, there's a corresponding table userphotos which holds the caption and filename/location data
For moods, there is a mood lookup table that holds the mood descriptions (i.e., I'm lazy =\ )
What I need to do is query this data to show on a user's profile page, it will show this feed for any of their favorite users for the last x hours of activity.
The problem I'm running into is that if a user uploads five photos over the course of a half hour or something, I just want that to be one line in the feed, not an entry for each photo upload.
Same goes for profile updates.
I need to query the data so the user will see something like this:
user x updated their mood! (I'm tired) on Apr 4, 2009 10:35 pm
user y uploaded x new photos on April 4, 2009 10:20 pm
user x updated their profile on April 4, 2009 10:15 pm
How do I group the photo updates into one record returned in a query based on all records being within let's say an hour of each other?
Is there a way to do this with one query?
Thanks!
You want something like
SELECT * FROM update_log WHERE update_time > NOW() - 30 MINUTES;
With 30 minutes being the period of time you're looking back.
I'm assuming you just needed to know how to return in a single query the updates of the last 30 minutes.
If you're trying to group all of the photos together into 30 minute blocks, say for the last two days, you'd be better off changing your database structure and creating a photo_group table [containing a primary key, userid, and time of creation] and adding a group_id column to the update_log table.
When adding a new photo, check for an existing group created by that user in the last 30 minutes. SELECT * FROM photo_group WHERE user_id = XXX AND created > NOW () - 30 MINUTES;
If one does not exist, create it. Link the photos to the newest by adding the primary key of the photo_group table as the group_id in the update_log.
When you retrieve rows later, you can group them by group_id using your scripting language.
The disadvantage of this method is your grouping structure will be difficult to modify later, as previous entries will be grouped by their old groups when you change the rules for creating new groups.
If you want to do this without storing the groups, you'll have to handle the logic in your scripting language by grouping the photos together in a loop that checks the creation time of a photo, groups following photos in an array with it if they have been created within a specific time period, or restarts the loop, using the most recent photo that did not fit with the previous. This would be more overhead than adding a new table, but it would be easier to modify later.
Have you considered trying to do this with PHP rather than SQL queries? It might be less complex to query the results you need (All updates between these times) and then use PHP to compare the timestamps in order to determine how they should be grouped.

Categories