MongoDB putting they key into $set instead of using it for lookup? - mongodb

I am trying to update a message using userID as my _id
Is splitting it up into findOne - Save - Update the best way?
//
// Find and update message
//
var messageModel = require('../models/messageModel');
var messageTable = mongoose.model('messageModel');
var messageRecord = new messageModel();
var findMessage = () => {
return new Promise((resolve, reject) => {
console.log("=====START findMessage=====")
messageTable.findOne(
{ _id: userID }
,function(err, data) {
if (err) {
reject(new Error('findMessage: ' + err))
return;
}
// Who will have this as unread?
if (userManager==true) {
messageRecord.readUser = false;
messageRecord.readManager = true;
} else {
messageRecord.readUser = true;
messageRecord.readManager = false;
}
// If message not found, then create new one
if (!data) {
console.log("=====CREATE NEW RECORD=====")
messageRecord._id = userID;
messageRecord.activityDate = Math.round(new Date().getTime()/1000);
messageRecord.messages = {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
messageRecord.save(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})
console.log("=====RESOLVE findMessage=====")
resolve();
return;
}
// If message found, then add message
console.log("=====ADD LINE TO RECORD=====")
messageTable.update (
{ _id: userID },
{
$set: {
activityDate : Math.round(new Date().getTime()/1000),
readUser : messageRecord.readUser,
readManager : messageRecord.readManager
},
$push: {
messages: {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
}
},
{ upsert: true }
).exec(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})
console.log("=====RESOLVE findMessage=====")
resolve();
return;
});
})};
Do I need to put upsert:true? (what ever that means)
Or should I use findOneAndUpdate?
And would you use findOneAndUpdate or just update? And why?
I tought it went like this:
findone
if not found then save
if found then update
UPDATE - Thanks to lascot I ended up doing this, and it works great!
// Save message
messageTable.update (
{ _id: userID },
{
$setOnInsert: {
_id: userID
},
$set: {
activityDate : Math.round(new Date().getTime()/1000),
readUser : messageRecord.readUser,
readManager : messageRecord.readManager
},
$push: {
messages: {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
}
},
{ upsert: true }
).exec(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})

Related

update if exist insert if it doesn't exist for sub docs in mongoose

I see every relevant links for my data there is not a proper solution.
My Schema is like this:
{
"_id" : ObjectId("590aa0e68d4b23760d8d0e50"),
"updatedAt" : ISODate("2017-05-08T07:03:08.314Z"),
"createdAt" : ISODate("1987-12-31T16:00:00.000Z"),
"Avatar" : "public/image/test.pic",
"countries" : [
{
"code" : "MY",
"is_favourite" : false,
"is_visited" : true,
},
{
"code" : "CA",
"is_favourite" : true
}
]
}
I want to add a country like this:
{
"code" : "QC",
"is_favourite" : true
}
if it does exist just update it from false to true or vise versa, otherwise insert the new object.
I write code for it but it seems long story and also it is not working correctly in insert mode(get this error : The positional operator did not find the match needed from the query). I would be grateful for any helps ....
var query = {"_id":req.params._id, "countries":{$elemMatch:{code:req.body.code}}}
var update = { $set: {"countries.$.is_favourite": req.body.is_favourite}}
var option = {"upsert": true}
User.findOneAndUpdate(query,update,option, function (err, user) {
if (err) return next(err);
return res.status(201).json({
success: true,
message: 'country '+ '<'+req.body.code+'> '+ 'updated as '
+req.body.is_favourite
});
});
This is what i have tested and works perfectly as expected.
Logic is pretty clear you just need to make small changes.
updateTestTable: function (req, res, callback) {
var pushData = {
"code": "QC",
"is_favourite": true
};
console.log("INSIDE");
var objectID=new mongoose.Types.ObjectId("59119107fd4790422fcb676a");
test.findOne({"_id":objectID,"countries.code":pushData.code},function(err,data){
console.log(JSON.stringify(data));
if(data!==null){
//Update Data
console.log("HELLO");
test.findOneAndUpdate({"_id":objectID,"countries.code":pushData.code},{ $set: { "countries.$.is_favourite": false} },function(err,data){
if(data){
console.log("DATA UPDATED");
console.log(data);
}
else{
console.log("ERR",err);
}
});
}
else{
//Insert Data
test.findOneAndUpdate({"_id":objectID},{$push: {countries: pushData }},function(err,data){
if(data){
console.log("DATA INSERTED");
console.log(data);
}
});
}
});
},

Sailsjs native with Mapreduce

I am working on sailsjs project, i just looking for suggestion to achieve the below output to make best performance with code samples.
My existing collection having this below document.
[{
"word" : "DAD",
"createdAt":"6/10/2016 7:25:59 AM",
"gamescore":1
},
{
"word" : "SAD",
"createdAt":"6/09/2016 7:25:59 AM",
"gamescore":1
},
{
"word" : "PAD",
"createdAt":"6/10/2016 8:25:59 AM",
"gamescore":1
}]
I need the below output which is something like this.
[{
"word" : "A",
"repeatedTimes" : "3",
"LatestRepeatedTime": "6/10/2016 8:25:59 AM"
},
{
"word" : "D",
"repeatedTimes" : "4",
"LatestRepeatedTime": "6/10/2016 8:25:59 AM"
},
{
"word" : "P",
"repeatedTimes" : "1",
"LatestRepeatedTime": "6/10/2016 8:25:59 AM"
},
{
"word" : "S",
"repeatedTimes" : "1",
"LatestRepeatedTime": "6/09/2016 8:25:59 AM"
}]
For the above scenario i implemented the below code to fetch, but it is not working at find query.
var m = function () {
var words = this.word;
if (words) {
for (var i = 0; i < words.length; i++) {
emit(words[i], 1);
}
}
}
var r = function (key, values) {
var count = 0;
values.forEach(function (v) {
count += v;
});
return count;
}
console.log(req.params.childid);
Activity.native(function (err, collection) {
console.log("hello");
collection.mapReduce(m, r, {
out: {merge: "words_count" + "_" + "575a4952bfb2ad01481e9060"}
}, function (err, result) {
Activity.getDB(function (err, db) {
var colname = "words_count" + "_" + "575a4952bfb2ad01481e9060";
var natCol = db.collection('words_count' + "_" + "575a4952bfb2ad01481e9060");
natCol.find({},..... **is not working**
natCol.count({}, function (err, docs) {
console.log(err);
console.log(docs);
res.ok(docs);
});
});
});
});
Answer:
natCol.aggregate([
{
$project:
{
_id: "$_id" ,
value:"$value"
}
}
], function(err, data){
console.log(data);
res.ok(data);
});
You could try the following
var m = function () {
if (this.word) {
for (var i = 0; i < this.word.length; i++) {
emit(this.word[i], {
"repeatedTimes": 1,
"LatestRepeatedTime": this.createdAt
});
}
}
};
var r = function (key, values) {
var obj = {};
values.forEach(function(value) {
printjson(value);
Object.keys(value).forEach(function(key) {
if (!obj.hasOwnProperty(key)) obj[key] = 0;
if (key === "repeatedTimes") obj[key] += value[key];
});
obj["LatestRepeatedTime"] = value["LatestRepeatedTime"];
});
return obj;
};
var opts = { out: {inline: 1} };
Activity.native(function (err, collection) {
collection.mapReduce(m, r, opts, function (err, result) {
console.log(err);
console.log(result);
res.ok(result);
});
});

Meteor call method to create Stripe customer when user/email exists in database

Submit a subscription plan form, which on the client runs through jquery validation and then calls createCustomer method. In the method, look for username and email (from the form in lowercase) and do a findOne and if no username and email exist in mongo, call stripeCreateCustomer and stripeCreateSubscription methods.
The issue is, it still registers the user to the Stripe dashboard with payment. EVEN though I receive error message saying username is taken, or email is taken. Why is stripeCreateCustomer and stripeCreateSubscription method still running?
Other than that, I believe it's working nicely. I just need email verification.. if anyone can help me with that as well, it'd be really nice. Thanks.
~/server/signup.js
import Future from 'fibers/future';
Meteor.methods({
createCustomer(customer) {
check(customer, {
username: String,
email: String,
password: String,
plan: String,
token: String
});
const usernameRegEx = new RegExp(customer.username, 'i');
const emailRegEx = new RegExp(customer.email, 'i');
const lookupCustomerUsername = Meteor.users.findOne({'username': usernameRegEx});
const lookupCustomerEmail = Meteor.users.findOne({'email': emailRegEx});
if(!lookupCustomerUsername) {
if(!lookupCustomerEmail) {
const newCustomer = new Future();
Meteor.call('stripeCreateCustomer', customer.token, customer.email, function(error, stripeCustomer) {
if(error) {
console.log(error)
} else {
const customerId = stripeCustomer.id,
plan = customer.plan;
Meteor.call('stripeCreateSubscription', customerId, plan, function(error, response) {
if(error) {
console.log(error)
} else {
try {
const user = Accounts.createUser({
username: customer.username,
email: customer.email,
password: customer.password
});
const subscription = {
customerId: customerId,
subscription: {
plan: customer.plan,
payment: {
card: {
type: stripeCustomer.sources.data[0].brand,
lastFour: stripeCustomer.sources.data[0].last4
},
nextPaymentDue: response.current_period_end
}
}
}
Meteor.users.update(user, {
$set: subscription
}, function(error, response) {
if(error) {
console.log(error)
} else {
newCustomer.return(user)
}
});
} catch(exception) {
newCustomer.return(exception);
}
}
});
}
});
return newCustomer.wait();
} else {
throw new Meteor.Error('customer-exists', 'email address is already taken')
}
} else {
throw new Meteor.Error('customer-exists', 'username is already taken')
}
}
});
~/server/stripe.js
import Future from 'fibers/future';
const secret = Meteor.settings.private.stripe.testSecretKey;
const Stripe = StripeAPI(secret);
Meteor.methods({
stripeCreateCustomer(token, email) {
check(token, String);
check(email, String);
const stripeCustomer = new Future();
Stripe.customers.create({
source: token,
email: email
}, function(error, customer) {
if(error) {
stripeCustomer.return(error)
} else {
stripeCustomer.return(customer)
}
});
return stripeCustomer.wait();
},
stripeCreateSubscription(customer, plan) {
check(customer, String);
check(plan, String);
const stripeSubscription = new Future();
Stripe.customers.createSubscription(customer, {
plan: plan
}, function(error, subscription) {
if(error) {
stripeSubscription.return(error)
} else {
stripeSubscription.return(subscription)
}
});
return stripeSubscription.wait();
}
});
for MasterAM:
Sorry for keep asking and thanks for sticking around. I'm very new at this... I provided a findOne of example user's document. After submitting the form, (if username or email does not exist in db) this is what is added:
{
"_id" : "WyWmdyZenEJkgAmJv",
"createdAt" : ISODate("2016-05-12T07:27:16.459Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$u8hyzWPu6Dnda1r8j7GkBuvQiF2iGFa5DdjEoD/CkHhT0jU.IsHhu"
}
},
"username" : "test",
"emails" : [
{
"address" : "test#test.com",
"verified" : false
}
],
"customerId" : "cus_8R6QC2ENNJR13A",
"subscription" : {
"plan" : "basic_monthly",
"payment" : {
"card" : {
"type" : "Visa",
"lastFour" : "4242"
},
"nextPaymentDue" : 1465716435
}
}
}

Mongodb find and insert

I would like to:
1) find documents
2) each of the found documents include an array, I would like to insert a new array element into the array. If the array element already exists, do nothing (do not insert a new element into the array).
I've played with aggregation however I can't seem to find an insert function?
Data:
{
"_id" : ObjectId("560c24b853b558856ef193a4"),
"name" : "ирина",
"pic" : "",
"language" : ObjectId("560c24b853b558856ef193a2"),
"cell" : 1,
"local" : {
"email" : "ирина#mail.com",
"password" : "12345"
},
"sessions" : [ // <--- this is the array I would like to insert a new element into
{
"id" : ObjectId("560c24b853b558856ef193a5")
}
]
}
Insert:
yield new Promise(function (resolve, reject) {
users.col.aggregate([
{
$match: {
'cell': socket.cell
}
},
{
// <--- insert here?
}
],
function (err, res) {
if (err === null)
resolve(res);
reject(err);
});
});
Update.
Tried the following also not willing to insert :/
yield new Promise(function (resolve, reject) {
var bulk = users.col.initializeUnorderedBulkOp();
bulk.find({
cell: 1
}).update({
$addToSet: {
sessions: {
id: 'test'
}
}
});
bulk.execute(function (err, res) {
console.log(res);
resolve(res);
});
});
As stated by user3100115 you should use update as follows:
db.collection.update({cell:1},{$addToSet:{sessions:{id: 'test'}}},{multi:true})
Using co-monk:
yield users.update({
cell: 1
}, {
$addToSet: {
sessions: {
id: 'test'
}
}
}, {
multi: true
});
You can use Bulk operations, particularly Bulk.find and update. As for adding unique values, you can use $addToSet
var bulk = db.items.initializeUnorderedBulkOp();
bulk.find({cell: socket.cell}).update({$addToSet: {sessions: id}});

How to use aggregrate in mongodb to $match _id

Document:
{
"_id" : ObjectId("560c24b853b558856ef193a3"),
"name" : "Karl Morrison",
"pic" : "",
"language" : ObjectId("560c24b853b558856ef193a2"),
"cell" : 1,
"local" : {
"email" : "karl.morrison#instanty.se",
"password" : "12345"
},
"sessions" : [
{
"id" : ObjectId("560c24b853b558856ef193a5")
}
]
}
This works:
yield new Promise(function (resolve, reject) {
users.col.aggregate([
{
$match: {
'name': 'Karl Morrison'
}
}
],
function (err, res) {
console.log('err ' + err);
console.log('res ' + JSON.stringify(res)); // <-- echos the object retrieved
if (err === null)
resolve(res);
reject(err);
});
});
This does not work:
yield new Promise(function (resolve, reject) {
users.col.aggregate([
{
$match: {
'_id': '560c24b853b558856ef193a3' // <-- _id of the user
}
}
],
function (err, res) {
console.log('err ' + err);
console.log('res ' + JSON.stringify(res));
if (err === null)
resolve(res);
reject(err);
});
});
The .col access the native mongodb object (using co-monk otherwise). So I'm doing it manually. This however isn't working. I suspect I am not casting the id hexstring to an ObjectId. No matter what I try nothing works.
const ObjectId = mongoose.Types.ObjectId;
const User = mongoose.model('User')
User.aggregate([
{
$match: { _id: ObjectId('560c24b853b558856ef193a3') }
}
])
Try this
const User = require('User')
const mongoose = require("mongoose");
User.aggregate([
{
$match: { _id: new mongoose.Types.ObjectId('560c24b853b558856ef193a3') }
}
])
use toString() method
const User = mongoose.model('User')
User.aggregate([
{
$match: { _id: user_id.toString() }
}
]