Can I use primary key in association for find Waterline - sails.js

I've created two models with associations:
User.js
attributes: {
fullname: {
type: 'string',
required: true
},
username: {
type: 'string',
unique: true,
required: true
},
email: {
type: 'email',
unique: true,
required: true
},
mothertongue: {
type: 'string'
},
passports: {
collection: 'Passport',
via: 'user'
},
words: {
collection: 'Words',
via: 'owners',
dominant: true
}
}
Words.js
attributes: {
word: {
type: 'string',
unique: true,
required: true
},
language: {
type: 'string',
required: true
},
owners: {
collection: 'User',
via: 'words'
}
}
When I'm searching about words owner someone he return back empty array
Words
.find({
owners: req.session.passport.user
})
.exec(function(err, data){
if (err) {
return res.send('error');
}
res.send(data);
})
Also I used .populate('owners') but don't work too.

To find the words a user has:
User.find(1).populate('words')
To find which users own a particular word, use
Word.find(id).populate('owners', {id : ...})

Related

How to develop nested condition query in mongoDB

I am pretty new to mongoDb and want to apply nested query.
I have a business schema like this:
const businessSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
businessType: {
type: Schema.Types.ObjectId,
ref: "businessCategory",
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
select: false,
},
review: {
type: [reviewSchema],
},
isDeleted: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
Business has a review where user can do the review and reviewSchema is
const reviewSchema = new mongoose.Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: "users",
required: true,
},
rating: {
type: Number,
enum: [1, 2, 3, 4, 5],
},
reviewArray: {
type: [singleReviewSchema],
},
},
{ timestamps: true }
);
One user can do many reviews, and it has reviewArray.
ReviewArray schema is
const singleReviewSchema = new mongoose.Schema(
{
title: {
type: String,
},
description: {
type: String,
},
isDeleted: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
How to fetch the business with a condition business: isDeleted:false and its reviews with singleReviewSchema: isDeleted:false
I dont know your model names, so please replace path with correct names
but it might look like:
businnesModel.find({isDeleted: false})
.populate({
path: 'reviewModelName',
model: 'review',
populate: {
path: 'reviewArray',
model: 'singleReviewModelName',
match: {
isDeleted : false
}
}
})
It should provide you array of businessModel documents - even when their singleReviews array will be empty (because all of reviews are deleted, or there was zero reviews). So you have to filter it out in JS.
To avoid filtering in JS, and to do it a bit more efficient way for mongodb, you can go with aggregate instead.

Mongoose findOneAndUpdate + upsert always replaces existing document

I have a collection I want to upsert with findOneAndUpdate. In addition to that I have two fields (isHandled, isNotADuplicate) that should be:
defaulted to 'false' upon insert
left untouched upon update (e.g. isHandled stays 'true')
I have however found that
isHandled, isNotADuplicate are always defaulted back to 'false'
_id is also regenerated upon every update (I use a compound key to query the doc, not _id)
My Model
export const QuickbrainFindingSchema = new Schema<QuickBrainFindingDocument>({
connectedApplicationType: { type: String, required: true, enum: ['jira'] },//e.g. jira
clientKey: { type: String, required: true },//e.g. 135eb702-156c-3b67-b9d0-a0c97548xxxx
//key
projectKey: { type: String, required: true },//e.g. AL
type: { type: String, required: true },
doc1key: { type: String, required: true },//e.g. AL-7
doc2key: { type: String, required: true },//e.g. AL-16
//data
calculationDate: { type: SchemaTypes.Date, default: Date.now },
direction: { type: String, required: true },
reasonAndMetric: { type: SchemaTypes.Mixed, reason: true },
scoreSummary: { type: String, reason: true },
isHandled: { type: SchemaTypes.Boolean, default: false },
isNotADuplicate: { type: SchemaTypes.Boolean, default: false },
similarityReference: { type: SchemaTypes.ObjectId, required: true, ref: "QuickbrainSimilarityMatrix" }
}, {
//options
});
QuickbrainFindingSchema.index(
{ connectedApplicationType: 1, clientKey: 1, project: 1, doc1key: 1, doc2key: 1, type: 1 },
{ unique: true, name: "compoundKey" }
);
export const QuickbrainFindingModel = model<QuickBrainFindingDocument>("QuickbrainFinding", QuickbrainFindingSchema);
My Code
public async addFinding(
projectKey: string,
doc1key: string,
doc2key: string,
type: ET_FindingType
, data: QuickbrainFindingData): Promise<QuickbrainFinding> {
let keyFull: QuickbrainFindingKey = {
connectedApplicationType: this.connectedApplicationType,
clientKey: this.clientKey,
projectKey: projectKey,
doc1key: doc1key,
doc2key: doc2key,
type: type
};
let insertObj: QuickbrainFinding = <QuickbrainFinding><unknown>{};
Object.assign(insert, keyFull);
Object.assign(insert, data);
delete (<any>insertObj).isHandled;
delete (<any>insertObj).isNotADuplicate;
return new Promise<QuickbrainFinding>(function (ok, nok) {
QuickbrainFindingModel.findOneAndUpdate(
keyFull, { $set: insertObj},
{
runValidators: true,
upsert: true,
setDefaultsOnInsert: true,
new: true,
omitUndefined: true,//I think only available for findAndReplace(..)
})
.lean().exec(function (err, result) {
if (err) {
nok(err);
}
else
ok(result)
});
});
}
Mongoose Debug Output
quickbrainfindings.findOneAndUpdate(
{
connectedApplicationType: 'jira',
clientKey: '135eb702-256c-3b67-b9d0-a0c975487af3',
projectKey: 'ITSMTEST',
doc1key: 'ITSMTEST-7',
doc2key: 'ITSMTEST-10',
type: 'Email'
},
{
'$setOnInsert':
{ __v: 0, isHandled: false, isNotADuplicate: false, _id: ObjectId("60789b02c094eb3ef07d2929") },
'$set': {
connectedApplicationType: 'jira',
clientKey: '135eb702-256c-3b67-b9d0-a0c975487af3', projectKey: 'ITSMTEST', doc1key: 'ITSMTEST-7', doc2key: 'ITSMTEST-10', type: 'Email',
calculationDate: new Date("Thu, 15 Apr 2021 19:58:58 GMT"),
direction: '2', scoreSummary: '100.0%',
similarityReference: ObjectId("60789b029df2079dfa8aa15a"),
reasonAndMetric: [{ reason: 'Title Substring', metricScore: '100%' },
{ reason: 'Title TokenSet', metricScore: '54%' }, { reason: 'Description TokenSet', metricScore: '100%' }]
}
},
{
runValidators: true, upsert: true, remove: false, projection: {},
returnOriginal: false
}
)
What happens
Existing documents are found, but when they are updated I'm confused that:
_id is regenerated
isHandled and isNotADuplicate are reset to 'false' (although insertObj does not contain them)
When looking at the debug output I can see that the new _id is the one fron $setOnInsert, which confuses the heck out of me, since the selector works
Notable
keyFull is used to query the existing document, it does not contain _id;
delete (<any>insertObj).isHandled <- the object used for $set does NOT contain isHandled
This is embarrasing to admit, but thanks to Joe I have found the problem.
Before every findOneAndUpdate / Upsert I had a delete statement removing the existing documents Pipeline:
Delete old documents
Calculate new documents
Upsert new documents -> always resulted in Insert
let matchAnyDoc = this.filterForDocKeyAny(projectKey, docKeyAny, findingType);
matchAnyDoc.forEach(async (condition) => {
QuickbrainFindingModel.deleteMany(condition).exec(function (err, res) {
if (err) {
nok(err);
} else {
ok();
}
});
}, this);

Mongoose nested populate virtual return null

Why does virtual in a nested populate return null?
I have a Post schema and a User schema like this:
Post Schema:
const PostSchema = new mongoose.Schema({
_author_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
privacy: {
type: String,
default: 'public'
},
...
}, { timestamps: true, toJSON: { virtuals: true }, toObject: { getters: true, virtuals: true } });
PostSchema.virtual('author', {
ref: 'User',
localField: '_author_id',
foreignField: '_id',
justOne: true
});
module.exports = mongoose.model('Post', PostSchema);
User Schema
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
minlength: 12,
unique: true,
validate: {
validator:
(email) => {
const regex = /^\w+([.-]?\w+)*#\w+([.-]?\w+)*(\.\w{2,3})+$/;
return regex.test(email);
},
message: '{VALUE} is invalid.'
}
},
password: {
type: String,
required: true
},
username: {
type: String,
unique: true,
required: true
}
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}],
...
}, { timestamps: true, toJSON: { virtuals: true }, toObject: { getters: true, virtuals: true } });
module.exports = mongoose.model('User', UserSchema);
And when I fetch the user to DB:
const user = await User
.findOne({ username })
.populate({
path: 'posts',
model: 'Post',
select: 'description name photos comments createdAt',
options: {
limit: 3,
sort: { createdAt: -1 },
},
populate: {
path: 'author', //virtual <------- returns null but '_author_id' is fine
model: 'User',
select: 'username fullname profilePicture'
}
})
.sort('-createdAt');
Sample returned document
{
...
"posts": [
{
"photos": [],
"comments": [
"5fe96ec48564ce31dcebe669",
"5fe97c43f4169834a48b3851",
"5fe97c726ccf4633006fbeaa"
],
"description": "Gago ka ba hahahaha",
"_id": "5fe96d84178485086090faa9",
"createdAt": "2020-12-28T05:30:44.157Z",
"author": null, // <-----
"id": "5fe96d84178485086090faa9"
}
]
}
Did I miss something? My author virtual works fine in a non-nested populate.
I think you need to add author in the outer populate select.
const user = await User.findOne({ username })
.populate({
path: 'posts',
select: 'description name photos comments createdAt author', // <------- try adding author here
options: {
limit: 3,
sort: { createdAt: -1 },
},
populate: {
path: 'author',
select: 'username fullname profilePicture',
},
})
.sort('-createdAt');
Also, you shouldn't have to add the model in the populate since it already knows which model from the schema.

ReferenceError: user is not defined in user api

When I open localhost:1337/user I got Internal Server Error
Something isn't right here.
ReferenceError: user is not defined
How can I fix it? My API is not working. I can't insert data in database using html forms in sailsjs.
My user model is:
module.exports = {
attribute: {
firstname: {
type: 'string',
unique: true
},
lastname: {
type: 'string',
unique: true
},
organisationName: {
type: 'string',
unique: true
},
DepatmentName: {
type: 'string',
unique: true
},
DOB: {
type: 'date',
unique: true
},
DOJ: {
type: 'date',
},
DOL: {
type: 'date',
},
Address: {
type: 'string',
},
City: {
type: 'string',
},
State: {
type: 'string',
},
Country: {
type: 'string',
},
email: {
type: 'string',
email: true,
required: true,
unique: true
},
encryptedPassword: {
type: 'string',
},
ContactNumber: {
type: 'integer',
},
//toJSON: function() {
// var obj = this.toObject();
//
// }
}
};
You are calling a variable "user" and it doesn't exist or it's empty. Check youre code for this variable..
In youre usermodel, you only have firstname and lastname. I guess you need one of this. If you want more specific help, then you have to show us some extra coding of the page, not only the model.

Sails.js model association

Currently my two models look like this
module.exports = {
tableName: 'accounts',
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true
},
name: {
type: 'string',
required: true
},
password: {
type: 'string',
required: true
},
email: {
type: 'string',
required: true
},
gang_name: {
type: 'string',
required: true
},
family_id: {
type: 'string',
required: true
},
world: {
type: 'string',
required: true
},
messages: {
collection: 'Messages',
via: 'for'
}
}
}
And my Messages model
module.exports = {
tableName: 'messages',
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true
},
title: {
type: 'string',
required: true
},
text: {
type: 'string',
required: true
},
for: {
model: 'Accounts',
required: true
},
by: {
type: 'integer',
required: true
}
}
};
I want to associate the for field of a message with an account so if 'for' field is = 11 load account with id 11... Currently im trying this way
Accounts.find({ id: req.session.accountid }).populate('Messages').exec(function(err, data) {
console.log(data, err);
});
But Im getting an error
Attempting to populate an attribute that doesnt exist
You've got to use the populate method with the attribute name ('messages'), not the model name ('Messages').