Node-Orm hasOne relationship is not formed correctly - node.js

var User = db.define('user', {
user_id : Number,
work_phone : String,
mobile_phone : String
}, {
id: 'user_id'
, autoFetch: true
, cache: true
}) ;
var UserCloud = db.define('user_cloud', {
id : Number,
file_path : String,
user_id : Number,
file_size : String, //not used
date_added : Date,
date_modified : Date,
is_disclaimer : Number,
folder_id : String
}, {
autoFetch: true
}) ;
UserCloud.hasOne('user', User, {
field: 'user_id'
, autoFetch: true
})
This relationship doesn't form! It sets up the functions getUser and hasUser on the UserCloud instances, but it doesn't set the user property.
However setting a simlar hasOne relationship up on user works for other "Classes". Further the UserCloud has other relationships that are unformed as well.
I don't know what other examples to give, nor do I know where to look. I didn't see anything in One.js that provided direction, I am really in the dark here and looking for any pointers / insight.

OK this wasn't posted win the original question:
var UserCloud = db.models.user_cloud
var UserCloudFolder = db.models.user_cloud.user_cloud_folders
THe big issue is that there was an accidental chaining of models on the second model:
var UserCloudFolder = db.models.user_cloud.user_cloud_folders\
SHould be
var UserCloudFolder = db.models.user_cloud_folders
Which fixed it. I discovered that orm was trying to link the UserCloud back to another UserCloud which lead me to look beyond the definition of the association. SO there you have it - no errors, just not working.

Related

how to query mongoDb with array of boolean fields?

My mongoose model schema looks like -
{
email: { type: String},
Date: {type: Date},
isOnboarded: {type: Boolean},
isVip: {type: Boolean},
isAdult: {type: Boolean}
}
In my frontend I have 3 checkboxes for "isVip", "isOnboarded" and "isAdult" options. If they are checked I'm adding them to an array, which I'll pass to the server. Let's say if "isVip" and "isAdult" are checked, I will pass [isVip, isAdult] in post api to server. Now how can I write a query to get all the documents with the fields in array as true i.e in above example how can I retrieve all docs with {isVip: true, isAdult:true}
I'm having trouble because the array values keep changing, it can be only one field or 3 fields. I couldn't find a way to give condition inside mongoose query.
User.find(
{ [req.query.array]: true},
{ projection: { _id: 0 } }
)
User is my mongoose model.
I want something like this (documents with the value 'true' for the fields given in the array) and 'req.query.array' is the array with field names I passed from frontend.
You have to create your object in JS and pass then to mongo in this way:
var query = {}
if(isVip) query["isVip"] = true;
if(isOnboarded) query["isOnboarded"] = true;
if(isAdult) query["isAdult"] = true;
And then you can use the mongoose method you want, for example:
var found = await model.find(query)
And this will return the document that matches the elements.
Also, note that this is to create and object to be read by the query, so you can use this into an aggregation pipeline or whatever you vant
Check the output:
var query = {}
query["isVip"] = true;
query["isOnboarded"] = true;
query["isAdult"] = true;
console.log(query)
Is the same object that is used to do the query here
{
"isVip": true,
"isOnboarded": true,
"isAdult": true
}
Also, to know if your post contains "isVip", "isOnboarded" or "isAdult" is a javascript question, not a mongo one, but you can use something like this (I assume you pass a string array):
var apiArray = ["isVip","isAdult"]
var isVip = apiArray.includes("isVip")
var isAdult = apiArray.includes("isAdult")
var isOnboarded = apiArray.includes("isOnboarded")
console.log("isVip: "+isVip)
console.log("isAdult: "+isAdult)
console.log("isOnboarded: "+isOnboarded)

Mask data in Mongoose find operation

The requirement is to mask mobile number and show only last 4 digits. I do not want this to be performed at client instead mask it before sending the response. I am not sure how to modify transaction object to mask the data. I want to check if there is any mongoose function to do this. If not please suggest me the best way to mask a selected field.
Logic to fetch transactions
Transaction.find(query).populate('from','name mobile email').sort({ createdAt : -1 }).skip((page) * limit).limit(limit).exec((err, transaction) =>{
if(transaction){
Transaction.countDocuments({to:id,paymentStatus:"SUCCESS"},function(err,count){
return res.status(200).send({transaction,count:count});
});
}
else if(transaction==null) return res.status(200).send("No Transactions Found");
else if(err) return res.status(400).send("Error Occurred");
});
User.Model.ts - Merchant model is similar with some additional fields
var User= new mongoose.Schema({
email:{type:String,required:"E-Mail address cannot be empty",unique:true},
mobile:{type:String,required:"Mobile number cannot be empty",min : [10,"Please provide a valid 10 digit mobile number"],unique:true},
password:{type:String,required:"Password cannot be empty",minlength : [4,"Password must be more than 4 characters"]},
.......some unrelated fields...
});
Transaction.Model.ts
var transactionSchema = new mongoose.Schema({
from:{ type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
amount : {type:String,required:true},
to:{ type: mongoose.Schema.Types.ObjectId, required: true, ref: 'Merchant' },
paymentStatus:{type : String, default : "INCOMPLETE"},
.......some unrelated fields...
});
Current output
{"transaction":[{"paymentStatus":"SUCCESS","isDisputed":true,"_id":"5eb8e50b3e2adb3b74e85d4f","from":{"_id":"5eb8e50a3e2adb3b74e85d43","name":"John Doe","email":"test#gmail.com","mobile":"9999999999"},"amount":"40","to":"5eb8e50a3e2adb3b74e85d46"}],"count":1}
Expected output
{"transaction":[{"paymentStatus":"SUCCESS","isDisputed":true,"_id":"5eb8e50b3e2adb3b74e85d4f","from":{"_id":"5eb8e50a3e2adb3b74e85d43","name":"John Doe","email":"test#gmail.com","mobile":"*******999"},"amount":"40","to":"5eb8e50a3e2adb3b74e85d46"}],"count":1}
You can use string-masking to mask the fields after you fetch them.
Mongoose plugin, virtuals or getters would also involve you to iterate over the array so the end result is same.
let stringMasking = require('string-masking');
...
transactions = transactions.map(transaction => {
let mask = stringMasking(transaction.from.phone, 0);
transaction.from.phone = mask.response;
return transaction;
});
...
return res.status(200).send({transaction,count:transaction.length});
Also its better to make the password not included in all find queries if not needed. Can be done by :
password: {type: String,select: false}

String + autoincrement number on mongoose

I want to create a String + number every time a value is inserted on database (the number must be autoincrement).
Is it possible to do in Schema? Or do I need to do that before the value's inserted on database?
'question' -> String followed by an autoincrement number
var random = [{
question: 'question1'
},{
question: 'question2'
},{
question: 'question3'
},{
question: 'question4'
}];
const RandomSchema = new Schema({
question: {
type: String,
unique: true
}
})
Autoincrement fields in Mongodb, don't work exactly the same way that they do in an RDBMS. There is a lot more overhead involved. Never the less, creating an auto field is a solved problem. There is also a third party mongoose-autoincrement package that make it a lot easier.
Extending, from that. Your problem is a solved problem too. Because
the string part will always be the same
Simply use string concatenation to prepend 'question' to the auto increment field at display time.
Here is what I implemented with one of the approaches #e4c5 pointed out, without the use of a third-party package.
Define add.js as below:
db.counters.insert(
{
_id: "itemid",
seq: 0
}
)
function getNextSequence(id) {
var ret = db.counters.findAndModify(
{
query: { _id: id },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
db.randoms.insert(
{
question: "question"+getNextSequence("itemid"),
}
)
db.randoms.insert(
{
question: "question"+getNextSequence("itemid"),
}
)
After starting a mongod instance, do mongo < add.js.

Sequelize join twice on two tables

I have two table: Conversations and ConversationParticipants.
I need to get the list of conversations that both users 1 and 2 participate too.
In MySQL, the query would be this:
SELECT conversation_participants.conversation_id FROM conversation_participants
JOIN conversations t1
ON t1.conversation_id = conversation_participants.conversation_id
AND conversation_participants.user_id = 11
JOIN conversation_participants t2
ON t1.conversation_id = t2.conversation_id
AND t2.user_id = 2
However, in Sequelize I cannot understand how to set models relations so that I can make a single query. I tried this without success (please note that this is almost pseudo code, reported as such for clarity):
var Conversation = sequelize.define('conversations', {
conversationId: Sequelize.INTEGER.UNSIGNED,
});
var ConversationParticipant = sequelize.define('conversation_participants', {
participationId: Sequelize.INTEGER.UNSIGNED,
userId: Sequelize.INTEGER.UNSIGNED,
conversationId : Sequelize.INTEGER.UNSIGNED,
});
Conversation.hasMany(ConversationParticipant, { as : 'Participants'});
and then
ConversationParticipant.findAll({
where : { userId : 1 },
include : [{
model : Conversation,
as : 'conversation',
include : [{
model : ConversationParticipant,
as : 'Participants',
where : { userId : 2 }
}]
}]
I get the following error:
Error: conversation_participants is not associated with conversations!.
Any idea?
One way you could do it is find the conversations that have participants with user 1 or 2, and afterwards filter the conversations that have both of them:
const userIdsFilter = [1,2];
//find Conversations that have user 1 OR user 2 as participants,
Conversation.findAll({
include : [{
model : ConversationParticipant,
as : 'conversation_participants',
where : { userId : { $in: userIdsFilter }}
}]
}).then((conversations) =>
{
//filter the conversations that have the same length participants
//as the userIdsFilter length (that way it excludes the ones
//that have just one of the users as participants)
return conversations.filter((conversation) =>
conversation.conversation_participants.length === userIdsFilter.length);
}).then((conversations) =>
{
//do what you need with conversations..
})
You are missing the belongsTo association in the ConversationParticipant definition. Sequelize model needs explicit association since you are trying to access Conversation via the ConversationParticipant instance even though Conversation is associated ConversationParticipant is not.
Something like this in addition to hasMany association:
ConversationParticipant.belongsTo(Conversation, { as : 'conversation',
foreignKey: 'conversationId', targetKey: 'conversationId'});
And then remove the conversationId field definition from ConversationParticipant model since belongsTo also creates that for you.

Mongoose update sub document if exists

I have following model :
var VoteSchema = new Schema({
up : Boolean
, createdBy:{type:ObjectId, ref:'users'}
, createdOn : {type:Date, default:Date.now}
});
var QuestionSchema = newSchema({
title:String
, description:String
, votes : [VoteSchema]
, createdBy:{type:ObjectId, ref:'users'}
, createdOn : {type:Date, default:Date.now}
});
var Question = mongoose.model('questions',QuestionSchema);
Suppose user1 is logged in user and question1 is current / viewing question.The user can upvote({up:true}) or downvote({up:false}) a question at any time. How can I add a new vote if a user1 have not casted a vote for question1 else update the vote.
I have been able to write the following lines of code:
QuestionSchema.statics.castVote = function(questionId, vote) {
//NOTE : vote.createdBy equalsto loggedInUserID
Q.nbind(Question.findOne, Question)({
$and:[
{_id:questionId},
{'votes.createdBy':vote.createdBy}
]
}).then(function(doc) {
if(doc) {
//I am clue less
//doc.votes is a list of votes for this question
// how can I get the particular vote casted by the user - vote.createdBy
}else {
//Question.votes.push(vote);
}
});
});
So you're part of the way there, but of course when you don't find a doc then you will not have a doc to work with in the callback. MongoDB has native ways of handling these sorts of updates, but of course you do need to test for the match as you are.
What we can do here is just work within the true or false condition of where the document exists.
Considering vote.value to be your true or false for the "upvote"
Where you do find that there is a matching document you can issue an update like this:
Question.update(
{
_id: questionId,
"votes.createdBy" vote.createdBy,
"votes.up": {"$ne": vote.value }
},
{ $set: { "votes.$.up": vote.value } }
);
So that matches and uses a positional $ operator to make sure the correct index of the matching item is updated. What I added there makes sure that you don't even touch the document where the vote.vaule is already of the same value.
And in the false condition you want to $push onto the array with the new item:
Question.update(
{
_id: questionId
},
{
$push: {
"votes": {
"up": vote.value,
"createdBy": vote.createdBy
}
}
}
);
Of course add the callback details where writeConcern is applied, which you probably do.

Resources