Mongoose, express- create schema with array of objects - node.js

I try to create mongoose schema containing array of objects. Schema looks like:
const mapSchema = new Schema({
name: {
type: String
},
description: {
type: String
},
rate: {
type: Number,
default: 0
},
sumOfRates: {
type: Number,
default: 0
},
places: [
{
name: String,
description: String,
difficulty: { type: String, default: 0 },
sumOfRates: { type: String, default: 0 },
lat: Number,
lng: Number
}
]
}, {
timestamps: true
})
Places is array of objects. I tried to create mongoose schema and use it as element of places array(places: [ref: 'placeSchema']).
const placeSchema = new Schema({
name: {
type: String
},
description: {
type: String
},
difficulty: {
type: Number,
default: 0
},
lat: {
type: Number
},
lng: {
type: Number
}
})
But I think it would create another, separeted document in mongo(places document). Does it? I try to avoid such situation To keep it in one document.Now(code pasted above), gives me an error, when i try to insert json in postman:
{
"name": "ergergergg",
"description": "rgergrg",
"places": [
{
"name":"cvcv",
"description":"rgergrg",
"lat":233,
"lng":232
}]
}
Error:
ValidationError: places: Cast to Array failed for value "[object Object]" at path "places"
Why such error? How to fix this?

The appropriate way to go would be to use of a sub schema.
const subSchema = new Schema({
name: {
type: String,
},
description: {
type: String,
},
difficulty: {
type: Number,
default: 0,
},
lat: {
type: Number,
},
lng: {
type: Number,
},
});
const mapSchema = new Schema({
name: {
type: String,
},
description: {
type: String,
},
rate: {
type: Number,
default: 0,
},
sumOfRates: {
type: Number,
default: 0,
},
places: [subSchema],
}, {
timestamps: true
});
To insert a new data
mapSchema.insert({
name: "ergergergg",
description: "rgergrg",
places: [
{
name: "cvcv",
description: "rgergrg",
lat: 233,
lng: 232,
},
{
name: "22",
description: "erwer2",
lat: 123,
lng: 321,
},
],
});

To make the subSchema optional, you can maybe do something like this #DanMossa :
pages: {
type: [subSchema],
required: false
},

Related

Mongoose: Array with 2 different nested schemas as key-value

I'm trying to insert data from Google Reply to Reviews API into MongoDb using mongoose but some of the objects inserted have empty fields and some of them are nested key-value objects inside an Array which I don't know how to catch.
This is the JSON response from the API:
{
"reviews": [
{
"reviewId": "19940672-2a0f-470b-8760-05ce91add518",
"authorName": "el piculin 357",
"comments": [
{
"userComment": {
"text": "\tEs bueno y los logos son faciles de adivinar",
"lastModified": {
"seconds": "1675041753",
"nanos": 386000000
},
"starRating": 5,
"reviewerLanguage": "es",
"device": "grandpplte",
"androidOsVersion": 23,
"appVersionCode": 99,
"appVersionName": "2.5.0",
"deviceMetadata": {
"productName": "grandpplte (Galaxy J2 Prime)",
"manufacturer": "Samsung",
"deviceClass": "FORM_FACTOR_PHONE",
"screenWidthPx": 540,
"screenHeightPx": 960,
"nativePlatform": "ABI_ARM_V7,ABI_ARM",
"screenDensityDpi": 240,
"glEsVersion": 196609,
"ramMb": 1407
}
}
}
]
}
]
}
This is my Schema (review.model.js):
const mongoose = require("mongoose");
const ReviewSchema = new mongoose.Schema({
reviewId: { type: String, default: '', unique: true },
authorName: { type: String, default: '' },
userComment: {
text: { type: String, default: '' },
lastModified: {
seconds: { type: String, default: '' },
nanos: { type: Number, default: 0 },
},
starRating: { type: Number, default: 0 },
reviewerLanguage: { type: String, default: '' },
device: { type: String, default: '' },
androidOsVersion: { type: Number, default: 0 },
appVersionCode: { type: Number, default: 0 },
appVersionNumber: { type: String, default: '' },
thumbsUpCount: { type: Number, default: 0 },
thumbsDownCount: { type: Number, default: 0 },
},
developerComment: {
text: { type: String, default: '' },
lastModified: {
seconds: { type: String, default: '' },
nanos: { type: Number, default: 0 },
}
},
appInformation: {
packageIdentifier: { type: String, default: '' }
}
});
const Review = mongoose.model("Review", ReviewSchema);
module.exports = Review;
Another thing that I tried:
const mongoose = require("mongoose");
const AppInformationSchema = new mongoose.Schema({
packageIdentifier: { type: String, default: '' }
});
const DeveloperCommentSchema = new mongoose.Schema({
text: { type: String, default: '' },
lastModified: { LastModifiedSchema }
});
const LastModifiedSchema = new mongoose.Schema({
seconds: { type: String, default: '' },
nanos: { type: Number, default: 0 },
});
const UserCommentScehma = new mongoose.Schema({
text: { type: String, default: '' },
lastModified: { type: LastModifiedSchema },
starRating: { type: Number, default: 0 },
reviewerLanguage: { type: String, default: '' },
device: { type: String, default: '' },
androidOsVersion: { type: Number, default: 0 },
appVersionCode: { type: Number, default: 0 },
appVersionNumber: { type: String, default: '' },
thumbsUpCount: { type: Number, default: 0 },
thumbsDownCount: { type: Number, default: 0 }
});
const ReviewSchema = new mongoose.Schema({
reviewId: { type: String, default: '', unique: true },
authorName: { type: String, default: '' },
comments: [UserCommentScehma, DeveloperCommentSchema],
appInformation: { AppInformationSchema }
});
const Review = mongoose.model("Review", ReviewSchema);
module.exports = Review;
But I get an error when trying to implement it like that:
ReferenceError: Cannot access 'LastModifiedSchema' before initialization
What is the right way of implementing it?
As UserCommentScehma is under userComment itself, so you need to consider this structure in the schema as well.
const ReviewSchema = new mongoose.Schema({
reviewId: { type: String, default: '', unique: true },
authorName: { type: String, default: '' },
comments: [{ userComment: UserCommentScehma }, DeveloperCommentSchema],
appInformation: { AppInformationSchema }
});

Reference to an object inside an array in another collection

Following is the WorkingDay model
const workingDaySchema = new mongoose.Schema({
date: { type: String, unique: true, required: true },
availableSlots: [
{
startTime: Date,
endTime: Date,
size: Number,
enrolledUsers: [{ type: Schema.Types.ObjectId, ref: 'Response' }]
}
]
})
module.exports = mongoose.model('WorkingDay', workingDaySchema);
And the following is the Response model:
const responseSchema = new Schema(
{
id: { type: String },
name: { type: String, required: true },
age: { type: String },
gender: { type: String },
height: { type: String },
weight: { type: String },
food: { type: String },
phone: { type: String },
email: { type: String },
category: { type: Array },
answers: { type: Object },
assignedSlot: { type: Schema.Types.ObjectId, ref: availableSlots, default: null }
},
{
timestamps: true,
}
);
module.exports = mongoose.model("Response", responseSchema);
Every object inside the availableSlots array has it own unique _id.
How to create a reference to an object inside availableSlots from the assignedSlot field of response? Also how to populate the same?
I tried referencing the availableSlots objects using
assignedSlot: { type: Schema.Types.ObjectId, ref: availableSlots, default: null }
and
assignedSlot: { type: Schema.Types.ObjectId, ref: "WorkingDay.availableSlots", default: null }
but it did not work.
const { ObjectId } = mongoose.Schema.Types;
assignedSlot: [{ type: ObjectId ,ref:"WorkingDay"}]
Given your schema structure, you should reference the WorkingDay collection:
assignedSlot: { type: Schema.Types.ObjectId, ref: 'WorkingDay', default: null }
Then, you can populate the reference with:
const response = await Response.findOne({}).populate('assignedSlot').exec();
if (response && response.assignedSlot) {
console.log(response.assignedSlot.availableSlots);
}

geo element must be an array or object: type: "Point"

When I tried post request like this
"{ "imgUrl": "Server\Pictures\i14182109167", "text": "Myself in
seoul", "tag": ["seoul", "tour"], "geometry" : {"type": "Point","coordinates": [80,
-27]} }"
The error causes
'Can\'t extract geo keys: { _id:
ObjectId(\'5b8e204455526366f86a6383\'), tag: [ "seoul", "tour" ],
date: new Date(1536041028423), imgUrl:
"Server\Pictures\i14182109167", text: "Myself in seoul", geometry: {
type: "Point", coordinates: [ 80, -27 ], _id:
ObjectId(\'5b8e204455526366f86a6384\') }, __v: 0 } geo element must
be an array or object: type: "Point"' }
even I added "type": "Point" in post request but, why?
const geoSchema = new Schema({
type: {
type: String,
default: 'Point',
index: '2dsphere'
},
coordinates: {
type: [Number]
}
});
const memoSchema = new Schema({
imgUrl: {
type: String
},
text: {
type: String
},
date: {
type: Date,
default: Date.now
},
tag: {
type: [String]
},
user: {
type: Schema.Types.ObjectId,
ref: 'Memo'
},
geometry: geoSchema
})
I experienced this error, tried several schema declarations, until I fixed by this implementing this design:
1. Create a new schema, which has Point as a property.
const mongoose = require('mongoose');
const {Point} = require('mongoose-geojson-schema');
const pointSchema = new mongoose.Schema({
type: {
type: String,
enum: ['Point'],
required: true
},
coordinates: {
type: [Number],
required: true
}
});
2. Schema for the object including above as a property:
const itemsSchema = new mongoose.Schema({
description: {
type: String,
required: true
},
location: {
type: pointSchema,
required: true
}
)
const Item = mongoose.model('Item', ItemsSchema);
module.exports = Item;
enter code here
3. Finally, my controller succesfully instantiates the object like this:
var geolocation =
{
type: 'Point',
coordinates: [
lng,
lat
]
};
const item = new Item({
description: req.body.description,
location: geolocation
})
Hope that helps.
I changed geometry to loc it worked!
but, I don't know why...
I was facing the same issue even after doing everything mention here. After spending too much time on this problem I get to know that if we save a default value it will work correctly.
Just follow the above answer and add
location: {
type: pointSchema,
default: {
type: 'Point',
coordinates: [0, 0],
},
required: false,
},
And delete the previous documents if any document has location.

Mongoose Populate Virtuals

How to populate array of objects not by _id? I need it because I use "market_hash_name" as id in my project and it's more effectively then use _id:
let schema = new mongoose.Schema({
steamid: {
type: String,
unique: true,
index: true,
required: true
},
inventory: [{
market_hash_name: String,
name: String,
assetid: {
type: String
},
instanceid: {
type: String,
default: '0'
},
contextid: {
type: String,
default: '2'
},
amount: {
type: Number,
default: 1
},
ongoing_price_manipulation: {
type: Boolean
},
price: {
type: Number
}
}]
});
I used virtuals but only for a separate model and it works:
schema.virtual('item_data', {
ref: 'Steamlytics',
localField: 'market_hash_name',
foreignField: 'market_hash_name'
});
this.find(query).populate("item_data").exec((err, items) => {});

Mongoose get value from embedded document

i have a scheme like this
var WFWorkItemDocument = new Schema({
id: { type: String, required: true, unique: true, default: uuid.v1 },
description: { type: String },
period: [{
id: { type: String, default: uuid.v1 },
start: { type: Date, default: Date.now }
due: { type: Number, integer: true },
new: { type: Number, integer: true },
}],
i want to get the period's due value for that i used a method like
WorkItem.findOne({ id: idUpdate }, function(err, WorkItem) {
if (err) {
console.log("invlaid id");
//return res.send(404, { error: 'invalid id' });
}
if (WorkItem) {
console.log("id");
console.log(WorkItem.period.due);
} else {
//res.send(404, new Error('Workitem not found'));
}
});
but it doesn't work how can i get the due value??
This is the result for console.log(WorkItem)
Change the schema to embed one object. Unless you need embedded array.
var WFWorkItemDocument = new Schema({
id: { type: String, required: true, unique: true, default: uuid.v1 },
description: { type: String },
period: {
id: { type: String, default: uuid.v1 },
start: { type: Date, default: Date.now }
due: { type: Number, integer: true },
new: { type: Number, integer: true },
},
And if you define it as an embedded array, you can access like :
WorkItem.period[index].due

Resources