I have a getting the problem when querying with embedded object ID
Here is my Activity Type schema
const ActivityTypeSchema = new mongoose.Schema({
activity_name: {
type: String
}
}
Here is my Activity schema
const ActivitySchema = new mongoose.Schema({
activity_type: {
type: mongoose.Schema.Types.ObjectId,
ref: 'activity_type',
},
location: {
type: String
},
start_time: {
type: String
},
end_time: {
type: String
}
}
My code
let regex = new RegExp(req.query.searchString, "i");
let getActivities = await Activity.find({
'location': regex,
'activity_type.activity_name': regex
});
In Result, am getting an empty array []
Related
I am trying to add tags to existing tags in a MongoDB collection with this Schema:
const workSchema = new mongoose.Schema({
title: {
type: String,
required: "Tile can't be blank"
},
description: {
type: String
},
imageURL: {
type: String,
unique: true
},
workURL:{
type: String,
unique: true
},
tags:{
type:Array
},
createdDate: {
type: Date,
default: Date.now
}
});
const Work = mongoose.model('Work', workSchema);
module.exports = Work;
I made an API that makes a PUT request to "/api/work/:workId/tags"
exports.updateTags = (req, res) =>{
try{
const newTags = req.body.tags.split(',');
newTags.forEach(tag => {
db.Work.update(
{"_id": req.params.workId},
{
$push:{
tags: tag
}
}
)
})
res.status(200).send({message : "tags updated"})
}
catch(error){
res.status(400).send(error)
}
}
request.body:
{
tags:"a,b,c"
}
The problem is that the array won't update with the new tag values
I searched for other ways to update in the docs and on the web but I didn't find any solutions.
You haven't defined _id in your workSchema so the type of _id would be ObjectId
But req.params.workId is probably a String, so querying an ObjectId with a String won't work.
So you should convert req.params.workId to ObjectId using mongoose.Types.ObjectId
{ "_id": mongoose.Types.ObjectId(req.params.workId) }
But you can improve your code a bit more by using .findByIdAndUpdate and $each operator
.findByIdAndUpdate will automatically convert your _id to ObjectId
You can use $each to $push multiple array elements at the same time without using .forEach
Work.findByIdAndUpdate(req.params.workId, {
$push: { "tags": { $each: newTags } }
})
I have a subdocument that is nested as an array. Inside that subdocument I have references to other models. Using the .Find and .Populate methods I can receive the entire objects for single models referenced in the subdocument (check out Stop below) but not an array of model instances, Facts/Recommendations. For Facts/Recommendations I receive an array of object _ids. I can probably just take those _ids and make another query but this seems messy.
Is there a way to populate the array? Do I need to use Aggregate + $lookup? Is there a more Mongo way to restructure the Schema to simplify this?
Thank you for all and any help!
My subdocument called TourStop:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const TourStopSchema = new Schema({
stop: {
type: Schema.Types.ObjectId,
ref: 'Stop',
required:[true, 'must have a Stop'],
},
mandatory: {
type:Boolean,
default: false
},
check_in_time: {
type: Number,
default: 0
},
order: {
type:Number,
required: [true, 'Must have an order']
},
facts: [{
type: Schema.Types.ObjectId,
ref: 'Fact'
}],
recommendations: [{
type: Schema.Types.ObjectId,
ref: 'Recommendation'
}]
});
module.exports = TourStopSchema;
TourStops lives inside of Tour:
const mongoose = require('mongoose');
const TourStopSchema = require('../subdocs/tour_stop');
const Schema = mongoose.Schema;
const tourSchema = new Schema({
name: {
type:String,
required:[true,'Name is required!'],
unique: true
},
city: {
type: String,
required: [true, 'City is required!']
},
tourStops:[TourStopSchema]
});
const Tour = mongoose.model('Tour', tourSchema);
module.exports = Tour;
Stop Schema which is populated just fine.
const mongoose = require('mongoose');
const LocationSchema = require('../subdocs/location');
const ImageSchema = require('../subdocs/image');
const Schema = mongoose.Schema;
const stopSchema = new Schema({
name:{
type: String,
required:[true,'Must have a name!']
},
location:LocationSchema,
categories: {
type: [String],
default:[]
},
images:{
type:[ImageSchema],
default:[]
}
});
const Stop = mongoose.model('Stop', stopSchema);
module.exports = Stop;
And Fact Schema which is not populated correctly, instead returns an array of strings with the _ids
Fact:
const mongoose = require('mongoose');
const ImageSchema = require('../subdocs/image');
const Schema = mongoose.Schema;
const factSchema = new Schema({
stop: {
type: Schema.Types.ObjectId,
ref:'Stop',
required:[true, 'A Fact must have a Stop!'],
},
description: {
type: String,
required: [true,'A Fact must have a description!']
},
images: {
type:[ImageSchema],
default:[]
}
});
const Fact = mongoose.model('Fact', factSchema);
module.exports = Fact;
And I'm running a test to check that the schema is properly hooked up to retrieve all the attributes of a TourStop:
it('saves a full relation graph', (done) => {
User.findOne({ first_name: 'Dean' })
.populate({
// in that user find the tours property and load up all tours
path: 'tours',
// inside of all those tours, find the tourstops property and load all associated stops
populate: {
path: 'tour_stops.facts',
model: 'Fact'
},
populate: {
path: 'tour_stops.stop',
model: 'Stop'
}
})
// .populate('tours.tour_stops[0].facts')
.then((user) => {
// console.log(user.tours[0].tour_stops[0].stop);
console.log(user.tours[0].tour_stops[0]);
// console.log(Array.isArray(user.tours[0].tour_stops[0].facts))
assert(user.first_name === 'Dean' );
assert(user.tours.length === 1);
assert(user.tours[0].name === "Awesome New Tour");
assert(user.tours[0].tour_stops[0].stop.name === 'Jaffa Clock Tower');
// assert(user.tours[0])
// assert(user.blogPosts[0].title === 'JS is Great');
// assert(user.blogPosts[0].comments[0].content === 'Congrats on great post!' );
// assert(user.blogPosts[0].comments[0].user.name === 'Joe' )
done();
})
})
You can use the following code to populate tours, stop, facts and recommendations.
Note in model property, we should not give string value, but the model itself. So you need to import them to your code.
User.findOne({ first_name: "Dean" })
.populate({
path: "tours",
populate: {
path: "tourStops.stop",
model: Stop
}
})
.populate({
path: "tours",
populate: {
path: "tourStops.facts",
model: Fact
}
})
.populate({
path: "tours",
populate: {
path: "tourStops.recommendations",
model: Recommendation
}
})
.then(user => {
console.log(user);
});
I have this model of workspace schema in my node js project(model is displayed below)
After the user logs into my application I want to display the information of a workspace only if it is created by him or he is a team member of that workspace
I am able to find the workspaces created by the user by the following query
Workspace.find({creator:req.user._id},function(err,workspaces){
res.render('home',{
wokspacses:workspaces
});
});
similarly, I also want the workspaces in which the user is the team member
Workspace.find({creator:req.user._id},function(err,workspaces){
Workspace.find({team_member:"WHAT SHOULD I WRITE HERE"},function(err,workspaces2){
res.render('home',{
wokspacses:workspaces
wokspacses2:workspaces2
});
});
Since team_members is an array simply passing the user id is not yielding the result and workspaces2 remains empty
Thank you for your time !!
const mongoose = require('mongoose');
const workspaceSchema = mongoose.Schema({
name:{
type:String,
required:true
},
date: {
type: Date,
required: true
},
creator:{
type: Object,
ref: 'User',
required: true
},
team_member: [{ type: Object, ref: 'User' }]
});
module.exports = mongoose.model('Workspace',workspaceSchema);
Use the $in Operator.
const mongoose = require("mongoose")
const Schema = mongoose.Schema
mongoose.connect('mongodb://localhost/stackoverflow', {useNewUrlParser: true});
const workspaceSchema = new Schema({
name:{
type:String,
required:true
},
date: {
type: Date,
required: true
},
creator:{
type: Object,
ref: 'User',
required: true
},
team_member: [{ type: Object, ref: 'User' }]
});
const WorkspaceModel = mongoose.model('Workspace',workspaceSchema);
const sessionUserId = "5d330f3de87ec83f95504c44" //i.e. req.user._id;
WorkspaceModel.find({
$or:[
{ creator: sessionUserId },
{
team_member: {
$in: [sessionUserId]
}
}
]
}).exec((err, result) => {
console.log("result", result)
})
I'm coding an app using Node.js and MongooseJS as my middleware for handling database calls.
My problem is that I have some nested schemas and one of them is populated in a wrong way. When I track every step of the population - all of the data is fine, except the devices array, which is empty. I double checked the database and there is data inside that array, so it should be fine.
I've got Room schema. Each object of Room has a field called DeviceGroups. This field contains some information and one of them is an array called Devices which stored devices that are assigned to the parent room.
As you can see in the code, I am finding a room based on it's ID that comes in request to the server. Everything is populated fine and data is consistent with the data in the database. Problem is that the devices array is empty.
Is that some kind of a quirk of MongooseJS, or am I doing something wrong here, that devices array is returned empty? I checked in the database itself and there is some data inside it, so the data is fine, the bug is somewhere in the pasted code.
The code:
Schemas:
const roomSchema = Schema({
name: {
type: String,
required: [true, 'Room name not provided']
},
deviceGroups: [{
type: Schema.Types.ObjectId,
ref: 'DeviceGroup'
}]
}, { collection: 'rooms' });
const deviceGroupSchema = Schema({
parentRoomId: {
type: Schema.Types.ObjectId,
ref: 'Room'
},
groupType: {
type: String,
enum: ['LIGHTS', 'BLINDS', 'ALARM_SENSORS', 'WEATHER_SENSORS']
},
devices: [
{
type: Schema.Types.ObjectId,
ref: 'LightBulb'
},
{
type: Schema.Types.ObjectId,
ref: 'Blind'
}
]
}, { collection: 'deviceGroups' });
const lightBulbSchema = Schema({
name: String,
isPoweredOn: Boolean,
currentColor: Number
}, { collection: 'lightBulbs' });
const blindSchema = Schema({
name: String,
goingUp: Boolean,
goingDown: Boolean
}, { collection: 'blinds' });
Database call:
Room
.findOne({ _id: req.params.roomId })
.populate({
path: 'deviceGroups',
populate: {
path: 'devices'
}
})
.lean()
.exec(function(err, room) {
if (err) {
res.send(err);
} else {
room.deviceGroups.map(function(currentDeviceGroup, index) {
if (currentDeviceGroup.groupType === "BLINDS") {
var blinds = room.deviceGroups[index].devices.map(function(currentBlind) {
return {
_id: currentBlind._id,
name: currentBlind.name,
goingUp: currentBlind.goingUp,
goingDown: currentBlind.goingDown
}
});
res.send(blinds);
}
});
}
})
This is an example of using discriminator method to be able to use multiple schemas in a single array.
const roomSchema = Schema({
name: {
type: String,
required: [true, 'Room name not provided']
},
deviceGroups: [{ type: Schema.Types.ObjectId, ref: 'DeviceGroup' }]
});
const deviceGroupSchema = Schema({
parentRoom: { type: Schema.Types.ObjectId, ref: 'Room' },
groupType: {
type: String,
enum: ['LIGHTS', 'BLINDS', 'ALARM_SENSORS', 'WEATHER_SENSORS']
},
devices: [{ type: Schema.Types.ObjectId, ref: 'Device' }]
});
// base schema for all devices
function DeviceSchema() {
Schema.apply(this, arguments);
// add common props for all devices
this.add({
name: String
});
}
util.inherits(DeviceSchema, Schema);
var deviceSchema = new DeviceSchema();
var lightBulbSchema = new DeviceSchema({
// add props specific to lightBulbs
isPoweredOn: Boolean,
currentColor: Number
});
var blindSchema = new DeviceSchema({
// add props specific to blinds
goingUp: Boolean,
goingDown: Boolean
});
var Room = mongoose.model("Room", roomSchema );
var DeviceGroup = mongoose.model("DeviceGroup", deviceGroupSchema );
var Device = mongoose.model("Device", deviceSchema );
var LightBulb = Device.discriminator("LightBulb", lightBulbSchema );
var Blind = Device.discriminator("Blind", blindSchema );
// this should return all devices
Device.find()
// this should return all devices that are LightBulbs
LightBulb.find()
// this should return all devices that are Blinds
Blind.find()
In the collection you will see __t property on each device
with values according to the schema used (LightBulb or Blind)
I haven't tried the code and i haven't used mongoose in a while but i hope it will work :)
Update - tested working example
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var util = require('util');
const roomSchema = Schema({
name: {
type: String,
required: [true, 'Room name not provided']
},
deviceGroups: [{ type: Schema.Types.ObjectId, ref: 'DeviceGroup' }]
});
const deviceGroupSchema = Schema({
parentRoomId: { type: Schema.Types.ObjectId, ref: 'Room' },
groupType: {
type: String,
enum: ['LIGHTS', 'BLINDS', 'ALARM_SENSORS', 'WEATHER_SENSORS']
},
devices: [{ type: Schema.Types.ObjectId, ref: 'Device' }]
});
// base schema for all devices
function DeviceSchema() {
Schema.apply(this, arguments);
// add common props for all devices
this.add({
name: String
});
}
util.inherits(DeviceSchema, Schema);
var deviceSchema = new DeviceSchema();
var lightBulbSchema = new DeviceSchema({
// add props specific to lightBulbs
isPoweredOn: Boolean,
currentColor: Number
});
var blindSchema = new DeviceSchema();
blindSchema.add({
// add props specific to blinds
goingUp: Boolean,
goingDown: Boolean
});
var Room = mongoose.model("Room", roomSchema );
var DeviceGroup = mongoose.model("DeviceGroup", deviceGroupSchema );
var Device = mongoose.model("Device", deviceSchema );
var LightBulb = Device.discriminator("LightBulb", lightBulbSchema );
var Blind = Device.discriminator("Blind", blindSchema );
var conn = mongoose.connect('mongodb://127.0.0.1/test', { useMongoClient: true });
conn.then(function(db){
var room = new Room({
name: 'Kitchen'
});
var devgroup = new DeviceGroup({
parentRoom: room._id,
groupType: 'LIGHTS'
});
var blind = new Blind({
name: 'blind1',
goingUp: false,
goingDown: true
});
blind.save();
var light = new LightBulb({
name: 'light1',
isPoweredOn: false,
currentColor: true
});
light.save();
devgroup.devices.push(blind._id);
devgroup.devices.push(light._id);
devgroup.save();
room.deviceGroups.push(devgroup._id);
room.save(function(err){
console.log(err);
});
// Room
// .find()
// .populate({
// path: 'deviceGroups',
// populate: {
// path: 'devices'
// }
// })
// .then(function(result){
// console.log(JSON.stringify(result, null, 4));
// });
}).catch(function(err){
});
Can you delete everything in you else statement and
console.log(room)
Check your mongo store to make sure you have data in your collection.
I have this schema where I validated the elements of array book, but I don't know how to validate the array itself.
var DictionarySchema = new Schema({
book: [
{
1: {
type: String,
required: true
},
2: String,
3: String,
c: String,
p: String,
r: String
}
]
});
For example, I would like to put the book array as required. Any help?
You can use a custom validator to do this. Simply check that the array itself is not empty:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var bookSchema = new Schema({
1: { type: String, required: true },
2: String,
3: String,
c: String,
p: String,
r: String
});
var dictSchema = new Schema({
books: [bookSchema]
});
dictSchema.path('books').validate(function(value) {
return value.length;
},"'books' cannot be an empty array");
var Dictionary = mongoose.model( 'Dictionary', dictSchema );
var dict = new Dictionary({ "books": [] });
dict.save(function(err,doc) {
if (err) throw err;
console.log(doc);
});
Which will throw an error when there is no content in the array, and otherwise pass off the validation for the rules supplied for the fields in the array.