How to implement transactional management in mongo with graphql? - mongodb

I'm currently working on a movie database with graphql and mongodb. When a new review is added I want to push the review id immediately to the reviews array of movie entity. I understand that I have to use transactional management. I'm trying to do it like this, but I'm getting this error : throw new MongooseError('Callback must be a function, got ' + callback);
this is the mutation where I'm trying to implement it.
import Review from "../../db/models/ReviewModel.js";
import Movie from "../../db/models/MovieModel.js";
import mongoose from "mongoose";
const generateReviewModel = () => ({
queries: {
getAll: () => Review.find({}),
getReviewByID: (id) => Review.findOne({ _id: id }),
getAllHavingKey: (keys) =>
Review.find({
_id: { $in: keys.map((key) => mongoose.Types.ObjectId(key)) },
}),
},
mutations: {
addReview: (review) => {
let session = null;
Review.startSession()
.then((_session) => {
session = _session;
return session.withTransaction(() => {
return new Review(review).save(review, {
session: session,
});
});
})
.then(() => session.endSession());
},
modifyReview: (body) => Review.findByIdAndUpdate(body.id, body.query),
deleteReview: (id) => Review.findByIdAndDelete(id),
},
});
export default generateReviewModel;

Related

React-Query useQueries hook to run useInfiniteQuery hooks in parallel

I am new to React-Query, but I have not been able to find an example to the following question:
Is it possible to use useInfiniteQuery within useQueries?
I can see from the parallel query documentation on GitHub, that it's fairly easy to set-up a map of normal queries.
The example provided:
function App({ users }) {
const userQueries = useQueries({
queries: users.map(user => {
return {
queryKey: ['user', user.id],
queryFn: () => fetchUserById(user.id),
}
})
})
}
If I have an infinite query like the following, how would I be able to provide the individual query options, specifically the page parameter?:
const ids: string[] = ['a', 'b', 'c'];
const useGetDetailsById = () => {
return useInfiniteQuery<GetDetailsByIdResponse, AxiosError>(
['getDetailsById', id],
async ({ pageParam = '' }) => {
const { data } = await getDetailsById(
id, // I want to run queries for `id` in _parallel_
pageParam
);
return data;
},
{
getNextPageParam: (lastPage: GetDetailsByIdResponse) =>
lastPage.nextPageToken,
retry: false,
}
);
};
No, I'm afraid there is currently no such thing as useInfiniteQueries.

MongoDB and triggers

I have a post call that inserts records in an Atlas mongodb database, the Atlas service has a trigger active to increment a correlative field (no_hc), then I make a query to retrieve that correlative field with the _id generated in the insert of the document. I put the code below so that they can tell me what I am doing wrong since this code sometimes returns null the field no_hc. Thanks since now
I add information about what I need to execute. I have a collection on which every time a document is added the mongodb server executes a trigger to increment a certain field, the problem is that the findoneandupsert query returns the inserted document with the autoincrement field set to null, since apparently the trigger is executed after the result of findoneandupsert is returned, how can I solve this issue?
router.post('/admisionUpsert', (req, res) => {
admisionUpsert(req.body)
.then(data => res.json(data))
.catch(err => res.status(400).json({ error: err + ' Unable to add ' }));
})
async function admisionUpsert(body) {
let nohc = body.no_hc
delete body['no_hc'];
let id;
let noprot
await Paciente.findOneAndUpdate(
{ no_hc: nohc },
{
apellido: body.apellido,
nombre: body.nombre,
sexo: body.sexo,
no_doc: body.no_doc,
fec_nac: body.fec_nac,
calle: body.calle,
no_tel: body.no_tel,
email: body.email
}, {
new: true,
upsert: true
}
)
.then(data => { id = data._id })
console.log("id pac", id)
Paciente.findOne({ _id: id }).then(data => { nohc = data.no_hc })
console.log("nohc pac", nohc)
await Protocolo.findOneAndUpdate(
{ no_prot: "" },
{
cod_os: body.cod_os,
no_os: body.no_os,
plan_os: body.plan_os,
no_hc: nohc,
sexo: body.sexo,
medico: body.medico,
diag: body.diag,
fec_prot: body.fec_prot,
medicacion: body.medicacion,
demora: body.demora
}, {
new: true,
upsert: true
}
)
.then(data => { id = data._id })
console.log("id prot", id)
Protocolo.findOne({ _id: id }).then(data => { noprot = data.no_prot })
console.log("noprot prot", noprot)
let practicasProtocolo = []
body.practicas_solicitadas.map((practica, index1) => {
practicasProtocolo.push({
no_prot: noprot,
cod_ana: practica.codigo,
cod_os: practica.cod_os,
estado_administrativo: practica.estado_administrtivo,
estado_muestra: practica.estado_muestra,
estado_proceso: practica.estado_proceso,
})
practica.parametro.map((parametro, index) => {
par = parametro.codigo
tipo_dato = parametro.tipo_dato
if ((tipo_dato === "numerico") || (tipo_dato === "frase") || (tipo_dato === "codigo")) {
practicasProtocolo[index1][par] = null;
}
})
})
console.log(practicasProtocolo)
practicasProtocolo.map(async (practica, index) => {
await new Practica(practica).save()
})
return { nohc: nohc, noprot: noprot }
}

MongoDB How to define a query for find an object in array, but the array exist of Object references

Basically what I want to do is that I have an ObjectId of a qr. I want to use that ObjectId to find out which qrBlock does it belong. Im leaving my dbmodel here
so you can track easily.
qrRoute.post("/scanQr", (req, res) => {
let { data } = req.body;
var id = mongoose.Types.ObjectId(data);
Qr.findById(id)
.exec()
.then((qrr) => {
QrBlock.find({ qr: { "qr.$": id } }, (qrblck) => {
console.log(qrblck);
});
});
});
I tried this code above it didn't work.
did you try this way
qrRoute.post("/scanQr", (req, res) => {
let { data } = req.body;
var id = mongoose.Types.ObjectId(data);
Qr.findById(id)
.exec()
.then((qrr) => {
QrBlock.find({ qr: id }, (qrblck) => {
console.log(qrblck);
});
});
});
QrBlock.findOne({ qr: { $all: [qrr._id] } })
this worked for me

Error: TypeError: user.insertOne is not a function using mongoose

I'm having difficulty creating the routes to send to MongoDB.
When I return user, it returns the full database. This goes for using User or 'user'.
User is a model
let User = require('../models/user.model');
User.findById(req.params.id)
.then(user => {
if (!user)
res.status(404).send("data is not found");
else
for(var key in req.body.proposal) {
//res.send(user.proposal)
//res.send(user)
//res.send(User.username)
user.proposal.insertOne(
{
"uid" : req.body.proposal[key].uid,
"clientEmail" : req.body.proposal[key].clientEmail,
"summary" :req.body.proposal[key].summary,
"terms" :req.body.proposal[key].terms,
"form" :req.body.proposal[key].form
} //update
)
}
user.save()
.then(user => res.json(user))
.catch(err => res.status(400).json('Error: ' + err));
})
.catch(err => res.status(400).json('Error: ' + err));
});
Thank you in advanced!
It should be something like this :
let proposalArr = [];
for (const key in req.body.proposal) {
proposalArr.push({
uid: req.body.proposal[key].uid,
clientEmail: req.body.proposal[key].clientEmail,
summary: req.body.proposal[key].summary,
terms: req.body.proposal[key].terms,
form: req.body.proposal[key].form
});
}
user.proposal = proposalArr;
user.save().............
You can't use .insertOne on result of database query, it's a function of mongoose model to insert new document to collection but not to insert new fields to objects. You need to do just like adding new fields to json object using .js code, but mongoose will keep track of object's changes & when you use .save() it can update the document in collection with all those changes.
Instead of two DB calls, you can do that in one call, Check : .findByIdAndUpdate() & try below sample code :
let proposalArr = [];
for (const key in req.body.proposal) {
proposalArr.push({
uid: req.body.proposal[key].uid,
clientEmail: req.body.proposal[key].clientEmail,
summary: req.body.proposal[key].summary,
terms: req.body.proposal[key].terms,
form: req.body.proposal[key].form
});
}
User.findByIdAndUpdate(
req.params.id,
{
proposal: proposalArr
},
{ new: true }
)
.then(user => {
if (!user) res.status(404).send("data is not found");
res.json(user);
})
.catch(err => res.status(400).json("Error: " + err));

Using bluebird library in meteor to save data from remote API into local mongo db

Meteor.methods({
'sync.toggl'(apiToken) {
const toggl = new TogglApi({ apiToken });
Promise.promisifyAll(toggl);
toggl.getWorkspacesAsync()
.each(ws => toggl.getWorkspaceProjectsAsync(ws.id)
.map(p => {
Projects.upsert({ projectId: p.id }, {
projectId: p.id,
name: p.name,
tracker: 'toggl',
tags: [],
contributors: []
});
})
.catch(err => console.error(`fetching ${ws.name} projects error - ${err.message}`));
)
.catch(err => console.error(`fetching ${ws.name} workspace error - ${err.message}`));
}});
I'm trying to save data from toggl api into local db here. But Meteor throws an error - Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment. I found couple solutions, but they doesn't allow me to use bluebird promises... or not?
Using async/await worked for me:
Meteor.methods({
'sync.toggl'(apiToken) {
const toggl = new TogglApi({ apiToken });
Promise.promisifyAll(toggl);
async function saveProject(pid, name) {
try {
return await Projects.upsert(
{ pid },
{
pid,
name,
tracker: 'toggl',
contributors: [],
}
)
} catch (err) {
return console.error(`async saveProject failed - ${err.message}`);
}
}
toggl.getWorkspacesAsync()
.each(ws => toggl.getWorkspaceProjectsAsync(ws.id)
.map(p => {
saveProject(p.id, p.name);
})
.catch(err => console.error(`fetching projects error - ${err.message}`))
)
.catch(err => console.error(`fetching workspaces error - ${err.message}`))
}});