Mongoose/Mongo faster to have separate collections? - node.js

Currently I have a schema USERS with a sub doc VIEWED
As a 'user' views other users, their ID gets logged in the viewers sub doc
So when viewing, technically this gets all the users, then filters that through all the viewed users [for any given user]. So you get a list of unique/fresh users.
My method is currently fetching the list of users - Query 1
Then its fetching the list of viewed users (for a given user) - Query 2
Then using array.filter function to get a list of new users.
(using async parallel for those queries)
Question is, would it be faster to just have a separate document/collection that stores a list of viewed users for any given user. e.g:
{
userID: 1002,
viewedID: 9112
},
{
userID: 1002,
viewedID: 9222
},
{
userID: 1002,
viewedID: 9332
}
Is it possible for me to some how do a query that gets me a fresh list of users, so i don't have to do the computation myself. i.e let mongo do all the work.
edit, adding code to make it more clear
var ViewedSchema = new Schema({
coupleId: {type: Number, required: true}
});
var UserSchema = new Schema({
name : { type: String, trim: true, required: true}
, partnerId : { type: Number}
, viewed : [ViewedSchema]
});
code to view partners/users that have not been viewed before
async.parallel([
function(callback) {
//gets all the users/partners
User.find({}, function(err, users) {
var allPartners = [];
users.forEach(function(user){
if(allPartners.indexOf(user.partnerId) == -1) {
allPartners.push(user.partnerId);
}
});
callback(null, allPartners);
});
},
function(callback) {
//gets all the users/partners i have already viewed
var votedPartners = [];
User.findById(id, function(err, user) {
user.viewed.forEach(function(user){
votedPartners.push(user.coupleId);
});
callback(null, votedPartners);
});
}
],
function(err, result) {
//gets the differences between the 2 arrays
function exists(element) {
return (result[1].indexOf(element) == -1);
}
var showPartners = result[0].filter(exists);
User.find({partnerId: showPartners[0]}, function(err, user){
var json = {objects: user};
res.render('index', json);
});
});
};

I'm not sure what you mean by fresh or new users, exactly, but have you loked at the distinct() command? You can use it to get all the unique viewed user IDs for all the users in the collection, which is what it sounds like you want to do. See
http://docs.mongodb.org/manual/reference/method/db.collection.distinct/
From the documentation:
Return an array of the distinct values of the field sku in the subdocument item from all documents in the orders collection:
db.orders.distinct( 'item.sku' )
If you give an example of your current document schema, I could try to write the exact query for you.
Edit: You can use $nin to find the userIds that are not in a given list. Here is an example I set up in my local Mongo:
> db.dating.insert({"userId":100,"viewedId":["200","201"]})
> db.dating.findOne()
{
"_id" : ObjectId("5398d81799b228e88aef2441"),
"userId" : 100,
"viewedId" : [
"200",
"201"
]
}
> db.dating.insert({"userId":200,"viewedId":[""]})
> db.dating.insert({"userId":201,"viewedId":[""]})
> db.dating.insert({"userId":202,"viewedId":[""]})
> db.dating.insert({"userId":203,"viewedId":[""]})
> db.dating.insert({"userId":204,"viewedId":[""]})
> db.dating.insert({"userId":205,"viewedId":[""]})
> db.dating.find({"userId":{$nin: [200,201]}})
{ "_id" : ObjectId("5398d81799b228e88aef2441"), "userId" : 100, "viewedId" : [ "
200", "201" ] }
{ "_id" : ObjectId("5398d84099b228e88aef2444"), "userId" : 202, "viewedId" : [ "
" ] }
{ "_id" : ObjectId("5398d84799b228e88aef2445"), "userId" : 203, "viewedId" : [ "
" ] }
{ "_id" : ObjectId("5398d85699b228e88aef2446"), "userId" : 204, "viewedId" : [ "
" ] }
{ "_id" : ObjectId("5398d85c99b228e88aef2447"), "userId" : 205, "viewedId" : [ "
" ] }

Related

Create Rating if Not Rated Yet, Update User Rating if Rating Exists

I am creating a user experience where a user will be able to rate items from different vendors. My initial thought is for each User schema to have an array which stores all the items that the user has rated. The rated item would include the unique vendor item ID and a numerical rating value.
User Model
const userSchema = new mongoose.Schema({
...
userType: String,
ratedItems: Array,
...
});
Controller
exports.postUpdateRatedItem = (req, res, next) => {
User.findById(req.user.id, (err, user) => {
if (err) { return next(err); }
user.update(
{ $push: {ratedItems : {
vendorItem : req.body.itemID,
rating : req.body.rating
}}},
function (err) {
res.send(200);
});
});
}
Current Output
{
"_id" : ObjectId("5c91869a71ece20551fd6aed"),
"userType" : "participant",
"ratedItems" : [
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "3" },
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "6" }
]
}
This approach works great in adding new object to the array, but only adds and does not update. Instead, every time a user updates a rating, a new object is added to the array. What approach would allow to check for the unique vendorItem id? How do I go about checking the user rated items? If found, update the rating value, if not found, push to the array.
Thank you in advance, still learning MongoDB/Mongoose.
Edit
Below is what I expect the outcome. For each object in the array, the 'rating' is updated when the user changes the rating. The ratedItems array will eventually have many many vendorItem with unique IDs and ratings.
Expected Output
{
"_id" : ObjectId("5c91869a71ece20551fd6aed"),
"userType" : "participant",
"ratedItems" : [
{ "vendorItem" : "5c9bdd524a0dfa753e08a0a4", "rating" : "6" },
// additional rated items all with unique IDs
{ "vendorItem" : "5c9bcc14d5161c38a4581e28", "rating" : "2" },
{ "vendorItem" : "5c9407d143cd0f20d758acdb", "rating" : "11" }
]
}
It sounds like you are looking for "upsert" functionality. The Mongoose model API provides findByIdAndUpdate and other similar methods for this.
Make sure you set the new and upsert options to true. This will create the object if it doesn't exist and return the modified document if it is updated.
Your use case would look something like this:
const update = {
$push: {
ratedItems: {
vendorItem: req.body.itemID,
rating: req.body.rating
}
}
};
const options = {'new': true, upsert: true};
User.findByIdAndUpdate(req.user.id, update, options, function(err, user) {
// ...
});

How do i retrieve just the child-object in Azure Cosmos using mongoose and Node.js?

I am using Azure cosmos db with the Mongodb API. Also i am using mongoose to create schemas and create new documents in the database. I am also using Node.js.
At this point I am considering using a One-to-Many relationship with embedded documents.
The data structure is like this :
{
"_id" : "locality1",
"_type" : "Locality",
"name" : "Wallmart",
"subsectionList" : [
{
"_id" : "subsection1",
"_type" : "SubSection",
"name" : "First floor",
"sensorList" : [
{
"_id" : "sensor1",
"_type" : "Sensor",
"placement" : "In the hallway"
},
{
"_id" : "sensor2",
"_type" : "Sensor",
"placement" : "In the ceiling"
}
]
},
{
"_id" : "subsection2",
"_type" : "SubSection",
"name" : "Second floor",
"sensorList" : [ ],
}
],
}
I want to retrieve ONLY the "sensor1"-object, not anything from the parent.
Using querying i am only able to retrieve the entire "locality1"-object, with all its underlying subsections and sensors. On a larger scale that is an unnecessary large amount of data.
Here is my query so far.
Locality.find().where('subsectionList.sensorList._id').equals("sensor1").then(doc => {
console.log(doc)
})
I appreciate any tips! :)
Based on my test, i can't get rid of the _id property anyway even though i followed the parameters which is mentioned here.
Locality.find({},'subsectionList', function (err, locas)
The above query still return the results including _id property.(It seems a default item)
I get a workaround from this blog that you could loop the array to filter your desired columns.
var mongoose = require('mongoose');
var COSMOSDB_CONNSTR= "mongodb://***.documents.azure.com:10255/db";
var COSMODDB_USER= "***";
var COSMOSDB_PASSWORD= "***";
mongoose.connect(COSMOSDB_CONNSTR+"?ssl=true&replicaSet=globaldb", {
auth: {
user: COSMODDB_USER,
password: COSMOSDB_PASSWORD
}
}).then(() => console.log('Connection to CosmosDB successful'))
.catch((err) => console.error(err));
const Locality = mongoose.model('Locality', new mongoose.Schema({
_id: String,
subsectionList: [{
sensorList: [{
_id: String,
_type: String,
placement: String
}]
}]
}));
Locality.find({},'subsectionList', function (err, locas) {
if (err) return handleError(err);
var returnArray = [];
for(var i = 0; i<locas.length;i++){
for(var j = 0; j<locas[i].subsectionList.length;j++){
for(var k = 0; k<locas[i].subsectionList[j].sensorList.length;k++){
if(locas[i].subsectionList[j].sensorList[k]._id == 'sensor1')
returnArray.push(locas[i].subsectionList[j].sensorList[k]);
}
}
}
console.log(returnArray);
});

Finding max field value in mongodb using node

I am new in mongodb and node. I am trying to find the max value for a field (userId). But it returns nothing.
My code is
EventSchema.static("createUser",function(event,user,callback){
var That = this;
var max_usr_Id = '';
async.waterfall([
function(callback) {
That.find({"userId" : {"$ne" : ""}, "$and" : [{"userId" : {"$exists" : 1}}]}).sort({"_id" : -1}).limit(1).select("userId").exec(function(err, doc)
{
if(err)
{
console.log('User ID ERROR-');
callback({error:err,message:"Error getting max User ID"});
}else {
console.log('User ID-');
console.log(doc.userId);
max_usr_Id = doc.userId;
console.log(max_usr_Id);
}
});
console.log(max_usr_Id);
},
});
For some reason the control doesn't go inside the find function. When I try the following query in mongodb shell it works.
db.users.find({
"userId" : {
"$ne" : ""
},
"$and" : [
{
"userId" : {
"$exists" : true
}
}
]
}).sort({
"_id" : -1.0
}).limit(1);
Any help is highly appreciated. Thanks in advance.
The $and is not used in the proper way, try with:
That.find({ $and: [
{ "userId": { $ne: "" } },
{ "userId": { $exists: true } }
] }).sort( ...
Take a look at the $and documentation.
Edit
After seen the comments, the problem must be in the way the logging is made. You need to call toArray to get a collection, and then iterate over it (with forEach for instance):
...find( ... ).toArray(function(err, docs) {
// Print each document returned
docs.forEach(function(doc) {
console.log(doc.userId);
});
});

$unwind nested document and $match

I have a nested document which looks like:
var User = new Schema({
id: String,
position: [{
title: String,
applied:[{
candidate_id: String,
name: String
}],
}],
What I am looking to do is return all of the 'applied' subdocuments which match a certain 'candidate_id'
What I have so far:
app.get('/applied', function(req, res){
var position = "58dc2bd4e7208a3ea143959e";
User.aggregate(
{$unwind : "$position"},
{$unwind : "$position.applied"},
{$match:{'position.applied.candidate_id': position}}).exec(function (err, result) {
console.log(result);
});
res.render('applied', { title: 'applied',layout:'candidate'});
});
I have another function which returns all the positions that match, and that code works:
app.post('/search', function (req, res) {
var position = new RegExp(req.body.position, 'i');
var location = new RegExp(req.body.location, 'i');
User.aggregate(
{$unwind : "$position"},
{$match:{'position.title': position,'position.location':location}}).exec(function (err, result) {
console.log(result);
res.send({ results: result });
});
});
So basically I am struggling with getting a sub-sub-document. Any idea where I'm going wrong?
Sample data:
{
"_id" : ObjectId("58c2871414cd3d209abf5fc9"),
"position" : [
{
"_id" : ObjectId("58d6b7e11e793c9a506ffe8f"),
"title" : "Software Engineer",
"applied" : [
{
"candidate_id" : "58d153e97e3317291gd80087",
"name" : "Sample user"
},
{
"candidate_id" : "58d153e97e3317291fd99001",
"name" : "Sample User2"
}
]
},
{
"_id" : ObjectId("58c2871414cd3d209abf5fc0"),
"title" : "Software Engineer",
}
],
}
What is going on above is there 2 positions, one of which (first entry) has 2 applied candidates, What I need to do is return the nested object if it matches the mongoose query.
Your code seems fine to me I have implemented same and it works for me only possible issue can be that your position="58dc2bd4e7208a3ea143959e" it might be talking it as a string just convert it to objectId by using the following code and check it should work for you.
var mongoose = require('mongoose');
var position = mongoose.Types.ObjectId("58dc2bd4e7208a3ea143959e");
User.aggregate(
{$unwind : "$position"},
{$unwind : "$position.applied"},
{$match:{'position.applied.candidate_id': position}}).exec(function (err, result) {
console.log(result);
});
res.render('applied', { title: 'applied',layout:'candidate'});
});

Using MongoDB/NodeJS, how can I increment by the number of documents modified in an update query?

I have written an update query in MongoDB/NodeJS that deletes objects from an array of a document, based on the parameters I define. After I pull these objects, I would like to to increment another variable in the document based on how many documents were modified by the update query.
Here is an example of one of my events documents:
{
"_id" : ObjectId("575ed7fca7b89bb4027dded9"),
"dateCreated" : "6/13/2016",
"latitude" : "56.294786195890076",
"longitude" : "-43.59161567687988",
"instructorName" : "Test User",
"instructorEmail" : "test#user.com",
"instructorRating" : 5,
"eventName" : "We gon exercise",
"eventDescription" : "We gon exercise",
"spacesAvailable" : 15,
"streetAddress" : "123 wer",
"city" : "rty",
"state" : "NY",
"zip" : "12332",
"date" : "06/21/2016",
"startTime" : "12:00",
"endTime" : "02:10",
"tags" : [
"Cardio",
"Crossfit"
],
"price" : 5,
"attendies" : [
{
"_id" : ObjectId("5759cfcdb71d80fb2d1203ef"),
"name" : "Buddy Loester",
"email" : "Bud18#gmail.com",
"timeStamp" : 1467048318510,
"payed" : true
},
{
"_id" : ObjectId("574f257b05086e2c7f7940ca"),
"name" : "Trainer Trainer",
"email" : "trainer#user.com",
"timeStamp" : 1467055627894,
"payed" : true
}
],
"unpayed" : 0
}
Here is my code to give a better visualization:
var eventCollection = req.db.get('events');
// get current time since epoch in milliseconds
var milliSinceEpoch = new Date().getTime();
eventCollection.update(
{"attendies.payed" : {$eq : false}},
{
$pull:
{
"attendies" : {"timeStamp": {$lt: milliSinceEpoch /*- 600000*/}}
},
$inc:
{
spacesAvailable: numberAffected
}
},
{
multi: true
}, function(err, numberAffected) {
console.log(numberAffected);
return res.end();
}
);
If I specify 'numberAffected' in the query portion to '1', then it works as expected and increments by 1. However, I would like to increment by the number affected.
I know this code will not work with 'numberAffected' in the query. Using 'numberAffected' in the callback actually does return the number of documents modified by my query.
Does there exist a way in MongoDB to do what I am trying to do?
I have solved my problem by rewriting the query. It is as follows:
var ObjectID = require("mongodb").ObjectID;
var eventCollection = req.db.get('events');
var milliSinceEpoch = new Date().getTime();
// find and return all the documents in the events DB where there is a user who has not payed for an event
// they RSVP'd for
eventCollection.find({"attendies.payed" : {$eq : false}}, function(err, documentsWithUnpayedUsers) {
// if error finding, print it and return
if(err) {
console.log(err);
return res.sendStatus(400, "Error cancelling");
}
// if everyone has payed for all RSVP'd events
if(!documentsWithUnpayedUsers) return res.sendStatus(404, "Everyone has payed!");
// loop through every document which has people who have not yet payed for RSVP'd events
for(var i = 0; i < documentsWithUnpayedUsers.length; i++) {
// for each of these documents:
eventCollection.update(
{_id: ObjectID(documentsWithUnpayedUsers[i]._id)},
{
// remove the user from the attendie list if they have not payed,
// and it has been 10 minutes since they RSVP'd
$pull:
{
"attendies" : {"timeStamp": {$lt: milliSinceEpoch - 600000}, "payed" : {$eq : false}}
},
// then modify the number of spaces available for the event by the number of people who were
// removed from the attendie list
// then modify the amount of people who have not payed for the event yet (will now be 0)
$inc:
{
spacesAvailable: documentsWithUnpayedUsers[i].unpayed,
unpayed: -documentsWithUnpayedUsers[i].unpayed
}
}, function(err) {
// error checking for the update query
if(err){
console.log(err);
return res.sendStatus(400, "There was an error removing an attendie fom the event: "
+ documentsWithUnpayedUsers[i].eventName);
}
}
); // end of update query
} // end of for loop
return res.end();
}
); // end of find()
}); // end of checkPayed

Resources