I have 3 collections: business, sevice, employee.
I need search by service (without fulltext), by employee and by the geolocation of each business, and should show only business.
var BusinessSchema = new Schema({
business_id: {
type: String,
required: true,
unique:true
},
name: {
type: String,
required: true
},
email: {
type: String,
},
description:{
type: String
},
location:{
country:{
type:String,
},
city:{
type:String
},
coord:[Number]
}
services:[{
type: Schema.Types.ObjectId,
ref: 'Service'
}]
},
{
timestamps: true
});
var ServiceSchema = new Schema({
business:{
type: Schema.Types.ObjectId,
ref: 'Business'
},
category:{
type: Schema.Types.ObjectId,
ref: 'Category',
index:true
},
name: {
type: String,
required: true,
index:true
},
employee: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
{
timestamps: true
});
var UserSchema = new Schema({
birthday:Date,
first_name: {
type: String,
required: true
},
last_name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address'],
required: true
},
password:{
type: String
},
{
timestamps: true
});
What changes should i make collections to optimize the query?
Service.distinct('business',filter_services)
.exec(function (err, business) {
if (err) {
return cb({ status: 400, message: err }, null);
} else {
if(business.length > 0){
var filter_business = [{is_active:true},{is_approved:true}]
filter_business.push({_id:{$in:business}})
filter_business.push({location.coord:input_coord}})
filter_business = {$and:filter_business}
Business.find(filter_business)
.select('name services')
.exec(function (err,result){
if(err){
return cb({ status: 400, message: err }, null);
}
else{
if(result.length > 0){
var total = result.length;
}
return cb(null, result);
}
})
}
// si no hay business en el primer query, se retorna [].
else{
return cb(null, business);
}
}
});
Could geo filter by text and at the same time to get closer to a point?
For now, i am not using the Employee collection, but, if i would search by business name, employee name and service name simultaneously, what changes should make.
Your business model should be
var BusinessSchema = new Schema({
business_id: {
type: String,
required: true,
unique:true
},
name: {
type: String,
required: true
},
email: {
type: String,
},
description:{
type: String
},
address:{
country:{
type:String,
},
city:{
type:String
}
},
location : {
type: [Number],
index: '2dsphere'
},
services:[{
type: Schema.Types.ObjectId,
ref: 'Service'
}],
employees:[{
type: Schema.Types.ObjectId,
ref: 'User'
}]
},
{
timestamps: true
});
The change in schema is you have to create index on location, and for find business based on employee have to add employees id in business schema.Then you can you geo near query of mongodb.
Related
In my NodeJS API and MongoDB, I'm trying to delete a record which is a reference to another collection.
What I would like to do is to delete the referred objectId and the records related to the other collection which is referred.
I have 2 models Profiles and Posts and I want to delete the same one post from Profile and Post collection.
I was able to delete the reference id in Profile but I don't know how to delete also the record from Posts collection.
I tried this:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
err => {
if (err) {
throw new ErrorHandlers.ErrorHandler(500, err);
}
res.json({ Message: "Deleted" });
}
);
} catch (error) {
res.status(500).send(error);
}
}
And my 2 models:
// Here defining profile model
// Embedded we have the Experience as []
const { Connect } = require("../db");
const { isEmail } = require("validator");
const postSchema = {
type: Connect.Schema.Types.ObjectId,
ref: "Post"
};
const experienceSchema = {
role: {
type: String,
required: true
},
company: {
type: String,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: false
},
description: {
type: String,
required: false
},
area: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
username: {
type: String,
required: false
},
image: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
}
};
const profileSchema = {
firstname: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: [true, "Email is required"],
validate: {
validator: string => isEmail(string),
message: "Provided email is invalid"
}
},
bio: {
type: String,
required: true
},
title: {
type: String,
required: true
},
area: {
type: String,
required: true
},
imageUrl: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
},
username: {
type: String,
required: true,
unique: true
},
experience: [experienceSchema],
posts: [postSchema],
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
}
};
const collectionName = "profile";
const profileSchemaModel = Connect.Schema(profileSchema);
const Profile = Connect.model(collectionName, profileSchemaModel);
module.exports = Profile;
const { Connect } = require("../db");
const reactionSchema = {
likedBy: {
type: String,
unique: true,
sparse: true
}
};
const postSchema = {
text: {
type: String,
required: true,
unique: true,
sparse: false
},
profile: {
type: Connect.Schema.Types.ObjectId,
ref: "Profile",
},
image: {
type: String,
default: "https://via.placeholder.com/150",
required: false
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
reactions: [reactionSchema],
comments: {
type: Connect.Schema.Types.ObjectId,
ref: "Comment",
required: false
}
};
const collectionName = "post";
const postSchemaModel = Connect.Schema(postSchema);
const Post = Connect.model(collectionName, postSchemaModel);
module.exports = Post;
Just add a query to remove the post after pulling it's ID from the profile collection:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
// You don't need an error callback here since you are
// using async/await. Handle the error in the catch block.
);
await Posts.remove({ _id: req.params.postId });
} catch (error) {
// This is where you handle the error
res.status(500).send(error);
}
}
I have a collection of jobs in MongoDB and in this collection there is a field in the documents named as appliedUser. I want to update this field when a new user applies for this job.So basically this field stores the id's of all the users who are applying for this job.
I am using findOneAndUpdate() function but not able to do it.
Job.findOneAndUpdate({ _id: req.params.id }, { $set: { appliedUser:
req.user.id } }, function(err, job) {
console.log(job);
})
and here is my Schema:
const jobSchema = new Schema({
date: { type: Date, default: Date() },
expirydate: { type: Date, required: true },
name: { type: String, required: true },
companydetails: { type: String, required: true },
address: { type: String, required: true },
role: { type: String, required: true },
city: { type: String, required: true },
minsalary: { type: Number, required: true },
maxsalary: { type: Number, required: true },
skills: { type: Array, required: true },
minex: { type: Number, required: true },
appliedUser: [{ type: Schema.Types.ObjectId, ref: 'users', unique:
true }],
user: { type: String, required: true }
})
The array of the document is not updating. I am not able to find the errors.
Look like what you need is $addToSet. Example:
Job.findOneAndUpdate({ _id: req.params.id }, { $addToSet: { appliedUser: req.user.id } }, function(err, job) {
console.log(job);
})
I have a function to update some values from a subdocument, but I am having troubles updating the data
let query = InvoiceModel.find({
is_draft:false
products.product: productid
})
query.exec(function (err, data) {
if (data) {
data.forEach(function(si) {
let saleinvoice_id = si._id
console.log(saleinvoice_id);
InvoiceMovement(invoice_id, si)
})
}
})
In the InvoiceMovement function I search for the record in the collection PRODUCT to update and try to update the array like this:
productModel.findOneAndUpdate({
_id: product_id,
'warehouses.warehouse': warehouse_id
}, {
$set: {
"warehouses.$.stock": stock_data,
}
}, function (errwu, wupdated) {
if (errwu) {
console.log(errwu);
}
if (wupdated) {
console.log(wupdated);
}
})
But not all the data is processed.
For an specific ID I have 4 invoices records, but only two or less affect the value to update in the product collection
I try using async.forEach , but I get the same result.
UPDATE
Schemas:
INVOICE
const InvoiceSchema = Schema({
invoice_number: {
type: String,
},
products: [{
product: {
type: Schema.Types.ObjectId,
ref: 'products',
required: [true, 'Producto no puede estar vacio']
},
warehouse: {
type: Schema.Types.ObjectId,
ref: 'warehouses'
},
work_order: {
type: Schema.Types.ObjectId,
ref: 'work_orders'
},
quantity: {
type: Number,
required: [true, 'Cantidad no puede estar vacio']
},
base_price: {
type: String,
required: [true, 'Valor Unitario no puede estar vacio']
},
sale_price: {
type: String,
required: [true, 'Valor Unitario no puede estar vacio']
},
discount: {
type: String,
default: "0.00"
},
subtotal: {
type: String,
required: true
},
tax: {
type: String,
required: true
},
total: {
type: String,
required: true
},
}]
}, {
timestamps: true
});
PRODUCT
const productSchema = Schema({
code: {
type: String,
index: true
},
name: {
type: String,
index: true,
required: [true, 'Nombre no puede estar vacio']
},
description: {
type: String,
required: [true, 'Descripcion no puede estar vacio']
},
unit: {
type: String,
index: true,
required: [true, 'Unidad no puede estar vacio']
},
exempt: {
type: Boolean,
default: false
},
product_category: {
type: Schema.Types.ObjectId,
ref: 'product_categories',
required: [true, 'CategorÃa de Producto es requerida']
},
base_price: {
type: String,
default: "0.00"
},
unit_cost: {
type: String,
default: "0.00"
},
warehouses: [
{
warehouse: {
type: Schema.Types.ObjectId,
ref: 'warehouses',
required: [true, "Seleccione una caracteristica"]
},
stock: {
type: Number,
},
unit:{
type: String
}
}
],
subproducts: [
{
subproduct: {
type: Schema.Types.ObjectId,
ref: 'characteristics',
// required: [true, "Seleccione una caracteristica"]
},
}
],
stock: {
type: Number,
default: 0
}
}, {timestamps: true});
From the Invoice Schema I go through the products array, to get the product ID and the quantity, with that data I update the stock in the warehouses array inside the PRODUCT schema.
I Need to do this for every Invoice.
One invoice can have many product registered
Below is one way you can do this. However, this might be easier if you use mongoose.promise.
InvoiceModel.find({}, 'products', function(err, invoice) {
if(err) throw err
const products = invoice.products
// Loop through all the objects in the array
products.forEach(function(productsObj) {
// Get the product model for each product
ProductModel.findById(productsObj.product, 'warehouses', function(err, product) {
if(err) throw err
// Get a new array of only ids
const warehousesIds = product.warehouses.map(warehousesObj => {
return warehousesObj.warehouse
})
// Get the index of the current warehouse in the warehouses array in your product model
const warehouseIndex = warehousesIds.indexOf(productsObj.warehouse)
// Check if warehouse exists and if so assign the new value
if(warehouseIndex > -1) product.warehouses[warehouseIndex].stock = productsObj.quantity
// Save your model
product.save()
})
})
})
Hope it helps!
I'm wondering if there a best way to do this:
/**
* Article Schema
*/
var PostSchema = new Schema({
title: {
type: String,
required: true,
trim: true
},
author:{
type: String,
required: true,
default: 'whisher'
},
slug: {
type: String,
index: { unique: true }
},
body: {
type: String,
required: true,
trim: true
},
avatar:{
type: String,
required: true
},
status: {
type: String,
required: true,
trim: true
},
created: {
type: Date,
required: true,
default: Date.now
},
published: {
type: Date,
required: true
},
categories: {
type: [String]
},
tags: {
type: [String],
required: true,
index: true
},
comment: {
type: Schema.Types.ObjectId,
ref: 'CommentSchema'
},
meta: {
votes: {
type: Number,
default: 0
},
comments: {
type: Number,
default: 0
}
}
});
/**
* Comment Schema
*/
var CommentSchema = new Schema({
post_id: {
type: Schema.Types.ObjectId,
ref: 'Post',
required: true
},
author:{
type: String,
required: true
},
email:{
type: String,
required: true
},
web:{
type: String
},
body: {
type: String,
required: true,
trim: true
},
status: {
type: String,
required: true,
default: 'pending'
},
created: {
type: Date,
required: true,
default: Date.now
},
meta: {
votes: Number
}
});
/**
* Create a comment
*/
exports.create = function(req, res) {
var comment = new Comment(req.body);
comment.save(function(err) {
if (err) {
return res.jsonp(500,{ error: 'Cannot save the comment' });
}
Post.findById(comment.post_id).exec(function(err, post) {
if (err) {
return res.jsonp(404,{ error: 'Failed to load post with id ' + comment.post_id });
}
if (!post) {
return res.jsonp(404,{ error: 'Failed to load post with id ' + comment.post_id });
}
post.meta.comments = post.meta.comments++;
post.save(function(err) {
if (err) {
return res.jsonp(500,{ error: 'Cannot update the post' });
}
res.jsonp(200,comment);
});
});
});
};
Btw I just looking at http://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate
but like this:
Model.findByIdAndUpdate(comment.post_id, { post.meta.comments: post.meta.comments++ })
doesnt work
I think you need to use the $inc operator to increment the comment count like this...
Post.findByIdAndUpdate(comment.post_id, { $inc: {"meta.comments" : 1} }, callback);
I'm using mean stack to create a hybrid app.I'm using nosql to create DB in mongoose.My DB consists of two tables one is 'donors' and another one is 'bloodGroup'.
My 'bloodGroup' schema is as follows:
module.exports = function(mongoose) {
var Schema = mongoose.Schema;
/* bloodGroup Schema */
var bloodGroupSchema = new Schema({
name: { type: String, required: true }
});
}
My 'Donor'schema is as follows:
/* Donor Schema */
var DonorSchema = new Schema({
Name: { type: String, required: true },
DOB: { type: Date, required: true, trim: true },
Sex: { type: String },
BloodGroupID: { type: Schema.Types.ObjectId, ref: 'BloodGroup', required: true },
ContactNo: { type: String, required: true },
LocationId: { type: Schema.Types.ObjectId, ref: 'Location', required:true },
EmailId: { type: String, required: true },
Password: { type: String, required: true }
});
When many donors refer to a single blood group then BloodGroup object Id error is reported.How to solve this problem?
You can refer this link for documentation: http://mongoosejs.com/docs/populate.html
Saving Refs
/* Donor Schema */
var DonorSchema = new Schema({
_id : {type: Number},
Name: { type: String, required: true },
DOB: { type: Date, required: true, trim: true },
Sex: { type: String },
BloodGroupID: { type: Schema.Types.ObjectId, ref: 'BloodGroup', required: true },
ContactNo: { type: String, required: true },
LocationId: { type: Schema.Types.ObjectId, ref: 'Location', required:true },
EmailId: { type: String, required: true },
Password: { type: String, required: true }
});
/* bloodGroup Schema */
var bloodGroupSchema = new Schema({
_bid :{ type: Number, ref: 'Donor' },
name: { type: String, required: true }
});
module.exports = mongoose.model('Donor', DonorSchema);
module.exports = mongoose.model('Blood', bloodGroupSchema);
var vidya = new Donor({ _id: 0, name: 'Vidya', sex: 'F' });
vidya.save(function (err) {
if (err) return handleError(err);
var blood = new BloodGroup({
name: 'B+',
_bid: vidya._id // assign the _id from the Donor
});
blood.save(function (err) {
if (err) return handleError(err);
// thats it!
});
});
Mongo is not a Relational database, relation one to many does not exist in mongDB. The question is quite confusing, but following the title, you should either embed the donnors into the BloodGroup, or create an Id field unique to which you will refer and do two queries.