Im working on some sort of social network in which people are able to make posts about a topic and like them.
Im having trouble tracking user likes.
The schema is the following:
Users:
{ userId: "someId", likes: ["idPost1", "idPost4", ...] }
Posts:
{ postId: "someId", topic: "idTopic", postContent: "someContent"}
I need a query that can:
Take all posts from a certain topic, like this:
r.table('posts').filter({
topic: idTopic
}).run().then( posts => res.json(posts))
Look up to see if the current user (given by the user id) has liked any of the posts on that specific topic. Then return a JSON with all posts on that topic, and those liked by the user with "liked: true".
Im having trouble with step 2,
Please let me know if im modelling data the wrong way, or if you can think of any way I can accomplish step 2.
Thanks!
This function does what you're looking for in javascript. (Tested using the Data Explorer)
r.db("test").table('posts').filter({ topic: "idTopic" }).merge(function(post){
return {liked: r.table('users').filter({userId: "someId"})(0)('likes').contains(post("postId")) }; })
The merge function basically iterates every post and adds the field liked to it. The contains function checks whether the posts id is in the likes array and return in directly as truth value.
Related
I am finding data from an array. that is a course and I want to find out an article by using the title, which is stored into the Course's Articles. I am going step by step...
(1) I want fine the course by the name,
(2) after of it, I want to find an article into course's article by using the title of an article
(3) and render the article and course to the web page
here is code
const course = req.params.course
const article_title = req.query.article
Course.findOne({name: course , article:article_title}).then(function(data){
console.log(data)
res.render('article',{data :data})
}).catch((err)=>{console.log(err)});
here is DB
_id:61c057cfd70f2fb178d4e996
name:"Soft "
discription:"House"
article:Array
0:Object
title:"Where does it come from?"
content:"<p>Title is very important for an article to explain content | article..."
_id:61c05d4a3905f61f72a8e61b
1 :Object
2:Object
To search you can use:
Couse.findOne({name: course, 'article.title': article_title})
So you will get the document with the articles you need.
Using projection and $elemMatch, you can filter the article array itself so that only the subdocument you want is there:
Couse.findOne({name: course, 'article.title': article_title}, {
article: {
$elemMatch: {
title: article_title
}
}
})
You can try it in MongoPlayground
https://mongoplayground.net/p/SVXNGE6tuW7
I'm trying to get a list of posts from people a user follows ordered by how recent the post was posted. So no matter who posted, as long as the user is following them, the most recent posts from that collection of users would be seen first. I've tried this (it's not completely correct, just trying to get concept down):
// get all the users you are following -- this will count for a lot of reads if they follow 3000 people
const following = await db
.collection('users')
.doc(userHandle)
.collection('following')
.get()
// get the first 10 posts from those users ordered by recently posted
const promises = following.map((doc) => {
return db
.collection('posts')
.orderBy('createdAt', 'desc')
.where('userHandle', '==', doc.data().userHandle)
.limit(10)
.get()
.then(async (data) => {
return data.docs.map((doc) => {
return {
postId: doc.id,
userHandle: doc.data().userHandle,
userImageUrl: doc.data().userImageUrl,
imageUrl: doc.data().imageUrl,
likeCount: doc.data().likeCount,
};
})
})
});
Promise.all(promises)
.then((posts) => {
res.json(posts);
})
Problem with the above concept... this will return a lot of posts if the user follows a bunch of users. That limit is only for the amount of posts for one user that can be returned on that page. Also it would return 10 posts from one user recent to oldest, then 10 posts from the next user recent to oldest even though there has been more recent posts. I was thinking of maybe adding a counter where if the amount of posts returned is more than 10, stop the function and just return those 10, but I was having problems with promising before where the function returns null so that's why i return everything when i'm done looping using promise.all. Will this work? That may solve the limit problem, but not getting the absolute most recent post from a collection of the users that the current user is following. I wish firestore had a large query where I can just get all the recent posts that have a username that matches to one of the usernames in a following array (which could be returned from the following function at the top of the above code). Pretty sure I'm only allowed to check 10 values though if I just converted the username field to an array.
So, if I'm understanding correctly, you want to get posts from people whom the user follows, and you want to get them sorted by time. Then, I would suggest you do this :
First, save the following list of your user in an array , like following: ['celeb1','celeb2'].
Then save all the posts in a collection, which has documents like
{
content: 'some content',
author: 'celeb1',
time: 1598681888 //timestamp or whatever you like
//some more props
}
And when you want to get posts for your user, just do
//Get the array contains all folloing people
let following = await db.collection('users').doc(userHandle).get();
//Get the first 10 posts from following people
let posts = await db.collection('posts').where('author', 'in',
following.data().following).limit(10).orderBy('time', 'desc').get();
Check this for more on in query.
I'm trying to make a messaging system that writes each message to a mongo entry. I'd like the message entry to reflect the user that sends the message, and the actual message content. This is the message schema:
const MessageSchema = new Schema({
id: {
type: String,
required: true
},
messages: {
type: Array,
required: true
},
date: {
type: Date,
default: Date.now
}
});
And this is where I either create a new entry, or append to an existing one:
Message.findOne({ id: chatId }).then(message => {
if(message){
Message.update.push({ messages: { 'name': user.name, 'message': user.message } })
} else {
const newMessage = new Message(
{ id: chatId },
{ push: { messages: { 'name': user.name, 'message': user.message } } }
)
newMessage
.save()
.catch(err => console.log(err))
}
})
I'd like the end result to look something like this:
id: '12345'
messages: [
{name: 'David', message: 'message from David'},
{name: 'Jason', message: 'message from Jason'},
etc.
]
Is something like this possible, and if so, any suggestions on how to get this to work?
This questions contains lots of topics (in my mind at least). I really want to try to break this questions to its core components:
Design
As David noted (first comment) there is a design problem here - an ever-growing array as a sub document is not ideal (please refer to this blog post for more details).
On the over hand - when we imagine how a separate collection of messages will looks like, it will be something like this:
_id: ObjectId('...') // how do I identify the message
channel_id: 'cn247f9' // the message belong to a private chat or a group
user_id: 1234 // which user posted this message
message: 'hello or something' // the message itself
Which is also not that great because we are repeating the channel and user ids as a function of time. This is why the bucket pattern is used
So... what is the "best" approach here?
Concept
The most relevant question right now is - "which features and loads this chat is suppose to support?". I mean, many chats are only support messages display without any further complexity (like searching inside a message). Keeping that in mind, there is a chance that we store in our database an information that is practically irrelevant.
This is (almost) like storing a binary data (such an image) inside our db. we can do this, but with no actual good reason. So, if we are not going to support a full-text search inside our messages, there is no point to store the messages inside our db.. at all
But.. what if we want to support a full-text search? well - who said that we need to give this task to our database? we can easily download messages (using pagination) and make the search operation on the client side itself (while keyword not found, download previous page and search it), taking the loads out of our database!
So.. it seems like that messages are not ideal for storage in database in terms of size, functionality and loads (you may consider this conclusion as a shocking one)
ReDesign
Using a hybrid approach where messages are stored in a separated collection with pagination (the bucket pattern supports this as described here)
Store messages outside your database (since your are using Node.js you may consider using chunk store), keeping only a reference to them in the database itself
Set your page with a size relevant to your application needs and also with calculated fields (for instances: number of current messages in page) to ease database loads as much as possible
Schema
channels:
_id: ObjectId
pageIndex: Int32
isLastPage: Boolean
// The number of items here should not exceed page size
// when it does - a new document will be created with incremental pageIndex value
// suggestion: update previous page isLastPage field to ease querying of next page
messages:
[
{ userId: ObjectID, link: string, timestamp: Date }
]
messagesCount: Int32
Final Conclusion
I know - it seems like a complete overkill for such a "simple" question, but - Dawid Esterhuizen convinced me that designing your database to support your future loads from the very beginning is crucial and always better than simplifying db design too much
The bottom line is that the question "which features and loads this chat is suppose to support?" is still need to be answered if you intend to desgin your db efficiently (e.g. to find the Goldilocks zone where your design suits your application needs in the most optimal way)
My post document looks like the following:
{
_id: ...,
type: 'post',
title: ...,
description: ...,
author: 'user_id'
}
And another user document:
{
_id: 'user_id',
type: 'user',
name: ...,
}
How do I fetch the post and the linked user document given that I only know post id?
Having user document inside the post document doesn't seems like a good solution as if the user changes his/her name or other details, I will have to update every post.
Another solution would be to include a posts array in the user document and use two emits in the view document. But with frequent posts and high number of posts, this looks a little inefficient.
You mentioned "linked documents" as if you were referencing this feature in CouchDB, but it doesn't appear like you meant it that way.
It turns out, this is totally supported. Your document structure doesn't need to change at all, you can use a map function like this:
function (doc) {
if (doc.type === 'post') {
emit(doc._id)
emit(doc._id, { _id: doc.author })
}
}
By emitting an object with an _id property as the value, it allows CouchDB to look up a different document (in this case, the user document) than the original when you add include_docs=true on your view query. This allows you to fetch an entire collection of related documents in a single query! I'd reference the documentation I linked to earlier for a complete example. (the rest of their docs are great too!)
I have a product which is owned by a user in my CouchDB.
product =
name: 'Laptop'
userId: somelongid
user =
username: 'James'
With views and include_docs=true it returns:
product =
name: 'Laptop'
user =
username: 'James'
( I know it doesn't exactly return the above but it's close enough )
I do this cause every time I need a product I also need the owner (to link to his page). At first I thought I would just use include_document=true on the _change feed but of course that does something else.
So how can I get the related user when getting product results?
One solution is to collect all the userIds from the search result and query the _all_docs view in couchDb to get the users.
use a view (f.E. "userByDocId") that emits (doc._id,doc.user)
and do a query userByDocId?key="Username"