create "dynamic query" in nodejs with sequelize? - node.js

I was wondering if it was possible to make a "dynamic condition" with sequelize using findOne/findOrCreate etc instead of row SQL query
i use it with graphQL and arguments could be optionnal in graphQL, so what i want to do is :
if i have id and name as argument :
user.findOrCreate({
where: {
id: args.id,
name : args.name,
}
})
and if i have id, name and email :
user.findOrCreate({
where: {
id: args.id,
name : args.name,
email: args.email,
}
})
so it is possible to have a parameter inside the findOrCreate to check if something exist(here, args.email) and if not, doesn't include it inside the query ?

You can create the the function and import that function:
export const findOrCreate = async function (query) {
try {
const user = await user.findOrCreate(query);
return user;
} catch (e) {
throw Error('Error occur while finding or creating the records’);
}
};
wherever you need import that function and call with the required parameters
const query = {
where: { // we search for this user
id: args.id,
name : args.name,
email: args.email,
},
defaults: {
job: 'Technical Lead JavaScript'
} // if it doesn't exist, we create it with this additional data
}
await findOrCreate(query )

Related

Update document in MongoDB via NodeJS

So my knowledge of NodeJS and MongoDD are non-existent (just need to do a small code update for a friend) and I'm stuck.
Need to update a single document inside a collection via a unique id but can't seem to do it.
Here's the Model (I've trimmed it down and cut out all unnecessary data). I'm trying to update the field notes inside a transaction.
In short each entry in the given (an Agent) table will have a collection of multiple Transactions & Documents. I need to update a specific Transaction with the unique _id that is auto generated.
import { Schema, model } from 'mongoose';
interface Transaction {
first_name: string;
last_name: string;
type: string;
notes: string;
}
interface Agent {
org_id: number;
transactions: Array<Transaction>;
documents: Array<string>;
}
const transactionSchema = new Schema<Transaction>({
first_name: { type: String },
last_name: { type: String },
type: { type: String },
notes: String,
});
const transactionsSchema = new Schema<Agent>({
org_id: { type: Number },
transactions: [transactionSchema],
documents: [documentTypesSchema],
});
const AgentTransaction = model<Agent>(
'agent_transaction_table',
transactionsSchema
);
export default AgentTransaction;
Here's what I tried but didn't work (obviously), again I've trimmed out all unnecessary data. Just to clarify, the endpoint itself works, but the DB update does not.
import AgentTransaction from '../models/transaction'; // the above model
transaction.put('/notes', async (req, res) => {
const { org_id, transaction_id, notes } = req.body;
try {
const notesResult = await AgentTransaction.updateOne({
'transactions._id': transaction_id,
}, {
$set: {
'notes': notes
},
});
res
.status(200)
.json({ message: 'Updated', success: true, notesResult });
} catch (error) {
res.status(400).send(error);
}
});
So I figured it out. Maybe it'll help someone else as well.
const notesResult = await AgentTransaction.updateOne({
'transactions._id': { $in: [trunc2] },
}, {
$set: {
'transactions.$.notes': notes
},
});
The main issue was that the payload object needed to target the collection folder + the wildcard + the field, not just only the field.

mongoose filter by multiple conditions and execute to update data

I am wondering what would be the best approach to make schema functions using mongoose. I have never used this so the way I think is somewhat limited, same goes for looking for docs, without knowing what's available, is not very efficient.
Through docs I found that either using findOneAndUpdate might solve the problem; but there are some constraints.
Here is the code I am planning to run:
models/Bookmark.js
const mongoose = require('mongoose')
const bookmarkItemSchema = new mongoose.Schema({
restaurantId: String,
cachedAttr: {
name: String,
latitude: Number,
longitude: Number,
},
})
const bookmarkListSchema = new mongoose.Schema({
listName: String,
items: [bookmarkItemSchema],
})
const bookmarkSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
lists: [bookmarkListSchema],
})
// const add = (lists, userId) => {
// let bookmark = Bookmark.findOne({userId})
// bookmark.lists.listName === lists.listName //current, new
// ? bookmark.lists.items.push(lists.items)
// : bookmark.lists.push(lists)
// return bookmark
// }
mongoose.model('Bookmark', bookmarkSchema)
Routes/bookmark.js
router.post('/bookmarks', async (req, res) => {
const {lists} = req.body
console.log(lists)
if (!lists) {
return res.status(422).send({error: 'You must provide lists'})
}
let bookmark = Bookmark.findOne({"userId": req.user._id})
if (bookmark.lists.listName === lists.listName){
let item = lists.items
bookmark.lists.items.push(item)
await bookmark.save()
res.send(bookmark)
}
try {
// const bookmark = Bookmark.add(lists, req.user._id, obj)
// await bookmark.save()
// res.send(bookmark)
let bookmark = Bookmark.findOne({"userId": req.user._id})
if (bookmark.lists.listName === lists.listName){ // THIS IS UNDEFINED. How to get this object?
let item = lists.items
bookmark.lists.items.push(item)
await bookmark.save()
res.send(bookmark)
}
} catch (e) {
res.status(422).send({error: e.message})
}
})
The req.body looks like this:
{
"lists": {
"listName": "My Saved List",
"items": {
"restaurantId": "abcdefg",
"cachedAttr": {
"name": "abcdefg",
"latitude": 200,
"longitude": 200
}
}
}
}
Basically what I commented out in the models/Bookmark.js file is what I would really like to do.
If the userId's list name already exists, then I would like to just add an item to the list.
Otherwise, I would like to add a new list to the object.
What is the best approach for doing this? Is there a straight forward mongoose api that I could use for this problem? or do I need to make two separated function that would handle each case and make that as schema methods and handle it in the routes file?

NodeJS Mongoose updateOne giving no match every time

I am trying to update a document in mongo with mongoose using updateOne method:
const updateResult = await UserModel.updateOne({
_id: mongoose.Types.ObjectId(userId)
}, {
$set: {
a: 'B'
}
})
userId contains a string of the ID of the user.
I have tried using the following
1. { _id: userId }
2. { email: theEmailOfTheUser }
But still, the updateResult is
n:0, nModified:0, ok:0
So I think it's must be something with the method itself and not in my query.
Also, when I'm trying to find the user using the query below, it can find it:
const user = await UserModel.find({
_id: userId
});
//user is found
Actually mongoose takes care of the $set and you do not have to add it. just:
const updateResult = await UserModel.updateOne({
_id: userId
}, {
a: 'B'
})
but the better solution would to just use findByIdAndUpdate():
const updateResult = await UserModel.findByIdAndUpdate(userId, {
a: 'B'
})

Field name different from column name in db

I just started using graphql with mysql, i would like to know if it is possible to use a name in the graphql query different from the column name in my data base.
For example i have a table users with the columns userName and password, when i define the type for the schema i have the following:
const unidadesMedidaInternaType = new GraphQLObjectType({
name: 'unidadesMedidaInterna',
fields: () => ({
userName: { type: GraphQLID },
password: { type:GraphQLString }
})
});
the resolver:
resolve (parent, args) {
return pool.query(`SELECT * FROM users`);
}
so i have to query like this:
{
users {
userName,
password
}
}
i would like to have different names in the query like this:
{
users {
Name,
secret
}
}
i tried changing the names of the fields in the type definition but the result of the query is full of nulls values.
In order to have different names in the queries you have 2 options:
Option 1: Use aliases to run the query:
You can run your query with aliases like
{
users {
Name: userName,
secret: password
}
}
In this case you are just renaming the fields name on execution time, so the original names will still be available to query.
Option 2: Map the query result to the GraphQLObject type.
First rename the fields:
const unidadesMedidaInternaType = new GraphQLObjectType({
name: 'unidadesMedidaInterna',
fields: () => ({
Name: { type: GraphQLID },
secret: { type:GraphQLString }
})
});
Then map the result of the query to match the fields:
resolve (parent, args) {
const result = pool.query(`SELECT * FROM users`);
// If the result of the query is an array then you have to map its items
return { Name: result.userName, secret: result.password }
}

GraphQL not loading children lists

I had started out writing verbose GraphQL and switched to graphql-tools and makeExecutableSchema and with the changes it will load queries for user(id: "N"), users, group(id: "N") and groups, however, the nested lists just return "id": null. I feel like I have to have a small mistake somewhere but am not seeing it:
const { makeExecutableSchema } = require('graphql-tools')
const db = require('./db')
const typeDefs = `
type User {
id: String
first_name: String!
last_name: String!
email: String!
friends: [User!]
groups: [Group!]
}
type Group {
id: String
name: String!
}
type Query {
users: [User!]!
user(id: String!): User
groups: [Group!]!
group(id: String!): Group
}
`
const resolvers = {
Query: {
users: db.readAllUsers,
user: (root, args, { loaders }) => loaders.user.load(args.id),
groups: db.readAllGroups,
group: (root, args, { loaders }) => loaders.group.load(args.id)
}
}
module.exports = makeExecutableSchema({ typeDefs, resolvers })
Any help would be greatly appreciated!
Edit: to clarify, here's what the data source looks like
You've defined resolvers for your queries, while are just fields on your Query type. However, you don't have any resolvers for the group field on the User type, so GraphQL falls back to using the default resolver for that field. Since the property field on the Object User resolves to is just an array of ids, GraphQL doesn't know how to make sense of it -- after all, you told it groups would be an array of objects.
You'll need to add a resolver for the groups field and transform that array into a Promise that will resolve to an array of Group objects:
const resolvers = {
Query: {
users: db.readAllUsers,
user: (root, args, { loaders }) => loaders.user.load(args.id),
groups: db.readAllGroups,
group: (root, args, { loaders }) => loaders.group.load(args.id)
},
User: {
groups: ({ groups }, args, { loaders }) => {
return Promise.all(groups.map(id => loaders.group.load(id)))
}
}
}

Resources