How to you relate tags to messages and users in a messaging app? - mongodb

I am working on an app with a MongoDB database to send messages between two people and I want each user to create tags for each message. I want users to be able to add new tags or select tags from the list of the ones they've created. Most importantly, once you receive a message with a tag, I want that to be added to your list of tags. Does the following (truncated) data model make sense?
User:
ID: ID
Name: STRING
Tags: Array of TAG-IDs
Message:
ID: ID
Sender: USER-ID
Receiver: USER-ID
Tags: Array of TAG-IDs
Tag:
ID: ID
Label: STRING

I would suggest you put also the Label of Tags into the Tags Array. It can help you avoid an extra lookup when you are required to display the Label.
Beware the trade-off that you need to update all array entries' Label field when there is an update. This could matter depending on your actual scenario.
Here is a good article about MongoDB schema design for your reference. It has a similar example to your case.

Related

Relational or full object in MongoDB documents

I have a general MongoDB question as I have recently found an issue with how I store things.
Currently, there is a collection called spaces like this:
{
_id: 5e1c4689429a8a0decf16f69,
challengers: [
5dfa24dce9cbc0180fb60226,
5dfa26f46719311869ac1756,
5dfa270c6719311869ac1757
],
tasks: [],
owner: 5dfa24dce9cbc0180fb60226,
name: 'testSpace',
description: 'testSpace'
}
As you can see, this has a challengers array, in which we store the ID of the User.
Would it be okey, if instead of storing the ID, I would store the entire User object, minus fields such as password etc?
Or should I continue with this reference path of referring to the ID of other documents?
The problem I have with this, is that when I want to go through all the spaces that a user has, I want to see what members are a part of that space (challengers array). However, I receive the IDS instead of name and email obviously. I am therefore struggling with sending the correct data to the frontend (I have tried doing some manual manipulation without luck).
So, if I have to continue the path of reference, then I will need to solve my problem somehow.
If it is okey to store the entire object in the array, It would be a lot easier.
HOWEVER, I want to do what is the best practice.
Thank you everyone!

How to return only selected items from Pods Framework?

Just started using the PODS framework for Wordpress. Most of it I picked up pretty fast, the rest I'm struggling with. Below is an example of my confusion:
I created a custom post (Partner Bio) with auto-templates turned on.
I created a pods-template (Bio for Industry) that is sourcing post (Partner Bio).
I created another custom post (Industry) with multi-select post-type relationship field to post (Partner Bio).
The issue I'm having is that when I create an instance of post (Industry), then select specific items from that relationship (Partner Bio), (say there are 10, but I chose 3), I'm getting data returned for all of Partner Bio's instead of just the three I chose, so in other words the template pulling data from all Partner Bios regardless of what I choose in the post field drop down.
The template I'm using looks like this:
<div>{#post_thumbnail.thumbnail}<br/>
{#first_name} {#middle_initial} {#last_name}<br/>
{#title_of_partner}<br/>
{#position}<br/>
{#phone}<br/>
Email {#first_name}
</div>
This made me think that when you embed a pod usinge a shortcode, it's not being controlled by the fields selected when you edit your post. So then I tried grabbign the code from the template and then just dropping it in the post, that did not work either.
My goal is to display the block listed above one under another, based on which bios I choose. What am I doing wrong?
If my example above is confusing, here's a simpler sample illustrating the same issue, this maybe more clear:
Post A structure/fields:
Name,
Email,
Rel, // Post A's Relationship via post-type to Post B,
Auto templates: on,
Pods Template created: {#name}{#Emal}{#Image}
The issue, is when I create an instance of Post A and select items from the Rel, I'm getting values pulled in from all members of that relationship rather than just what I checked off.
If I turn off auto templates and drop in magic tags into the instance of Post A, I do get back only data for the relationship items I chose but I ge it back in this format:
Name and Name and Name
Emaile and Email and Email
Rather than:
Name
Email
Name
Email
...

Ordering Firebase posts Chronologically Swift

I have added posts to firebase and I am wondering how I can pull the posts chronologically based on when the user has posted them.
My Database is set up like below
The first node after comments is the User ID and then the posts are underneath that. Obviously, these posts are in order, however if a new user posts something in between "posting" and "another 1" ,for example, how would I pull that so it shows up in between.
Is there a way to remove the autoID and just use the userID as a key? The problem I am running into is the previous post is overwritten then.
I am accepting the answer as it is the most thorough. What I did to solve my problem was just create the unique key as the first node and then use the UID as a child and the comment as a child. Then I pull the unique key's as they are in order and find the comment associated with the uid.
The other answers all have merit but a more complete solution includes timestamping the post and denormalizing your data so it can be queried (assuming it would be queried at some point). In Firebase, flatter is better.
posts
post_0
title: "Posts And Posting"
msg: "I think there should be more posts about posting"
by_uid: "uid_0"
timestamp: "20171030105500"
inv_timestamp: "-20171030105500"
uid_time: "uid_0_ 20171030105500"
uid_inv_time: "uid_0_-20171030105500"
comments:
comment_0
for_post: "post_0"
text: "Yeah, posts about posting are informative"
by_uid: "uid_1"
timestamp: "20171030105700"
inv_timestamp: "-20171030105700"
uid_time: "uid_1_20171030105700"
uid_inv_time: "uid_1_-20171030105700"
comment_1
for_post: "post_0"
text: "Noooo mooooore posts, please"
by_uid: "uid_2"
timestamp: "20171030110300"
inv_timestamp: "-20171030110300"
uid_time: "uid_2_20171030110300"
uid_inv_time: "uid_2_-20171030110300"
With this structure we can
get posts and their comments and order them ascending or descending
query for all posts within the last week
all comments or posts made by a user
all comments or posts made by a user within a date range (tricky, huh)
I threw a couple of other key: value pairs in there to round it out a bit: compound values, query-ing ascending and descending, timestamp.
You can not use the userID as key value instead of the autoID, because the key must be unique, thats why Firebase just updates the value and does not add another one with the same key. Normally Firebase nodes are ordered chronologically by default, so if you pull the values, those should be in the right order. However if you wanna make sure about that, you can add a timestamp value and set a server timestamp. After pulling the data you can order it by that timestamp (I think there is actually a timestamp saved automatically by firebase that you can access somehow, but you need to look that up in the documentation). If I got it right, in order to accomplish what you want, you need to change the structure of your database. For example you could maybe use the autoID but save the userID you wanted to use as key as a value if you need that. Hope I got your idea right, if not just be more precise and I will try to help.
Firebase keys are chronological by default - it's built into their key generation algorithm. I think you need to restructure/rethink your data.
Your POSTS database should (possibly) have the comments listed with each post, and then you can duplicate on the user record if needed for faster retrieval if they need to be accessed by user. So something like:
POSTS
- post (unique key)
- title (text)
- date (timestamp)
- comments
- comment (unique key)
- text (text)
- user_id (user key)
- date (timestamp)
When you pull the comments, you shouldn't be pulling them from a bunch of different users. That could result it a lot of queries and a ton of load time. Instead, the comments could be added (chronologically of course) to the post object itself, and also to the user if you want to keep a reference there. Unlike in MySQL, NoSQL databases can have quite a bit of this data duplication.

Should a tag be it's own resource or a nested property?

I am at a crossroads deciding whether tags should be their own resource or a nested property of a note. This question touches a bit on RESTful design and database storage.
Context: I have a note resource. Users can have many notes. Each note can have many tags.
Functional Goals:
I need to create routes to do the following:
1) Fetch all user tags. Something like: GET /users/:id/tags
2) Delete tag(s) associated with a note.
3) Add tag to a specific note.
Data/Performance Goals
1) Fetching user tags should be fast. This is for the purpose of "autosuggest"/"autocomplete".
2) Prevent duplicates (as much as possible). I want tags to be reused as much as possible for the purpose of being able to query data by tag. For example, I'd like to mitigate scenarios where the user types a tag such as "superheroes" when the tag "superhero" already exists.
That being said, the way I see it, there are two approaches of storing tags on a note resource:
1) tags as nested property. For example:
type: 'notes',
attributes: {
id: '123456789',
body: '...',
tags: ['batman', 'superhero']
}
2) tags as their own resource. For example:
type: 'notes',
data: {
id: '123456789',
body: '...',
tags: [1,2,3] // <= Tag IDs instead of strings
}
Either one of the approaches above could work but I am looking for a solution that will allow scalability and data consistency (imagine a million notes and ten million tags). At this point, I am leaning toward option #1 since it is easier to cope with code wise but may not necessarily be the right option.
I am very interested in hearing some thoughts about the different approaches especially since I cannot find a similar questions on SO about this topic.
Update
Thank you for the answers. One of the most important things for me is identifying why using one over the other is advantageous. I'd like the answer to include somewhat of a pro/con list.
tl;dr
Considering your requirements, IMO you should store tags as resources and your API should return the notes with the tags as embedded properties.
Database design
Keep notes and tags as separate collections (or tables). Since you have many notes and many tags and considering the fact that the core functionality is dependent on searching/autocomplete on these tags, this will improve performance when searching for notes for particular tags. A very basic design can look like:
notes
{
'id': 101, // noteid
'title': 'Note title',
'body': 'Some note',
'tags': ['tag1', 'tag2', ...]
}
tags
{
'id': 'tag1', // tagid
'name': 'batman',
'description': 'the dark knight',
'related': ['tagx', 'tagy', ...],
'notes': [101, 103, ...]
}
You can use the related property to handle duplicates by replacing tagx, tagy by similar tags.
API Design
1. Fetching notes for user:
GET /users/{userid}/notes
Embed the tags within the notes object when you handle this route at backend. The notes object your API send should look something like this:
{
'id': 101,
'title': 'Note title',
'body': 'Some note',
'tags': ['batman'] // replacing the tag1 by its name from tag collection
}
2. Fetching tags for user:
GET /users/{userid}/tags
If it's not required, you can skip on sending the notes property which contains the id for your notes.
3. Deleting tags for notes:
DELETE /users/{userid}/{noteid}/{tag}
4. Adding tags for notes:
PUT /users/{userid}/{noteid}/{tag}
Addressing the performance issues, fetching tags for user should be fast because you have a separate collection for the same. Also, handling duplicates will be simpler because you can simply add the similar tags (by id or name) into the related array. Hope this was helpful.
Why not to keep tags as nested property
The design is not as scalable as the previous case. If the tags are nested property and a tag has to be edited or some information has to be added, then it will require changes in all the notes since multiple notes can contain the same tag. Whereas, keeping the tags as resources, the same notes will be mapped with their ids and a single change would be required in the tags collection/table.
Handling duplicate tags might not be as simple as when keeping them as separate resources.
When searching for tags you will need to search for all the tags embedded inside every note. This adds overhead.
The only advantage of using tags as nested property IMO is it'll make it easier to add or delete tags for a particular note.
It might be a little bit complicated. So I can just share my experience with Tag work (in our case, it was a main feature of VoIP App).
In any case all Tags will be as unique object, which contains a lot info. As you know it would be a more complicated for transferring, but you would need this information, for example below. And sure, Json it's fastest solution.
type: 'notes',
data: {
id: '123456789',
body: '...',
tags: [UUID1,UUID2,UUID3]
}
Just for example, how much of information you would needed. When you want to change color of tag, or size, based on Tag Rate, color based on number usage, linked (not same), duplicates, and so on.
type: 'tag',
data: {
uuid: '234-se-324',
body: 'superhero',
linked: [UUID3, UUID4]
rate: 4.6,
usage: 4323
duplicate: [superheros, suppahero]
}
As you can see, we use even duplicates. Just to save uniques of every Tag. Sure we also contain logic to filter the Words Root, but as you can see from example above, we also use duplicate value with special Roots, like "Superhero" and "Suppahero" which are same for us.
And you might think, this is a lot information for the "autosuggest" or "autocomplete", but we never faced performance issues (in case, if sever side support sanity). And all information is important for every usage, and Note in this case too.
Saving tags as nested property makes sense if you want to have all data in same row. Let me give you an example.
On invoice you add items,
Title, description, price, qty, tax, ...
tax in this case could be : VAT 20% so you calcualte invoice with 20%, but one day tax changes to 22% and all invoices that are saved on DB will be 2% more. In this case you add new column and you save it as raw number 20, and when you read that invoice from db you get all data from one row, instead of calculating it from different tables or variables.
Same thing is with tags. If you somehow want to merge duplicates, its easy to do it with IDs rather than strings.
Also there are some other factors that you might consider it.
in a social network, a user might have tags that are called skills, interests, sports, and more. There is no real way to differentiate between tags from (https://github.com/mbleigh/acts-as-taggable-on)
So if you are making tags that you will tag many things you have to use id

Parse Data Model for users tagging items

im trying to figure out a good data model approach when using parse (underlying mongodb) for users that are allowed to select a tag and provide a value to them, lets call it just a rating since thats the easiest to understand.
Im working on a class that is called user tags that has the following structure currently in its collection.
User (pointer to user class)
Object (pointer to object to tag)
Tags (array of tags with values)
The tags can be up to maybe 30 tags and each one of them can have a rating of 1-5 in this case...
I was wondering if I could do a PFRelationship in an array that has the objectId of the tag as the key and value as the 1 - 5 rating.. here is an example json object mocked up to what im saying.
{
"3q24afadfadf": 3.5 //parse relation object id : value,
"234rrdfadfk": 2.4 //parse relation object id : value,
"as4q2w34lsdf": 2.3 //parse relation object id : value
}
This way I can store one row for the item that the user tagged and all the tags with its rating value along with that.
I'm not sure this is the right way or if its scalable when doing queries for get me all users items he or she tagged, along with the tag (name) and the values).
I also on top of that need to figure out a way to when many users tag the same item with different values that I build up some analytics or maybe counter class that gets incremented or averaged in to then be displayed along with the item. I might try cloudcode to do saveafter to update the analytic data class for that item.
Anyhow Any thoughts on this model would be appreciated and most importantly need to be able to get at the data inside the tag array, with hopefully the key being a pointer, if not a pointer i'm up to suggestions because the result should return
Item A
Tag name 1 with value 4.5
Tag Name 2 with value 3.5
and so on..
...
Also if you have any pointers to how to build aggregated data of the item and its over all value that many users have tagged over time.. My thought as above is to have a analytic class that the cloud code increments or that the app then increments, the challenge is to load all the user tags of item x, and get the tag and value out of the array and then add them to the analytic class. I could run this at night since it doesn't have to be real time.