mongoose db - array insertion using create and save - arrays

How to insert array and normal variable data to mongoose database..
var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }], class: 10 })
parent.save(callback);
This is the method i know currently.
I need it to be done from the req.body. So how can I done after creating the parent object. ie
var parent = new Parent();
///code for inserting the array data and other normal datatypes
parent.save(callback);

Use the document instance like any other javascript object.
parent.children = [{ name: 'Matt' }, { name: 'Sarah' }];
parent.class = 10;
parent.save();
Just don't change it entirely (like doing parent = {...}), otherwise you'd have de-referenced the actual mongoose document instance. Only make changes on its properties like shown above.

Related

how to populate nested objects

I am using MERN stack.
I have to populate the objects that was in pair of object the following is my schema.
var schema = new mongoose.Schema({
rounds:{
t_tag:{
type:String,
},
schedule:[
{
teamone:{
type:ObjectId,
ref:"Team"
},
teamtwo:{
type:ObjectId,
ref:"Team"
},
}
}
]
i have to populate teamone and teamtwo. I have tried the following code.
.populate({
path:'rounds',
populate:{
path:'schedule',
model:"Team",
},
You need to call populate and pass the ref as the argument, this should populate all objects from the same collection:
.populate("Team")
In your example:
.populate({path:"Team"})
For multiple paths you can simply chain the populate method:
.populate({path:"Team"}).populate({path:"Some other ref"})

Pushing onto Mongo SubDoc of SubDoc array

I'm going around in circles with this one so hoping someone can help. I'm building a nodejs application that receives sensor values from nodes. There can be multiple sensors on a node.
Using NodeJS, Mongod DB and Mongoose, all running on a raspberry pi, 3 I've built the following Schemas & Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var valueSchema = new Schema ({
timestamp: {type: Date},
value: {}
});
var sensorSchema = new Schema ({
id: {type: Number},
type: {type: String},
description: {type: String},
ack: {type: Boolean},
values: [valueSchema]
});
var SensorNode = mongoose.model('SensorNode', {
id: {type: Number, required: true},
protocol: {},
sensors: [sensorSchema]
});
I can add in the node, and push sensors onto the sensors array, but I seem unable to push values onto the values array.
I've looked over a few other examples and questions on similar issues, and looked at using populate, but cant seem to get them to work.
Here is my code:
function saveValue(rsender, rsensor, payload) {
var value = {
values: {
timestamp: new Date().getTime(),
value: payload
}
}
SensorNode.findOneAndUpdate({
"id": rsender,
"sensors.id": rsensor
}, {
"$push": {
"sensors.$": value
}
}, function(err, result) {
if (err) {
console.log(err);
}
console.log(result);
});
}
This is returning undefined for the result and this error:
MongoError: exception: Cannot apply $push/$pushAll modifier to non-array
Values is definitely an array in the sensor schema.
I'm using readable ids rather than the auto assigned Mongo DB IDs for the sake of the UI, but I could use the MongoDB _id if that makes any difference, I don't see why it would?
Where am I going wrong ?
You're using positional operator $ so let's check the docs
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array. To project, or return, an array element from a read operation, see the $ projection operator.
So sensors.$ will return one particular document from your sensors array. That's why you're getting an error. On this level of your document you can only replace this item by using $set. I bet you wanted to do something like this:
SensorNode.findOneAndUpdate({
"id": rsender,
"sensors.id": rsensor
}, {
"$push": {
"sensors.$.values": payload
}
});
This operation will just append payload to values array in one particular sensor with id equal to rsensor.

Is there any way to .populate() a mongoDB document with mongoose and save the populated object back into the model? [duplicate]

This question already has answers here:
Mongoose populate vs object nesting
(1 answer)
MongoDB relationships: embed or reference?
(10 answers)
Closed 5 years ago.
I have a question, wondering if there is any way to persist the returned document when using the Mongoose .populate() function by saving it back to the model. Also some questions on how to structure things. Here are my schemas:
var clientSchema = new Schema({
phone: String,
email: String
},
);
var menuSchema = new Schema({
itemName: String,
itemPrice: Number,
});
var transactionSchema = new Schema ({
createdBy: { type: Schema.ObjectId, ref: 'Client'},
items: [{ type: Schema.ObjectId, ref: 'Menu' }],
});
var Menu = mongoose.model('Menu', menuSchema);
var Client = mongoose.model('Client', clientSchema);
var Transaction = mongoose.model('Transaction', transactionSchema);
When I create a new Transaction with a POST request, I populate it and return the populated Transaction as a response:
{
"_id": "5a0bde94f4434c0a604341d2",
"createdBy": {
"_id": "5a0a8a3f9c348f0998ba8c2c",
"phone": "1234567890",
"email": "some#thing.com"
},
"__v": 0,
"items": [{ Many Menu objects }]
}
However, when I query the DB again with GET, I get this:
{
"_id": "5a0bde94f4434c0a604341d2",
"createdBy": "5a0a8a3f9c348f0998ba8c2c",
"__v": 0,
"items": [Array of ObjectIds]
}
I can't use .save() because the original schema only accepts ObjectId, not an entire Object.
I noticed that when I made my schema include SubDocuments, I did not really need to use the .populate() function. I simply pushed an object into the array, and it would be there when queried.
var transactionSchema = new Schema ({
createdBy: { type: Schema.ObjectId, ref: 'Client'},
items: [menuSchema], // Sub Doc
});
MongoDB Docs say creating large mutable arrays is a bad design. I could see some transactions having 50 or 100 objects. I can see this being more of a problem if I use subDocuments because file size , but I could also imagine that doing a .populate() on an array of 100 object IDs may be expensive.
I need to update the items array in the transaction schema every time the client registers an onclick function. I need to re-render that to the client, which involves a single PUT request per click. But I have to parse that array with each click, one by one. If I'm doing a .populate() on every single item in the array...that's not great. But using subDocuments would increase the filesize of the database.
I previously had a simpler schema, but thought that passing by reference would increase the integrity of the prices being rendered. Is it better to just have an array of Objects and push into that?
var transactionSchema = new Schema ({
createdBy: { type: Schema.ObjectId, ref: 'Client'},
items: [{
name: {type: String},
price: {type: Number}
}]
});

ExtJS 4 - Model containing other model without Id relation

Given is a nested model structure like this:
Model Website
+ id
+ name
+ images[] // List of Image instances
Model Image
+ imageName
+ imageUrl
A serialised version of the response looks like:
{
"id": 4711,
"name": "Some name",
"images" [
{"imageName": "Beach", "imageUrl": "http://example.com/whatever.jpg"},
...
]
}
This nested model set is persisted in a document store and is returned on request by Website.id.
There is no by-id-relation to the nested list of images, as they are persisted as a list directly in the parent model. As far as I know, the classic relations in Ext.data.Model refer to the related models via a by-id-relation.
The question is: Is there any way that I can tell the parent model to use the Image model for each of the children in it's images list?
As a first step, you can make your images data to be loaded into the model by using a field type of auto:
Ext.define('My.Model', {
extend: 'Ext.data.Model'
,fields: [
{name: 'images', type: 'auto'}
// ... other fields
}
});
Then:
myModel.get('images');
Should return:
[
{"imageName": "Beach", "imageUrl": "http://example.com/whatever.jpg"},
...
]
From there, you should theoretically be able to implement a fully automatized solution to creates the models from this data, and -- the hardest part -- try to keep these created records and the children data in the parent model synchronized. But this is a very involved hack, and a lot of entry points in Ext code base have to be covered. As an illustration, I once tried to do that for "has one" relations, and that represent a lot of code. As a result, I never took the time to consolidate this code, and finally never used it.
I would rather advocate for a simple and local (to the model) solution. You can add a simple method to your model to get the images as records. For example:
Ext.define('My.Model', {
// ...
,getImages: function() {
var store = this.imageStore;
if (!store) {
store = new Ext.data.Store({
model: 'My.ImageModel'
,data: this.get('images') || []
});
this.imageStore = store;
}
return store;
}
});
Creating a store for the associated model will save you from having to play with the proxy and the reader. It also gives you an interface that is close to Ext's default one for associations.
If you need support for loading images more than once for the same parent record, you can hook on the field's convert method.
Finally, you may also need to handle client-side modifications of associated data, in order to be able to save them to the server. If your associated model allows it, you could simply use the children store's sync method (and don't forget to update the parent model's data in the sync callback!). But if your associated model isn't connected to an endpoint on the server-side, you should be able to hook on the serialize method to save the data in the associated store (as opposed to the one stored in the parent record, that won't get updated if you work with the associated store).
Here's a last example showing both:
Ext.define('My.Model', {
extend: 'Ext.data.Model'
,fields: [
{
name: 'images'
,type: 'auto'
// enables associated data update
,convert: function(data) {
var store = this.imageStore;
if (store) {
store.loadData(data || []);
}
return data;
}
// enables saving data from the associated store
,serialize: function(value, record) {
var store = record.imageStore,
if (store) {
// care, the proxy we want is the associated model's one
var writer = store.proxy && store.proxy.writer;
if (writer) {
return Ext.Array.map(store.getRange(), function(record) {
return writer.getRecordData(record);
});
} else {
// gross implementation, simply use the records data object
return Ext.pluck(store.getRange(), 'data');
}
} else {
return record.get('images');
}
}
}
// ... other fields
}
,getImages: function() {
var store = this.imageStore;
if (!store) {
store = new Ext.data.Store({
model: 'My.ImageModel'
,data: this.get('images') || []
});
this.imageStore = store;
}
return store;
}
});
Please notice that I haven't tested this code, so it might still contains some mistakes... But I hope it will be enough to give you the general idea!

backbone.js with backgrid.js to populate JSON

I am using backgrid.js with backbone.js. I'm trying to populate JSON (user list) in backgrid. Below is my JSON,
[{"name": "kumnar", "emailId":"kumar#xxx.com",
"locations":{"name":"ABC Inc.,", "province":"CA"}
}]
I can access name & emailId as below,
var User = Backbone.Model.extend({});
var User = Backbone.Collection.extend({
model: User,
url: 'https://localhost:8181/server/rest/user',
});
var users = new User();
var columns = [{
name: "loginId",
label: "Name",
cell: "string"
}, {
name: "emailId",
label: "E-mail Id",
cell: "string"
}
];
var grid = new Backgrid.Grid({
columns: columns,
collection: users
});
$("#grid-result").append(grid.render().$el);
userEntities.fetch();
My question is, how do I add a column for showing locations.name?
I have specified locations.name in the name property of columns but it doesn't work.
{
name: "locations.name",
label: "E-mail Id",
cell: "string"
}
Thanks
Both backbone and backgrid currently don't offer any support for nested model attributes, although there are a number of tickets underway. To properly display the locations info, you can either turn the locations object into a string on the server and use a string cell in backgrid, or you can attempt to supply your own cell implementation for the locations column.
Also, you may try out backbone-deep-model as it seems to support the path syntax you are looking for. I haven't tried it before, but if it works, you can just create 2 string columns called location.name and location.province respectively.
It's really easy to extend Cell (or any of the existing extensions like StringCell). Here's a start for you:
var DeepstringCell = Backgrid.DeepstringCell = StringCell.extend({
render: function () {
this.$el.empty();
var modelDepth = this.column.get("name").split(".");
var lastValue = this.model;
for (var i = 0;i<modelDepth.length;i++) {
lastValue = lastValue.get(modelDepth[i]);
}
this.$el.text(this.formatter.fromRaw(lastValue));
this.delegateEvents();
return this;
},
});
In this example you'd use "deepstring" instead of "string" for your "cell" attribute of your column. Extend it further to use a different formatter (like EmailFormatter) if you want to reuse the built-in formatters along with the deep model support. That's what I've done and it works great. Even better is to override the Cell definitions to look for a "." in the name value and treat it as a deep model.
Mind you, this only works because I use backbone-relational which returns Model instances from "get" calls.

Resources