How to auto populate nested associations of sails blueprint for url /model/<id>/association - sails.js

I have two models Users and Chat with many to many association.
User.js
module.exports = {
attributes: {
name: {
type : 'string'
,required : true
},
email:{
type : 'string'
,email : true
,required: true
,unique : true
},
enpassword : {
type: 'string'
},
online : {
type: 'boolean',
defaultsTo: false
},
socketid: {
type: 'string'
},
chats : {
collection: 'chat',
via: 'users',
dominant: true
}
};
Chat.js
module.exports = {
attributes: {
messages : {
collection: 'message',
via : 'chat',
dominant : true
},
users : {
collection: 'user',
via : 'chats'
},
}
};
When I call sails blueprint /user/1/chats I am getting list of chats but users association of each chat is not populated.
How can I achieve this from Sails queries ?

Great question. Here is a really easy way to do this.
First, require the following module.
var nestedPop = require('nested-pop');
Next, run your query.
getPopulatedUsers = function(req, res) {
User.find()
.populate('chats')
.then(function(users) {
return nestedPop(users, {
chats: [
'messages',
'users' // I actually wouldn't recommend populating this since you already have the users
]
}).then(function(users) {
console.log(users);
res.json(users);
});
});
}
More docs on this can be found at the following link.
https://www.npmjs.com/package/nested-pop

Related

How to update with mongoose

I have this record
{
"_id" : ObjectId("5dfdff479ad032cbbc673507"),
"selection" : [
{
"highlights" : "test",
"comment" : "CHANGE THIS",
"el" : "body:nth-child(2)>div:nth-child(2)#root>div.App>p:nth-child(1)"
},
{
"highlights" : "Barrett’s lyrical prose opens with a clever and tender solution",
"comment" : "",
"el" : "body:nth-child(2)>div:nth-child(2)#root>div.App>p:nth-child(2)"
}
],
"category" : [],
"status" : "",
"url" : "http://localhost:3000/theone",
"title" : "React App test",
"__v" : 4
}
And I want to update the comment. I have tried to use update and findOneAndUpdate and nothing is working. Here is my attempt
WebHighlight.findOneAndUpdate(
{
_id: req.params.highlight,
"selection.highlights": "test"
},
{ "selection.$.comment": "yourValue" }
);
That req.params.highlight is the id (I even hardcoded it)
I also tried this
WebHighlight.findById(req.params.highlight, (err, book) => {
var test = [...book.selection];
test[0].comment = "somethibf"
book.save();
res.json(book);
});
And nothing is working.
This is the model
const webhighlightsModel = new Schema({
selection: { type: Array, default: "" },
category: { type: Array, default: [] },
title: { type: String },
url: { type: String },
status: { type: String, default: "" }
});
Actually your code seems to work, but findOneAndUpdate returns the old document if you don't give {new: true} option.
I think for this reason, you think the update wasn't successfull, but if you check your collection, you will see the update.
WebHighlight.findOneAndUpdate(
{
_id: req.params.highlight,
"selection.highlights": "test"
},
{ "selection.$.comment": "yourValue" },
{ new: true }
)
.then(doc => res.send(doc))
.catch(err => res.status(500).send(err));
Also I think it would be better if selection had a sub schema like this:
const mongoose = require("mongoose");
const schema = new mongoose.Schema({
selection: [
new mongoose.Schema({
highlights: String,
comment: String,
el: String
})
],
category: { type: Array, default: [] },
title: { type: String },
url: { type: String },
status: { type: String, default: "" }
});
module.exports = mongoose.model("WebHighlight", schema);
So with this every selection would an _id field, and it would be better to update with this _id.
You should use the $set operator to update existing values:
WebHighlight.findOneAndUpdate(
{
_id: req.params.highlight,
"selection.highlights": "test"
},
{ '$set': { "selection.$.comment": "yourValue" } }
);

mongoose populate field using _id of its own document

I'm creating an appliction using MEAN stack in which i've one to one chat feature. I'm using mongoose and have 3 schemas :
Users
{
name: {
type: String,
required: true
},
email: {
type: String
},
phone: {
type: Number,
required: true
},
profile_pic: {
type: String,
default: null
},
password: {
type: String
},
salt: {
type: String
}
}
Messages
{
message : {
type : String,
required : true
},
timestamp : {
type : Number,
required : true
},
from : {
type : String,
required : true
},
conversation_id : {
type : mongoose.Schema.Types.ObjectId,
required : true
}
}
Conversations
{
users : [ {
type: type : mongoose.Schema.Types.ObjectId,
ref : User
}]
}
Now, when user goes to his messenger i want to show him all his past conversations with latest messages so right now i'm exceuting find command on Conversation and populating User
then after getting all conversations i'm executing find command for each conversation to get their latest message, so by this there are so many find operations are done on database in single request. i.e. why i need some solution like populate latest message for each conversation ( like i can populate using _id of conversation )
Edit
Here is the code i'm using right now :
let completeConversations = [];
conversation.find({ "users": req.body.token_data.id }).populate({ path: 'users', select: 'name profile_pic' }).exec((err, conversations) => {
if (err) throw err
if (conversations.length == 0) {
res.status(200);
res.json({ message: "No Conversations Found" })
} else {
conversations.forEach(element => {
messages.find({ conversation_id: element._id }).sort('-timestamp').limit(1).exec((err, message) => {
if (err) {
res.status(200);
res.json({ message: "Latest Message Not Found", conversations })
} else {
element = JSON.parse(JSON.stringify(element));
element.latest = message[0];
completeConversations.push(element);
}
if (completeConversations.length === conversations.length) {
res.json(completeConversations)
}
})
});
}
})
thanks in Advance

[mongodb]How to save a document and automatically increase a field of it?

I need to save or insert a new record into my mongo database. I hope the field "userid" of it can automatically increase by 1. Is it possible to do it in mongodb?
Schema
generalUserApplication: {
userid: Number, // 1
lora: [
{},
{}
]
}
Here's the way from the mongo tutorial.
You need to create new collection counters in your db.
function getNextSequence(db, name, callback) {
db.collection("counters").findAndModify( { _id: name }, null, { $inc: { seq: 1 } }, function(err, result){
if(err) callback(err, result);
callback(err, result.value.seq);
} );
}
Then, you can use getNextSequence() as following, when you insert a new row.
getNextSequence(db, "user_id", function(err, result){
if(!err){
db.collection('users').insert({
"_id": result,
// ...
});
}
});
I have use another package named 'mongoose-sequence-plugin' to generate sequence which is auto-incremented by 1.
Here I am posting code that I have tried for same problem. Hope it will help.
Schema :
var mongoose = require('mongoose');
var sequenceGenerator = require('mongoose-sequence-plugin');
var Schema = mongoose.Schema;
var ProjectSchema = new Schema({
Name : { type : String, required: true },
Description : { type : String, required: true },
DetailAddress : { type : String, required: true },
ShortAddress : { type : String, required: true },
Latitude : { type : Number, required: true },
Longitude : { type : Number, required: true },
PriceMin : { type : Number, required: true, index : true },
PriceMax : { type : Number, required: true, index: true },
Area : { type : Number, required: true },
City : { type : String, required: true }
});
ProjectSchema.plugin(sequenceGenerator, {
field: 'project_id',
startAt: '10000001',
prefix: 'PROJ',
maxSaveRetries: 2
});
module.exports = mongoose.model('Project', ProjectSchema);
This will create projects collection with parameter project_id starting from PROJ10000001 and on wards. If you delete last inserted record then this package reads the current last entry of field project_id and next id is assigned by incrementing current id.

Mongoose find() returning array empty

I have the following product model:
'use strict';
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
// create a schema
let produtoSchema = new Schema(
{
descricao: { type: String, required: true },
gateways: [ { type : mongoose.Types.ObjectId, ref: 'Gateway' } ]
}
);
mongoose.model('Produto', produtoSchema);
The the following collection:
rs0:PRIMARY> db.produtos.find().pretty()
{
"_id" : ObjectId("55fef1a3d7c6912033f2da72"),
"descricao" : "Product description",
"gateways" : [
ObjectId("55fee8a97cb7db7740acb322")
]
}
rs0:PRIMARY>
So, I'm trying to use Mongoose to fetch a specific product, but 'gateway' array is empty :
let Produto = mongoose.model('Produto');
Produto.find(
{
_id: mongoose.Types.ObjectId("55fef1a3d7c6912033f2da72")
}, function(err, result)
{
if (err) console.log(err);
console.log(result);
});
And the result is :
[ { _id: 55fef1a3d7c6912033f2da72,
descricao: 'Product description',
gateways: [] } ]
A also tried, but with the same result:
Produto
.find( { _id: mongoose.Types.ObjectId("55fef1a3d7c6912033f2da72") })
.populate('gateways')
.exec(function(err, result)
{
if (err) console.log(err);
console.log(result);
});
Any idea of what I'm doing wrong ?
Thanks.
I've found the problem.
I've changed:
gateways: [ { type : mongoose.Types.ObjectId, ref: 'Gateway' } ]
by
gateways: [ { type : mongoose.Schema.ObjectId, ref: 'Gateway' } ]
What's the difference between them ?

extjs4 proxy rest multiple request issue

Hi I am encountering a wierd behavior with my application:when I modify an item and then my proxy doas a put request, the first time is ok, the second time it sends two requests: the first with the data of the previous one, the second one with the actual data, the third time it sends three requests, onmy system it is not a big issue, because at end I get the right value on my database, but on my customer's system the result it is not always correct. Then I would like to remove this behavior.
this is my store:
Ext.create('Ext.data.Store',
{
storeId: 'bbCompaniesStore',
model:'Company',
pageSize: pageSize,
proxy:
{
idProperty : '_id',
type: 'rest',
url: 'data/companies/',
autoload: true,
noCache: true,
sortParam: undefined,
actionMethods:
{
create : 'PUT',
read : 'GET',
update : 'POST',
destroy: 'DELETE'
},
reader:
{
type: 'json',
root: 'data',
totalProperty: 'total'
},
},// proxy
listeners: {
exception: function(proxy, response, operation) {
Ext.gritter.add({
title: MongoVision.text['action.' + operation.action] || operation.action,
text: (operation.error ? operation.error.statusText : null) || MongoVision.text.exception
});
// Ext JS 4.0 does not handle this exception!
switch (operation.action) {
case 'create':
Ext.each(operation.records, function(record) {
record.store.remove(record);
});
break;
case 'destroy':
Ext.each(operation.records, function(record) {
if (record.removeStore) {
record.removeStore.insert(record.removeIndex, record);
}
});
break;
}
}
}
}
);
this is my model:
Ext.define('Company',
{
extend: 'Ext.data.Model',
fields: [
{
name : 'id',
type : 'string'
},
{
name :'firm',
type : 'string',
allowBlank: false
},{
name : 'p'
},
{
name: 'linee'
},
{
name : 'c'
},
{
name : 'data',
type: 'date'
},
{
name :'note'
},
{
name :'paese'
},
{
name : 'email'
},
{
name : 'telefono'
},
{
name : 'type'
},
{
name : 'website'
},
{
name : 'session_id'
},
{
name : 'group_id'
}
],
proxy : {
type : 'rest',
url : 'data/companies/'
}
}
);
after googling around I found a similar issue for extjs3, with no solution, I think it is strange that after so long time, there is no yet a solution; it should work now
I faced the same issue, resolved it by simply passing
batchActions: true when creating the Proxy. The default behavior for 'rest' proxies is to turn off batching.... (See extjs/src/data/proxy/Rest.js)