MongoDB $geoWithin $centerSphere query - node.js

I wrote the following schema in my Node.js server:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var config = require('../../config.js');
var schema = new Schema({
title : { type: String, default: "generic"},
guide : String,
loc : {
type : {type: String, default:"Point"},
coordinates : [ ]
},
}
);
schema.index({"loc":"2dsphere"});
module.exports = mongoose.model(config.DATA_TYPE.SUPPORT, schema);
Then I wrote my dispatcher for adding, removing and research.
Adding and removing are ok but I have some problem with research.
This is my route:
router.route('/')
.post(function(req, res, next){
SupportManager.getGuides(req.body.lat, req.body.lon, function(err,data){
if(err){
return res.json({
success: false,
message: 756
});
}else{
return res.json({
success: true,
message: 856,
supports: data
});
}
});
})
where my SupportManager.getGuides is the following code:
getGuides: function(lat,lon,callback){
Support.find({ loc: { $geoWithin: { $centerSphere: [[ lon , lat], 10/3963.2]}}}, callback);
},
The strange behaviour is that I added the following object in my db:
{
"_id": "58bf2f07d5fd2a0cdde4cca9",
"guide": "a1",
"loc": {
"coordinates": [
"34",
"22"
],
"type": "Point"
},
"title": "tappp",
"__v": 0
}
But when I do the research, using lat=22 and long=34, I receive the answer with
success: true,
message: 856,
supports: []
The array "supports" is empty.

The code is perfect! Just the coordinates value are not saved as Number.
So the Schema should become:
var schema = new Schema({
title : { type: String, default: "generic"},
guide : String,
loc : {
type : {type: String, default:"Point"},
coordinates : [ { Number } ]
},
}
);
And the coordinates data in DB will be now
"loc": {
"coordinates": [
34,
22
],
"type": "Point"
},
while before the values were between the " " :
"loc": {
"coordinates": [
"34",
"22"
],
"type": "Point"
},

Related

Insert items into mongo array via mongoose

I have a mongo collection like the code below:
const ExerciseSchema = new Schema({
name: { type: String, required: true },
exercise: [
{
exerciseId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: Exercise,
},
period: { type: String, enum: ["day", "night"], required: true },
},
],
timestamp: { type: Date, default: Date.now() },
});
When I try to insert multiple exercises at the same time, considering that my exercises are an array, the mongo saves just the first register. For instance, I try to insert:
{
"name": "Exercise 1",
"exercise": [
{
"exerciseId": "1",
"period": "night"
},
{
"exerciseId": "1",
"period": "day"
}
]
}
And, after saving it, the get method returns me:
[
{
"timestamp": "2021-11-30T14:18:42.455Z",
"_id": "1",
"name": "Exercise 1",
"exercise": [
{
"exerciseId": "1",
"period": "night",
}
],
"__v": 0
}
]
That is, the mongoose saves just the first register in my array. Does anyone know why it is happening?
Hear it my create method from the controller:
exports.create = (req, res) => {
const {
name,
exercise: [{ exerciseId, period }],
} = req.body;
const newExercise = new Exercise({
name,
exercise: [{ exerciseId, period }],
});
newExercise.save((err, data) => {
if (err) {
res.status(500).send({ message: err });
return;
}
res
.status(200)
.send({
message: "Success",
});
});
};
Obs: I'm referring to my ExerciseSchema from the controller by "new Exercise(...)"
use it to update arrays:
parent ---> a mongooose doc
parent.child.push({new child object});
parent.markModified('child');
parent.save();
in your case child is exercise array and in parent.markModified('child'); you have to put parent.markModified('exercise');

mongoose pull command to delete product from shopping cart

CartController.prototype.addToCart =(req, res, next) => {
// Using mongoose in-built function "findbyID"
Product.findById({_id : req.params.productId}).then( item =>
{
if (!item) {res.status(400).send({message : "item not found"})}
Cart.findByIdAndUpdate(req.params.userId,
{
total_quantity : 0,
total_price : 0,
final_price : 0,
"$push": {"products": {
// Passing Data
productid : item._id,
MRP : item.offers.MRP,
finalprice : item.offers.finalprice,
discount : item.offers.discount,
organization : item.property.organization,
brand : item.property.brand,
color : item.property.color,
weight : item.property.weight,
category : item.property.category
}
},
userid : req.params.userId
},
{ upsert: true, returnNewDocument : true}
).then(() => {
res.status(200).send({message: "product added to cart"});
}).catch(err => {
res.status(500).send(err);
});
}).catch (err => {
res.status(500).send("item fetch related issue found", err);
});
};
//json output
[
{
"_id": "5e5a58843ed45a235c32ac8c",
"__v": 0,
"createdAt": "2020-03-16T18:04:31.370Z",
"updatedAt": "2020-03-16T18:41:23.068Z",
"userid": "5e5a58843ed45a235c32ac8c",
"inCart": false,
"products": [
{
"category": "Home Decor",
"weight": 300,
"color": "Multicolor",
"MRP": 5000,
"productid": "5e6f4234564e2d1b74ba3383",
"_id": "5e6fbfaf3818775ac010187e",
"quantity": 1,
"brand": "",
"organization": "",
"discount": 20
},
{
"category": "Home Decor",
"weight": 300,
"color": "Multicolor",
"MRP": 5000,
"productid": "5e6b21458f7019378c4981ff",
"_id": "5e6fbff53818775ac0101881",
"quantity": 1,
"brand": "",
"organization": "",
"discount": 20
}
],
"final_price": 0,
"total_price": 0,
"total_quantity": 0
}
]
//cart model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var product = new Schema({
productid : {
type: Schema.Types.ObjectId,
// required: true,
ref: 'Product'
},
MRP : {
type : Number,
// required : true
},
finalPrice : {
type : Number
},
discount : {
type : Number, default : 0
},
organization : {
type : String, default : null
},
brand : {
type : String, default : null
},
color : {
type : String
},
weight : {
type : Number
},
category : {
type : String,
// required :true
},
quantity : {
type : Number,
// required : true,
default : 1
}
});
var cartSchema = new Schema({
total_quantity : {type : Number, required : true, default: 0},
total_price : {type : Number, required : true, default: 0},
final_price : {type : Number, required : true, default: 0},
products : [product],
userid : {
type: Schema.Types.ObjectId,
required: true,
ref: 'Pujari'
},
inCart : {type : Boolean, default : 0}
},
{
timestamps:true
}
);
module.exports = mongoose.model('Cart', cartSchema);
//delete cart item
CartController.prototype.deleteCartItem = (req, res, next) => {
Cart.update({userid : req.params.userId}, { $pull: { products : {productid: req.params.productId }}}, {multi: true})
};
So, basically I have to collection one for product and another for cart, my question is how to delete particular product from cart like in bellow json I have to product. I just want to delete object where "productid": "5e6b21458f7019378c4981ff", I tried mongodb $pull operator but its not working for this data.
finally I found the solution, there was a small mistake I was doing
//delete cart item
CartController.prototype.deleteCartItem = (req, res, next) => {
Cart.update({userid : req.params.userId}, { $pull: { products : {category: "Home" }}}, function (err, data) {
if (err) res.send(err)
else res.send(data)
})
For me your update is working (for the given document):
db.collection.updateMany(
{ userid: "5e5a58843ed45a235c32ac8c" },
{ $pull: { "products": { productid: "5e6b21458f7019378c4981ff" } } }
)
{
"acknowledged" : true,
"matchedCount" : 1.0,
"modifiedCount" : 1.0
}

Why do I save the ObjectId('key'), instead of the values in my mongodb? [duplicate]

This question already has answers here:
How to push an array of objects into an array in mongoose with one call?
(4 answers)
Closed 3 years ago.
I'm using Angular 7 with node.js, express and mongoose
I have a problem when adding objects to my mongodb via mongoose, it saves only "_id": ObjectId('randomID') instead of the values i want.
I'm trying to add values to an existing document in my collection with nested arrays.
To be more precise: there are customers with a customer number, customer name and domains.
Domains shall be an array with a domain.
A domain has a name value and two more arrays (called "in" & "out")
both arrays contain a graph array with nodes and links.
nodes have 3 values (id, label, description) and links have also 3 values (source, target, label)
First Problem I have, is that I cant add new domain to my domains array
I don't understand what am I am doing wrong??
Or what is best practice to insert and/or update nested arrays inside nested arrays?
edit: I try to understand, why am I saving only the ObjectId() with no values, linked question doesn't work for me.
here is an Example of what i want in my db:
{
"_id" : ObjectId("5c76a093aac6fa3f140a5672"),
"KdNr" : "10004",
"Kundenname" : "Customer GmbH",
"__v" : 0,
"domains" : [ {
"domain" : "testB.de",
"in" : [ {
"content" : "New Graph B",
"graph" : { ... } } ],
"out" : [ {
"content" : "Another new Graph B",
"graph" : { ... } } ]
}, [ {
"domain" : "testA.de",
"in" : [ {
"content" : "New Graph A",
"graph" : { ... } } ],
"out" : [ {
"content" : "Another new Graph A",
"graph" : { ... } } ]
} ]
}
here is an Example of what i get (not what I want):
{
"_id" : ObjectId("5c76a093aac6fa3f140a5672"),
"KdNr" : "10004",
"Kundenname" : "Customer GmbH",
"__v" : 0,
"domains" : [ {
{ "_id" : ObjectId("5c7f86ad42d63141fc921d04") },
{ "_id" : ObjectId("5c655c828be0b2b295aa126f") }
] }
here is my Schema:
const mongoose = require('mongoose');
const graphSchema = mongoose.Schema({
graph: {
nodes: [{
id: { type: String, required: true, unique: true },
label: { type: String, required: true },
description: { type: String }
}],
links: [{
source: { type: String, required: true },
target: { type: String, required: true },
label: { type: String }
}]
}
});
const domainSchema = mongoose.Schema({
domain: {
name: { type: String, unique: true, required: true },
in: {
content: { type: String },
graphSchema
},
out: {
content: { type: String },
graphSchema
}
}
});
const diagramSchema = mongoose.Schema({
KdNr: { type: String, required: true, index: true, unique: true },
Kundenname: { type: String, required: true },
domains: [{
domainSchema
}]
});
module.exports = mongoose.model('Diagram', diagramSchema);
here is my domains-routes.js:
// core Modules
const express = require('express');
// own modules
const Diagram = require('../models/diagrams');
const router = express.Router();
// add Domain to Diagram
router.put('/:KdNr', (req, res, next) => {
console.log(req.body.domain);
const data = {
domains : [{
domain: req.body.domain,
in: [{
content : "New Graph",
graph : {}
}],
out: [{
content : "New Graph",
graph : {}
}]
}]
}
Diagram.updateOne(
{ KdNr: req.params.KdNr },
{ $push: data }
).then(result => {
if(result) {
console.log('Diagram found in database:');
console.log(result);
res.status(200).json({ message: 'Diagram saved' });
} else {
res.status(404).json({ message: 'Diagram for customer: '
+ req.params.KdNr + 'not found!'})
}
})
})
module.exports = router;
Try this in
Diagram.updateOne
{ $push:{"domains": data }}

Why Mongoose createdAt / updatedAt output a wrong time(only return current time not the time in db)

The Data stored in my db is:
{
"_id" : ObjectId("58da135cfc80bc44f7653fd4"),
"updatedAt" : ISODate("2017-03-28T08:00:59.541Z"),
"createdAt" : ISODate("2017-03-28T07:40:12.742Z"),
"name" : "hello",
"delete" : false,
"enabledPlugins" : [
ObjectId("58c24f65b363502f907738f9")
],
"__v" : 0
}
My Schema Like:
const mongoose = require('./db');
const { Schema } = mongoose;
const templateSchema = new Schema({
name: { type: String, index: true, unique: true },
enabledPlugins: [
{ type: Schema.Types.ObjectId }
],
delete: { type: Boolean, default: false }
}, {
timestamps: true
});
const Template = mongoose.model('Template', templateSchema);
module.exports = Template;
But When I want to get templates, I get the wrong timestamp:
exports.getAllTemplates = async function() {
return await Template.aggregate(
{ $match: { delete: false } },
{ $project: { id: '$_id', _id: 0, name: 1, enabledPlugins: 1, createdAt: 1 } }
);
};
The result like :
[
{
"createdAt": "2017-03-28T17:04:30.502+08:00",
"name": "hello",
"enabledPlugins": [
"58c24f65b363502f907738f9"
],
"id": "58da135cfc80bc44f7653fd4"
}
]
And I found before toJSON, the output has been wrong. I don't use any plugins. All the date type has the same problem.
Thanks, the problem is that I rewrite Date.prototype.toISOString

Mongoose - accessing nested object with .populate

Schema Definitions
Team.js
var TeamSchema = new Schema({
// Team Name.
name: String,
lead: String,
students :type: [{
block : Number,
status : String,
student : {
type: Schema.ObjectId,
ref: 'Student'
}]
});
Student.js
var StudentSchema = new Schema({
name: String,
rollNo : Number,
class : Number
});
How I can populate "student" to get output, as below:
team
{
"__v": 1,
"_id": "5252875356f64d6d28000001",
"students": [
{
"__v": 1,
"_id": "5252875a56f64d6d28000002",
block : 1,
status : joined,
"student": {
"name": Sumeeth
"rollNo" : 2
"class" : 5
}
},
{
"__v": 1,
"_id": "5252875a56f64d6d28000003",
block : 1,
status : joined,
"student": {
"name": Sabari
"rollNo" : 3
"class" : 4
}
}
],
"lead": "Ratha",
}
This is JS I use to get the document using Mongoose:
Team.findOne({
_id: req.team._id
})
.populate('students')
.select('students')
.exec(function(err, team) {
console.log(team);
var options = {
path: 'students.student',
model: 'Student'
};
Student.populate(team.students,options,function(err, students) {
console.log(students);
if (err) {
console.log(students);
res.send(500, {
message: 'Unable to query the team!'
});
} else {
res.send(200, students);
}
});
});
In my console output I get the following:
{ _id: 53aa434858f760900b3f2246,
students
[ { block : 1
status: 'joined'
_id: 53aa436b58f760900b3f2249 },
{ block : 1
status: 'joined'
_id: 53aa436b58f760900b3f2250 }]
}
And the expected output is:
{ _id: 53aa434858f760900b3f2246,
students
[ { block : 1
status: 'joined'
student :{
"name": Sumeeth
"rollNo" : 2
"class" : 5
}
},
{ block : 1
status: 'joined'
student :{
"name": Sabari
"rollNo" : 3
"class" : 4
}
}
]
}
Some one please help me where I am wrong. How should I make use of .populate, so that , I can get the entire student object and not only its id.
Reference :
Populate nested array in mongoose
I have been facing same issue. I have use this code for my rescue :
Team.findOne({_id: req.team._id})
.populate({ path: "students.student"})
.exec(function(err, team) {
console.log(team);
});
Here is a simplified version of what you want.
Basic data to set up, first the "students":
{
"_id" : ObjectId("53aa90c83ad07196636e175f"),
"name" : "Bill",
"rollNo" : 1,
"class" : 12
},
{
"_id" : ObjectId("53aa90e93ad07196636e1761"),
"name" : "Ted",
"rollNo" : 2,
"class" : 12
}
And then the "teams" collection:
{
"_id" : ObjectId("53aa91b63ad07196636e1762"),
"name" : "team1",
"lead" : "me",
"students" : [
{
"block" : 1,
"status" : "Y",
"student" : ObjectId("53aa90c83ad07196636e175f")
},
{
"block" : 2,
"status" : "N",
"student" : ObjectId("53aa90e93ad07196636e1761")
}
]
}
This is how you do it:
var async = require('async'),
mongoose = require('mongoose');
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/team');
var teamSchema = new Schema({
name: String,
lead: String,
students: [{
block: Number,
status: String,
student: {
type: Schema.ObjectId, ref: 'Student'
}
}]
});
var studentSchema = new Schema({
name: String,
rollNo: Number,
class: Number
});
var Team = mongoose.model( "Team", teamSchema );
var Student = mongoose.model( "Student", studentSchema );
Team.findById("53aa91b63ad07196636e1762")
.select('students')
.exec(function(err, team) {
console.log( team );
async.forEach(team.students, function(student,callback) {
Student.populate(
student,
{ "path": "student" },
function(err,output) {
if (err) throw err;
callback();
}
);
},function(err) {
console.log( JSON.stringify( team, undefined, 4 ) );
});
});
And it gives you the results:
{
"_id": "53aa91b63ad07196636e1762",
"students": [
{
"block": 1,
"status": "Y",
"student": {
"_id": "53aa90c83ad07196636e175f",
"name": "Bill",
"rollNo": 1,
"class": 12
}
},
{
"block": 2,
"status": "N",
"student": {
"_id": "53aa90e93ad07196636e1761",
"name": "Ted",
"rollNo": 2,
"class": 12
}
}
]
}
You really do not need the "async" module, but I am just "in the habit" as it were. It doesn't "block" so therefore I consider it better.
So as you can see, you initial .populate() call does not do anything as it expects to "key" off of an _id value in the foreign collection from an array input which this "strictly speaking" is not so as the "key" is on "student" containing the "foreign key".
I really did cover this in a recent answer here, maybe not exactly specific to your situation. It seems that your search did not turn up the correct "same answer" ( though not exactly ) for you to draw reference from.
You are overthinking it. Let Mongoose do the work for you.
Team.findOne({
_id: req.team._id
})
.populate({path:'students'})
.exec(function(err, team) {
console.log(team);
});
This will return students as documents rather than just the ids.
TL DR
const team = await Team.findById(req.team._id)
.populate("students");
team.students = await Student.populate(team.students, {path: "student"});
Context
Reading from all the answers I went testing everything and just Neil Lun's answer worked for me. The problem is it was on the path to a cb hell. So I cracked my head a little and 'refactored' to an elegant one-liner.
const foundPost = await Post.findById(req.params.id)
.populate("comments")
.populate("author");
foundPost.comments = await User.populate(foundPost.comments, {path: "author"});
My initial problem:
{
title: "Hello World",
description: "lorem",
author: {/* populated */},
comments: [ // populated
{text: "hi", author: {/* not populated */ }}
]
};
How my models basically are:
User = {
author,
password
};
Post = {
title,
description,
author: {}, //ref User
comments: [] // ref Comment
};
Comment = {
text,
author: {} // ref User
};
The output after problem solved:
{
comments: [
{
_id: "5dfe3dada7f3570b60dd977f",
text: "hi",
author: {_id: "5df2f84d4d9fcb228cd1df42", username: "jo", password: "123"}
}
],
_id: "5da3cfff50cf094c68aa2a37",
title: "Hello World",
description: "lorem",
author: {
_id: "5df2f84d4d9fcb228cd1aef6",
username: "la",
password: "abc"
}
};

Resources