Insert to array if value is not present in more arrays - javascript

I am building a forum web app where user can like/dislike post of other users.
I have the following model, representing a Post:
[
{
_id:"0002253,
title:"My first post",
likes:[],
dislikes:[]
},
{
_id:"0002254,
title:"My second post",
likes:[],
dislikes:[]
},
{
_id:"0002255,
title:"My third post",
likes:[],
dislikes:[]
},
]
When a user sends a like or dislike, the latter's ID will be inserted into the corresponding array (like or dislike).
Each user can, for each post, insert only a like or a dislike (not both).
The query I used for the insert is the following:
let vote_result = await Poll.updateOne(
{
_id: poll_id,
$and: [
{ "likes": { $nin: [MY_ID] } },
{ "dislikes": { $nin: [MY_ID] }},
],
},
{
$addToSet: {
"likes": MY_ID,
},
},
So, theoretically, query should insert MY_ID in likes or dislikes array if and only if MY_ID is not present in either array.
The problem is that query still writes MY_ID in likes array (even if MY_ID is present in dislikes) and vice versa.
Let's suppose now I want to leave a 'Like' to a post:
Phase 1:
likes:[]
dislikes:[]
(I am able to like or dislike, it works.)
Phase 2:
likes: [MY_ID]
dislikes:[]
(I am not able send like again, it works, but I am still able to dislike - it should not allow me to vote again.)

Related

Remove element from Multiple MongoDB Array simultaneously

I am creating MERN Classroom where users can Create/Join classes using the class's unique Code. Whenever a user joins the classroom, class id gets pushed into the User's MongoDB document array (classesJoined) and the user id gets pushed into Classroom's MongoDB document array (classroomMembers)
Updated User's Document:
{
"_id": "62e7ef5f636d28247a21b1e7",
"name": "User One",
"email": "userone#mydomain.com",
"password": "encryptedPassword",
"classesJoined": [
"62e7f00e636d28247a21b20d",
"62e7f0a0636d28247a21b247"
]
}
Updated Classroom's Document:
{
"_id": "62e7f00e636d28247a21b20d",
"classroomName": "Class by User One",
"classroomCode": "9ljsgNqx",
"classroomMembers": [
"62e7ef5f636d28247a21b1e7"
]
}
When a user wants to leave the classroom, the classroom's ID should be removed from the User document as well as the User ID should also be removed from the Classroom Document.
To achieve this, I have tried this logic in my Express Server route:
try{
await Users.findByIdAndUpdate({_id: req.body.userID}, { $pull: { classesJoined: req.body.classID } })
await Classroom.findByIdAndUpdate({_id: req.body.classID}, { $pull: { classroomMembers: req.body.userID }
res.send("Class Removed")
})
} catch (error){
res.send("Error Occured")
}
POST Request Body Contains:
{
"userID": "62e7ef5f636d28247a21b1e7",
"classID": "62e7f00e636d28247a21b20d",
}
But the problem I am facing is that the User ID from Classroom.classroomMembers gets removed but in User Document, nothing is changing or showing an error. The classesJoined array keeps the same element.

Firestore - Sorting data based on internal data of firestore collection document

I am developing chat application using Firestore with very simple structures which include groups and number of unread messages also. I want to sort groups with unread messages first (User wise) & then others. I'm looking for a way but not found yet.
Here are the 2 ways which I am trying to follow to get what I want but have not any way.
By Keeping my message and status per user in same doc.
By keeping message_status in some other collection with required info and some query.
Group structure -
{
group_name: "GROUP-NAME",
group_id: "MESSAGE-GROUP-ID",
admin: "Anuj",
participants: [
1,2
],
participants_status: [
{
id: 1,
active: 1 // true (User active)
},
{
id: 2,
active: 0 // false (User not active)
},
],
message_status_user_wise: [
{
user_id: 1, // ID of participant
unread_message_count: 1
},
{
user_id: 2, // ID of participant
unread_message_count: 5
}
]
}
Message structure -
{
message: "This is test message"
group_id: "MESSAGE-GROUP-ID",
sender: "Anuj",
sender_id: 1
}
Now there will be multiple groups and with unread/read messages. Now question is how I can query to sort and get the groups which are having unread messages on top ie. - Groups having unread_message_count > 0 of any user should come on top in results.
Similarly, I have some other questions also regarding to it -
Keeping the same structure, How Can I get total unread message count of all groups?
How I can get only those groups in which some user having any id is active?
I also looked at subcollections, i.e also none of my use.

MongoDB - Comment Upvoting/Downvoting with Aggregation Pipeline

I'm trying to implement an upvote/downvote mechanism for comments (similar to the upvoting/downvoting mechanism found on reddit). I have a separate collection called commentReputation and the documents inside can look like this:
{
"_id" : ObjectId("5e5acb6d6034a879655c8819"),
"commentId" : ObjectId("5e5983102328a83d1a4b541f"),
"creationDate" : ISODate("2020-02-29T20:37:01.509Z"),
"upvotes" : [
ObjectId("5e5983102328a83d1a4b53e7"),
ObjectId("5e5983102328a83d1a4b53e4")
],
"downvotes" : [
ObjectId("5e5983102328a83d1a4b53e5")
]
}
In short: every comment will eventually have it's own CommentReputation document (the CommentReputation document should be created as soon as someone upvotes/downvotes a comment)
There are 2 case scenarios:
The collection is empty meaning that I need to create my very first CommentReputation document with a given commentId x. In some other part of the project I was using $setOnInsert with { upsert: true } but it seems (looking at the documentation) that the aggregation pipeline does not support $setOnInsert as for now. Is there another way to deal with this problem?
The document is there and the actuall upvoting should occur.
a) Both upvotes and downvotes arrays do not contain the userId that is trying to upvote thus it gets added to the upvotes array without any further actions
b) The upvotes array contains the userId that is trying to upvote the comment as a result the userId should be REMOVED from the upvotes array. (the user already had this comment upvoted and clicked a second time the upvote button which cancels out the upvote)
c) The downvotes array contains the userId. In this case the userId should be removed from downvotes and added to upvotes
I'm trying to accomplish the above logic with the updateOne method and a aggreagtion pipeline however I'm not sure if this is even possible.
What I currently have is returning a "Unrecognized pipeline stage name: '$cond'"
const updateUpvotes = {
$cond: {
if: { $elemMatch: { upvotes: ObjectID(userId) } },
then: { $pull: { upvotes: ObjectID(userId) } },
else: { $addToSet: { upvotes: ObjectID(userId) } }
}
};
db.collection(collectionName).updateOne({
commentId: ObjectID('5e5983102328a83d1a4b541f')
}, [updateUpvotes])
Am I overthinking the whole feature? I guess the 1. problem can be solved by simply creating a CommentReputation document (with empty upvotes and downvotes at the same time the Comment document is being created.
Is there a better way of doing this? I would love to have it working inside a single query request. Maybe someone of You guys implemented a similar feature and can give me some hints on this one.
you can do it with the following pipeline update but it requires that the upvotes and downvotes arrays exist. even if it's just empty.
var comment_id = ObjectId("5e5983102328a83d1a4b541f");
var user_id = ObjectId("5e5983102328a83d1a4b53e5");
db.commentReputation.update(
{
commentId: comment_id
},
[
{
$set: {
upvotes: {
$cond: [
{ $in: [user_id, '$upvotes'] },
{ $setDifference: ['$upvotes', [user_id]] },
{ $setUnion: ['$upvotes', [user_id]] }
]
}
}
},
{
$set: {
downvotes: {
$cond: [
{ $in: [user_id, '$downvotes'] },
{ $setDifference: ['$downvotes', [user_id]] },
'$downvotes'
]
}
}
}
]
);

Optimalization of firebase query. Getting data by ids

I'm new in Firebase. I would like to create an app (using Angular and AngularFire library), which shows current price of some wares. I have list all available wares in Firebase Realtime Database in the following format:
"warehouse": {
"wares": {
"id1": {
"id": "id1",
"name": "name1",
"price": "0.99"
},
"id2": {
"id": "id2",
"name": "name2",
"price": "15.00"
},
... //much more stuff
}
}
I'm using ngrx with my app, so I think that I can load all wares to store as an object not list because normalizing state tree. I wanted load wares to store in this way:
this.db.object('warehouse/wares').valueChanges();
The problem is wares' price will be refresh every 5 minutes. The number og wares is huge (about 3000 items) so one response will be weight about 700kB. I know that I will exceed limit downloaded data in a short time, in this way.
I want limit the loading data to interesing for user, so every user will can choose wares. I will store this choices in following way:
"users": {
"user1": {
"id": "user1",
"wares": {
"id1": {
"order": 1
},
"id27": {
"order": 2
},
"id533": {
"order": 3
}
},
"waresIds": ["id1", "id27", "id533"]
}
}
And my question is:
Is there a way to getting wares based on waresIds' current user? I mean, does it exist way to get only wares, whose ids are in argument array? F.e.
"wares": {
"id1": {
"id": "id1",
"name": "name1",
"price": "0.99"
},
"id27": {
"id": "id27",
"name": "name27",
"price": "0.19"
},
"id533": {
"id": "id533",
"name": "name533",
"price": "1.19"
}
}
for query like:
this.db.object('warehouse/wares').contains(["id1", "id27", "id533"]).valueChanges();
I saw query limits in Angular Fire like equalTo and etc. but every is for list. I'm totally confused. Is there anyone who can help me? Maybe I'm making mistakes in the design of the app structure. If so, I am asking for clarification.
Because you are saving the ids inside user try this way.
wares: Observable<any[]>;
//inside ngOnInit or function
this.wares = this.db.list('users/currentUserId/wares').snapshotChanges().map(changes => {
return changes.map(c => {
const id = c.payload.key; //gets ids under users/wares/ids..
let wares=[];
//now get the wares
this.db.list('warehouse/wares', ref => ref.orderByChild('id').equalTo(id)).valueChanges().subscribe(res=>{
res.forEach(data=>{
wares.push(data);
})
});
return wares;
});
});
There are two things you can do. I don't believe Firebase allows you to query for multiple equals values at once. You can however loop over the array of "ids" and query for each one directly.
I am assuming you already queried for "waresIds" and you've stored those ID's in an array named idArray:
for id in idArray {
database.ref('warehouse/wares').orderByChild('id').equalTo(id).once('value').then((snapshot) => {
console.log(snapshot.val());
})
}
In order to use the above query efficiently you'll have to index your data on id.
Your second option would be to use .childChanged to get only the updated data after your initial fetch. This should cut down drastically on the amount of data you need to download.
Yes , you can get exactly data that you want in firebase,
See official Firebase documents about filtering
You need to get each waresID
var waresID = // logic to get waresID
var userId = // logic to get userId
var ref = firebase.database().ref("wares/" + userId).child(waresID);
ref.once("value")
.then(function(snapshot) {
console.log(snapshot.val());
});
this will return only data related to that waresID or userId
Note: this is javascript code, i hope this will work for you.

How bind search values in mongodb with mongoose

I have the following code in my /search/:query route:
var param = {
query: req.query['query']
}
MyModel.find({
"$or": [
{ 'name': req.param.query },
{ 'age': req.param.query },
{ 'event': req.param.query },
]
}, function (err, results) {
if (err) {
console.log(err)
}
else {
res.render('index', {
data: results
});
}
}
);
And is good, i can search for pretty much every data that i want, but only individually. What if i want search name + age, can i? Example: 'Leo 22'.
There is any way that mongoose help me with this?
UPDATE:
My problem is:
I have tables lists it titles, this title is the concatenation of 'eventName' and 'eventDate'.
Real examples of this fields:
'Special Event - 20/12/2015'
'Classic Event - 12/03/2015'
'Hot Summer Event - 05/07/2005'
Every week will be create 4 events. In some point, a user will search for an old event, and i believe that the user will search in this format:'EVENT NAME - EVENT DATE'..
So i need a way to bind this values in my controllers.
I'm no familiar with mongoose but in order to do that, you must have a way to bind your query param to the attribute you want to search. Otherwise, they wouldn't know Leo is name and 22 is age.
Ur path would be like search?name=:name&age=:age&event=:event and in your code, you will have to process like if the param is not null, add and condition to it.
It seems you are using only one parameter (req.param.query) to filter all attributes. That's not mongoose related: you could create distinct parameters for each attribute and pass them along the query string.
For instance:
"$or": [
{ 'name': req.param.name },
{ 'age': req.param.age },
{ 'event': req.param.event },
]
And your HTTP request will be like this:
http://youraddress/expressRoute?name=Leo&age=22

Categories

Resources