conditional $match query based on key value in mongo db - javascript

#Schema()
export class GlobalSearch {
//Commonn Fields
#Prop({
type:String,
})
id: string
#Prop({
type:String,
index:true
})
name:string
#Prop({
type:String,
index:true
})
workspaceId:string
#Prop({
type:String,
index:true
})
status:string
//Project Fields
#Prop({type: raw([{
userId: {
type:String,
index:true
},
userName: {
type: String,
index:true
},
image: {
type: String,
}
}])})
projectMembers: Record<string, any>
#Prop({
type: String,
index:true
})
brandName:string
//Task Fields
#Prop({
type:String,
index:true
})
taskStatus:string
#Prop({type: raw([{
userId: {
type:String,
index:true
},
userName: {
type:String,
index:true
},
image: { type: String }
}])})
taskAssignee: Record<string, any>
//Task Types
#Prop({
type:String,
index:true,
enum:TaskTypes
})
type:string
//Vault Fields
#Prop({
type:String,
index:true
})
fileType:string
#Prop({
type:String,
index:true
})
displayName:string
#Prop({
type:String,
index:true
})
filePath:string
#Prop({
type:String,
index:true,
enum:DocumentType //project,task,vault
})
documentType: string
}
Data Saving structure
Note:- When documentType is project then only project field are saved in collection which is
(id name workspaceId status projectMembers brandName documentType) and
when documentType is task then we save this
(id name workspaceId status taskStatus taskAssignee type documentType ) and
when documentType is vault then we save
( id name workspaceId fileType displayName filePath documentType)
Requirement:- I need to get all data based by default but when applying filter it should be applied on separatly based on documentType
for example:- When I filter data with "projectMembers.userId" then it should applied only where documentType is project but when filter with both "projectMembers.userId" and "taskAssignee.userId" then it should be like "projectMembers.userId" this should be applied on project and "taskAssignee.userId" should be applied on task only save for vault type too.
let filterMatchData = {}
if(searchIn.includes('task') && UserRole[accessLevel] == 'Workspace-admin' || UserRole[accessLevel] == 'Workspace-owner'){
if(taskType && taskType.length > 0){
filterMatchData.let = {
taskType:'$taskType'
}
filterMatchData["$in"] = ['type','$$taskType']
}
// if(taskAssignee && taskAssignee.length > 0){
// filterMatchData.let = {
// taskType: '$taskType'
// }
// filterMatchData."taskAssignee.userId" = {$in:taskAssignee}
// }
if(taskStatus){
filterMatchData.taskStatus=taskStatus
}
}
let filterData = {
$match: {
$expr: {
$switch: {
branches: [
{
case: {
documentType:"task"
},
then: filterMatchData
},
{
case: {
documentType:"vault"
},
then: {
fileExtension:"png"
}
}
],
default: {}
}
}
}
}

Related

How to find all children (depth up to 5 levels)mongodb (mongoose)?

i have table model User
const schema = new mongoose.Schema(
{
telegramId_parent: {
type: Number,
default: null,
},
telegramId: {
type: Number,
required: true,
unique: true,
},
telegramName: {
type: String,
},
},
{ timestamps: true }
);
So I can find the first level
User.find({ telegramId_parent: id })
.then((user) => {
resolve(user);
})
.catch((err) =>
);
accordingly, after I can make the same query for the result through recursion 5 times.
maybe there is another way? better? can the data be stored differently?

Can Sequelize use something like a getter or setter in a find query?

For my email field, I store and query emails as lowercase strings to avoid duplicate emails like user#example.com and User#example.com. I have a set method defined in my model like:
const User = sequelize.define('user', {
email: {
type: DataTypes.STRING,
unique: { msg: 'That email is already registered.' },
validate: { isEmail: { msg: 'Invalid email.' } },
set(value) {
this.setDataValue('email', value.toLowerCase().trim())
}
}
})
This prevents setting emails with uppercase letters, but it does not prevent queries with uppercase letters. To avoid queries, I have to remember to use .toLowerCase() everywhere. It would be better if I could define it on the model so that a query like this would just work:
const user = await User.findOne({ where: { email: 'SomeEmail#example.com' } })
You can use the hooks in models to store the email in lower case.
Please have a look in below example
const createUserModel = (sequelize, { STRING, UUIDV4, UUID, DATE }) => {
const User = sequelize.define(
'User',
{
userId: {
type: UUID,
defaultValue: UUIDV4,
primaryKey: true,
},
email: {
type: STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true,
},
},
password: {
type: STRING,
allowNull: true,
},
},
{
freezeTableName: true,
timestamps: false,
hooks: {
beforeCreate: async instance => {
const email = instance.get('email');
instance.set('email', email.toLowerCase());
},
beforeUpdate: async instance => {
if (instance.changed('email')) {
const email = instance.get('email');
instance.set('email', email.toLowerCase());
}
},
},
},
);
return User;
};
module.exports = {
createUserModel,
};

defining 2 types under 1 variable in mongodb

I'm trying to create a Schema that looks like this:
const exampleSchema = mongoose.Schema({
topic: {
type: String,
required: true,
},
words: {
type: String || Array: {
type: String,
required: true,
}
required: true,
}
});
I've read about custom types in mongodb but don't understand what the documentation is showing me. Could anyone help me out?
Creating custom schema in mongoose,
class StringOrArray extends mongoose.SchemaType {
constructor(key, options) {
super(key, options, 'StringOrArray');
}
cast(val) {
// please change your logic as per your requirement
if (typeof val !== 'string' && !Array.isArray(val)) {
throw new Error('StringOrArray: ' + val + ' must be a String or Array');
}
return val;
}
}
// Don't forget to add `Int8` to the type registry
mongoose.Schema.Types.StringOrArray = StringOrArray;
Use StringOrArray in your schema,
const exampleSchema = mongoose.Schema({
topic: {
type: String,
required: true,
},
words: {
type: StringOrArray,
required: true
}
});

Mongoose + GraphQL cannot populate model [duplicate]

This question already has answers here:
What is the correct type to use for an ObjectId field across mongoose and GraphQL?
(3 answers)
Closed 3 years ago.
Well, I know there are a lot of questions regarding this issue but neither of their solutions worked for me, or I can't find what is happening.
I have a backend server running Apollo Server + Mongoose on localhost. My issue is I can't populate or create the collection since I get:
Error: "ID cannot represent value: <Buffer 5e 38 65 18 f1 e3 f5 43 10 d4 c1 45>".
This is the model:
import mongoose from 'mongoose';
const Float = require('mongoose-float').loadType(mongoose);
const gastoSchema = new mongoose.Schema({
descripcion: String,
importe: Float,
proveedor: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Proveedor'
},
}, {
timestamps: {
createdAt: 'fechaCreacion',
updatedAt: 'fechaActualizacion'
}
});
export default mongoose.model('gastos', gastoSchema);
This is my type definition:
import { gql } from 'apollo-server';
export default gql`
type Gasto {
id: ID
descripcion: String
importe: Float
proveedor: Proveedor
}
extend type Query {
gastos: [Gasto]
gasto(id: ID!): Gasto
}
extend type Mutation {
crearGasto(descripcion: String, importe: Float, proveedor: ID): Gasto
actualizarGasto(id: ID!, descripcion: String, importe: Float, proveedor: ID): Gasto
eliminarGasto(id: ID!): String
}
`;
And this is my resolver:
import Gasto from '../models/Gasto';
export default {
Query: {
gastos: () => Gasto.find().populate('proveedores').exec(),
gasto: (_, { id }) => Gasto.findById(id).populate('proveedores').exec()
},
Mutation: {
crearGasto: (_, input) => Gasto.create(input),
actualizarGasto: (_, input) => Gasto.findOneAndUpdate(input),
eliminarGasto: (_, { id }) => Gasto.findOneAndDelete(id)
}
};
Attempt 1
I have tried this change on my model definition but it didn't work:
import mongoose from 'mongoose';
const Float = require('mongoose-float').loadType(mongoose);
const ObjectId = require('mongoose').Types.ObjectId;
ObjectId.prototype.valueOf = function() { return this.toString(); }
const gastoSchema = new mongoose.Schema({
descripcion: String,
importe: Float,
proveedor: {
type: ObjectId,
ref: 'Proveedor'
},
}, {
timestamps: {
createdAt: 'fechaCreacion',
updatedAt: 'fechaActualizacion'
}
});
export default mongoose.model('gastos', gastoSchema);
Attempt 2
According to the mongoose documentation I also tried switching between populate('proveedores') (plural), populate('proveedor') (singular) but the error changes:
import Gasto from '../models/Gasto';
export default {
Query: {
gastos: () => Gasto.find({}).populate('proveedor').exec(),
gasto: (_, { id }) => Gasto.findById(id).populate('proveedor').exec()
},
Mutation: {
crearGasto: (_, input) => Gasto.create(input),
actualizarGasto: (_, input) => Gasto.findOneAndUpdate(input),
eliminarGasto: (_, { id }) => Gasto.findOneAndDelete(id)
}
};
Error: "message": "Schema hasn't been registered for model \"Proveedor\".\nUse mongoose.model(name, schema)".
Just in case this is my Proveedor model definition:
import mongoose from 'mongoose';
const proveedorSchema = new mongoose.Schema({
nombre: {
type: String,
required: true
},
telefono: String,
direccion: String,
email: String,
}, {
timestamps: {
createdAt: 'fechaCreacion',
updatedAt: 'fechaActualizacion'
}
});
export default mongoose.model('proveedores', proveedorSchema);
This is what i am querying on the GraphQL playground:
query {
gastos {
id
importe
proveedor {
id
nombre
}
}
}
Any clues? Thanks in advance.
The first argument of mongoose.model() must be the singular name of the collection your model is for. Mongoose automatically looks for the plural, lowercased version of your model name, so for your case:
mongoose.model('Gasto', gastoSchema);
mongoose.model('Proveedor', proveedorSchema);
Next you have to add a resolver for the proveedor field and .exec() to execute the mongoose queries
import Gasto from '../models/Gasto';
import Proveedor from '../models/Proveedor';
export default {
export default {
Gasto: {
proveedor: ({ proveedor }) => Proveedor.findById(proveedor).exec()
},
Query: {
gastos: () => Gasto.find().exec(),
gasto: (_, { id }) => Gasto.findById(id).exec()
},
Mutation: {
crearGasto: (_, input) => Gasto.create(input).exec(),
actualizarGasto: (_, input) => (_, { id: _id, ...input }) =>
Gasto.findOneAndUpdate({ _id }, input, { new: true }).exec(),
eliminarGasto: (_, { id: _id }) => Gasto.findOneAndDelete({ _id }).exec()
}
}
note that the resolver for actualizarGasto must be
actualizarGasto: (_, { id: _id, ...input }) => Gasto.findOneAndUpdate({ _id }, input, { new: true }).exec()

How to update existing object with additional data

The project is created with nodejs and mongoose. What I am trying to do is to update the existing model with addition data (which is a comment, in that case).
This is the model and its methods:
const bugSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
date: {
type: String,
required: true
},
time: {
type: String,
required: true
},
assignedTo: {
type: String,
required: true
},
assignedBy: {
type: String,
required: true
},
status: {
type: String,
required: true
},
priority: {
type: String,
required: true
},
comments: {
comment:[
{
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
}
]
}
});
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
const updatedComments = [...this.comments];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
The controller, which is passing the information from the form:
exports.postComment = (req,res,next) =>{
const bugId = req.body.bugID;
const name = req.session.user.fullName;
const content = req.body.content;
const prod = {name, content};
Bug.findById(bugId).then(bug =>{
return bug.addComment(prod);
})
.then(result =>{
console.log(result);
});
};
I am getting a following error:
(node:3508) UnhandledPromiseRejectionWarning: TypeError: this.comments is not iterable
(node:3508) UnhandledPromiseRejectionWarning: TypeError: this.comments is not iterable
The error indicate you're trying to iterable a type of data which does NOT has that capability.
You can check that printing the type:
console.log(typeof this.comments)
Or even, priting the whole object:
console.log(this.comments)
as you can see, in both cases you're getting an object, not a list (how you spect)
So you can do 2 things:
1- Iterable a list
this.comments is an object but into that object you have the list you want, so just use the list instead.
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
//const updatedComments = [...this.comments];
const updatedComments = [...this.comments.comment];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
Or you can modify your schema making the comments a list instead of an object
2- comments as list in schema
Define the comments attribute as a list
const bugSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
...
...,
comments:[
{
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
}
]
});
And then, try to iterable it as how you been doing
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
const updatedComments = [...this.comments];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
I am not sure but comments is an object and not an array so you can't push using [...this.comments] and I think it is the comment you want to push?
const updatedComments = [...this.comment];
updatedComments.push({
user : username,
content: content
});
this.comment = updatedComments;
From your schema comments is not an array. you are trying to spread an object into an array. const updatedComments = [...this.comments]; also push works on array.
try to modify your schema definitions by declaring the commentSchema outside the bugSchema.
const commentSchema = new Schema({
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
})
const bugSchema = new Schema({
comments: {
type: [commentSchema]
}
})
Bug.findByIdAndUpdate(bugId, {$push: {comments: newComment}})
Don't use findByIdAndUpdate Mongoose method, you better use save
it is written here https://mongoosejs.com/docs/tutorials/findoneandupdate.html
The findOneAndUpdate() function in Mongoose has a wide variety of use cases. You should use save() to update documents where possible, but there are some cases where you need to use findOneAndUpdate(). In this tutorial, you'll see how to use findOneAndUpdate(), and learn when you need to use it.
Below a router example
router.put('/items', (req, res) => {
if (!req.body._id || !req.body.title) {
return res.status(501).send({ message: 'Missing parameters, or incorrect parameters' });
}
return itemModel.findOne({ _id: req.body._id }, (err, item) => {
if (err) {
return res.status(500).send({
message: err
});
}
item.title = req.body.title; // <------------- You rewrite what was before stored on title attribute
return item.save((err, item) => { // <------------- You save it, this is not gonna create a new one, except if it doesn't exist already
if (err) {
return res.status(400).send({
message: 'Failed to update item'
});
} else {
return res.status(200).send({
message: 'Item update succesfully',
data: item
});
}
});
});
});

Categories

Resources