How to group nested data in GraphQL - node.js

{
"data": {
"books": [
{
"name": "Himu",
"genre": "novel",
"author": {
"name": "Humayun Ahmed",
"country": "Bangladesh"
}
},
{
"name": "Tatina",
"genre": "novel",
"author": {
"name": "Zafor iqbal",
"country": "Bangladesh"
}
},
{
"name": "Show Me",
"genre": "novel",
"author": {
"name": "Nick Pirog",
"country": "United States"
}
}
]
}
}
I have this data I want to display all the books of authors who live in a specific country
(e.g. all books of all authors living in Bangladesh). I am new to GraphQL and I don't know how to do that. I am using node.js with MongoDB.
const BookType = new GraphQLObjectType({
name: "Book",
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
genre: { type: GraphQLString },
author: {
type: AuthorType,
resolve(parent, args) {
return Author.findById(parent.authorId);
},
},
}),
});
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
books: {
type: new GraphQLList(BookType),
resolve(parent, args) {
return Book.find({});
},
},
},
});

Related

How can I put the attributes in the same order in the API by using Sequelize?

I am trying to put the attributes in the same order in the API by using Sequelize . How can I do that? Here is my controller function and here is my GET/API result:
---------CONTROLLER FUNCTION --------
bookList (req,res){
const {page , size } = req.query
return Book
.findAll({
attributes:['id',"book"],
include: [{
model: Author,
as: 'author',
attributes:['author']
},{
model: Category,
as: 'category',
attributes:['category']
},{
model: Publisher,
as: 'publisher',
attributes:['publisher']
}],
limit: size,
offset: size * page
})
.then((result) => res.status(200).send(result))
.catch((error) => res.status(400).send(error));
}
---------API RESULT-------------
[
{
"id": 6,
"book": "Şehir-Kültür-İstanbul",
"author": {
"author": "İlber Ortaylı"
},
"category": {
"category": "Hukuk"
},
"publisher": {
"publisher": "Doğan Yayınları"
}
},
{
"id": 7,
"book": "Kürk Mantolu Madonna",
"author": {
"author": "Sabahattin Ali"
},
"category": {
"category": "Psikoloji"
},
"publisher": {
"publisher": "Timaş Yayınları"
}
},
{
"id": 8,
"book": "Mahur Beste",
"author": {
"author": "Sabahattin Ali"
},
"category": {
"category": "Edebiyat"
},
"publisher": {
"publisher": "Timaş Yayınları"
}
},
{
"id": 9,
"book": "Aydaki Kadın",
"author": {
"author": "Sabahattin Ali"
},
"category": {
"category": "Psikoloji"
},
"publisher": {
"publisher": "Timaş Yayınları"
}
},
{
"id": 10,
"book": "Başlangıç",
"author": {
"author": "Dan Brown"
},
"category": {
"category": "Edebiyat"
},
"publisher": {
"publisher": "Altın Kitaplar"
}
}
]
I want to put the attributes in the same order in the API. Like this:
{
"id": 6,
"book": "Şehir-Kültür-İstanbul",
"author": "İlber Ortaylı" ,
"category": "Hukuk",
"publisher":"Doğan Yayınları"
}
I think this should do it for you
bookList (req,res){
const {page , size } = req.query
return Book
.findAll({
attributes: [
['id', 'book', 'author', 'category', 'publisher'],
[sequelize.col('author.author'), 'author'],
[sequelize.col('category.category'), 'category'],
[sequelize.col('publisher.publisher'), 'publisher']
],
include: [{
model: Author,
as: 'author',
attributes:['author']
},{
model: Category,
as: 'category',
attributes:['category']
},{
model: Publisher,
as: 'publisher',
attributes:['publisher']
}],
limit: size,
offset: size * page
})
.then((result) => res.status(200).send(result))
.catch((error) => res.status(400).send(error));
}

Update many sub of sub-document array using $set / $push

Following is the Sample Workspace document.I want to update box positions when we drag and drop at front end.
{
"_id": ObjectId("5eaa9b7c87e99ef2430a320b"),
"logo": {
"url": ".../../../assets/logo/dsdsds.png",
"name": "testUpload"
},
"name": "My World",
"sections": [{
"box": [{
"_id": ObjectId("5da87b33502d6c634b3aa7ce"),
"name": "Meran To",
"position": 0
},
{
"_id": ObjectId("5da87b33502d6c7d873aa7d0"),
"name": "Documentation",
"position": 2
},
{
"_id": ObjectId("5da87b33502d6cdbb93aa7cf"),
"name": "File Manager Upload File Drive",
"position": 1
},
{
"_id": ObjectId("5da87b33502d6c276a3aa7cd"),
"name": "File Manager Upload File Drive",
"position": 1
}
],
"id": 1,
"title": "Simplicity",
"description": "Follow your barriers"
},
{
"box": [],
"id": 2,
"title": "xfxdfxcx 34",
"description": "sdsdsd sfsfsd ewrewrewre"
}
]
}
I send the updated positions from front-end to back-end via API, in an array as shown below.
[
{
"id": "5da87b33502d6c634b3aa7ce",
"position": 0
}, {
"id": "5da87b33502d6c7d873aa7d0",
"position": 1
}, {
"id": "5da87b33502d6cdbb93aa7cf",
"position": 2
}, {
"id": "5da87b33502d6c276a3aa7cd",
"position": 3
}]
I am currently updating DB using the below code
for (const el of req.body) {
await this.model.updateOne({
_id: req.params.workspaceId,
sections: {
$elemMatch: {
id: req.params.sectionId
}
},
'sections.box': {
$elemMatch: {
_id: el.id
}
},
}, {
$set: {
'sections.$[outer].box.$[inner].position': el.position
}
}, {
arrayFilters: [{
'outer.id': req.params.sectionId
}, {
'inner._id': el.id
}],
upsert: false,
});
}
But this is not the best method, it hits DB multiple times.
so I need to optimize this code with mongoose query itself.
May be using $set / $push.I don't know any exact methods.
So basically we need to remove the external for loop and make it work with mongoose itself.This is my requirement.
Thanks in advance for all the support.
There are 2 methods for doing it.
bulkWrite
const bulkOps = [];
req.body.forEach((el) => {
const upsertDoc = {
updateOne: {
filter: {
_id: req.params.workspaceId,
sections: {
$elemMatch: {
id: req.params.sectionId
}
},
'sections.box': {
$elemMatch: {
_id: el.id
}
},
},
update: {
$set: {
'sections.$[outer].box.$[inner].position': el.position
}
},
arrayFilters: [{
'outer.id': req.params.sectionId
}, {
'inner._id': el.id
}],
upsert: false,
}
};
bulkOps.push(upsertDoc);
});
const result = await this.model.collection.bulkWrite(bulkOps);
bulkUpdate
const bulkUpdate = this.model.collection.initializeUnorderedBulkOp();
req.body.forEach((el) => {
bulkUpdate.find({
_id: req.params.workspaceId,
sections: { $elemMatch: { id: req.params.sectionId } },
'sections.box': { $elemMatch: { _id: el.id } },
}).arrayFilters([{ 'outer.id': req.params.sectionId }, { 'inner._id': el.id }])
.updateOne({ $set: { 'sections.$[outer].box.$[inner].position': el.position }
});
});
await bulkUpdate.execute();

how to retrieve data from 3 mongoose sachems in custom order

I'm using node, express, mongoose for my back-end. I created User schema as follows,
const mongoose = require('mongoose');
const addressSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
no: { type: String },
firstStreet: { type: String },
secondStreet: { type: String },
city: { type: String, required: true },
district: { type: String }
})
const contactDetailsSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
landNumber: { type: String },
mobileNumber: { type: String },
momNumber: { type: String },
dadNumber: { type: String },
gardianNumber: { type: String }
})
const userSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
email: {
type: String,
required: true,
unique:true,
lowercase: true,
match: /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/
},
password: { type: String, required: true },
role: { type: String, required: true },
fullName: { type: String, required: true },
batch: { type: Number, required: true },
subject: [{
type: String
}],
school: { type: String, required: true },
birthday: { type: String },
address: { type: mongoose.Schema.Types.ObjectId, ref:'User' },
contactDetails: { type: mongoose.Schema.Types.ObjectId, ref:'User' },
stream: { type: String },
});
module.exports = {
user: mongoose.model('User', userSchema),
address: mongoose.model('Address', addressSchema),
contactDetails: mongoose.model('ContactDetails', contactDetailsSchema)
};
and I created a GET route to retrieve data from all users. I store these data in three separate mongoDB collections with same _id. And I want to get those all User data.
my User GET route is like this,
const userModels = require('../models/user');
const User = userModels.user;
const Address = userModels.address;
const ContactDetails = userModels.contactDetails;
router.get('/', (req, res) =>{
User
.find()
.exec()
.then(docs => {
console.log(docs);
const responce1 = {
count: docs.length,
Users: docs.map(doc => {
return {
Message: 'User Details',
Id: doc._id,
Email: doc.email,
Role: doc.role,
Full_Name: doc.fullName,
Batch: doc.batch,
Subject: doc.subject,
School: doc.school,
Birthday: doc.birthday,
Stream: doc.stream,
request: {
type: 'get',
url: 'http://localhost:3000/user/' +doc._id
}
}
})
}
Address
.find()
.exec()
.then(docs => {
console.log(docs)
responce2 = {
count: docs.length,
Address: docs.map(doc => {
return {
Message: 'Address',
_id: doc._id,
Address: doc.city,
First_Street: doc.firstStreet,
Second_Street: doc.secondStreet,
city: doc.city,
District: doc.district,
}
})
}
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
ContactDetails
.find()
.exec()
.then(docs => {
console.log(docs)
responce3 = {
count: docs.length,
ContactDetails: docs.map(doc => {
return {
Message: 'Contact Details',
_id: doc._id,
Land_Number: doc.landNumber,
Mobile_Number: doc.mobileNumber,
Mom_Number: doc.momNumber,
Dad_Number: doc.dadNumber,
Gardian_Number: doc.gardianNumber,
}
})
}
res.status(200).json([responce1,responce2,responce3]);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
I get data like this,
[
{
"count": 3,
"Users": [
{
"Message": "User Details",
"Id": "5b8e68fb9c898543b4970628",
"Email": "new2#gmail.com",
"Role": "student",
"Full_Name": "Dinusha",
"Batch": 16,
"Subject": [
"ICT"
],
"School": "School",
"Birthday": "1996/10/23",
"Stream": "English",
"request": {
"type": "get",
"url": "http://localhost:3000/user/5b8e68fb9c898543b4970628"
}
},
{
"Message": "User Details",
"Id": "5b8e68fd9c898543b4970629",
"Email": "new3#gmail.com",
"Role": "student",
"Full_Name": "Dinusha",
"Batch": 16,
"Subject": [
"ICT"
],
"School": "School",
"Birthday": "1996/10/23",
"Stream": "English",
"request": {
"type": "get",
"url": "http://localhost:3000/user/5b8e68fd9c898543b4970629"
}
},
{
"Message": "User Details",
"Id": "5b8f52e707c299266c0a6b97",
"Email": "new4#gmail.com",
"Role": "student",
"Full_Name": "Dinusha",
"Batch": 16,
"Subject": [
"ICT"
],
"School": "School",
"Birthday": "1996/10/23",
"Stream": "English",
"request": {
"type": "get",
"url": "http://localhost:3000/user/5b8f52e707c299266c0a6b97"
}
}
]
},
{
"count": 3,
"Address": [
{
"Message": "Address",
"_id": "5b8e68fb9c898543b4970628",
"Address": "city",
"city": "city",
"District": "rathnapura"
},
{
"Message": "Address",
"_id": "5b8e68fd9c898543b4970629",
"Address": "city",
"city": "city",
"District": "rathnapura"
},
{
"Message": "Address",
"_id": "5b8f52e707c299266c0a6b97",
"Address": "city",
"city": "city",
"District": "rathnapura"
}
]
},
{
"count": 3,
"ContactDetails": [
{
"Message": "Contact Details",
"_id": "5b8e68fb9c898543b4970628",
"Land_Number": "072846",
"Mobile_Number": "7368438",
"Mom_Number": "7364738",
"Dad_Number": "648364"
},
{
"Message": "Contact Details",
"_id": "5b8e68fd9c898543b4970629",
"Land_Number": "072846",
"Mobile_Number": "7368438",
"Mom_Number": "7364738",
"Dad_Number": "648364"
},
{
"Message": "Contact Details",
"_id": "5b8f52e707c299266c0a6b97",
"Land_Number": "072846",
"Mobile_Number": "7368438",
"Mom_Number": "7364738",
"Dad_Number": "648364"
}
]
}
]
but I need to get data of every User like in this form
[
"Users":
{
"Message": "User Details",
"Id": "5b8e68fb9c898543b4970628",
"Email": "new2#gmail.com",
"Role": "student",
"Full_Name": "Dinusha",
"Batch": 16,
"Subject": [
"ICT"
],
"School": "School",
"Birthday": "1996/10/23",
"Stream": "English",
"request": {
"type": "get",
"url": "http://localhost:3000/user/5b8e68fb9c898543b4970628"
}
},
"Address":
{
"Message": "Address",
"_id": "5b8e68fb9c898543b4970628",
"Address": "city",
"city": "city",
"District": "rathnapura"
},
"ContactDetails":
{
"Message": "Contact Details",
"_id": "5b8e68fb9c898543b4970628",
"Land_Number": "072846",
"Mobile_Number": "7368438",
"Mom_Number": "7364738",
"Dad_Number": "648364"
},
]
can I get those data like that.
how to get data from mongodb as that custom form.
Use aggregate method and $lookup
https://mongoosejs.com/docs/api.html#aggregate_Aggregate-lookup

Mongo Aggregation Returns [{}] for one of it's key:value

I am use NodeJS, ExpressJS with Mongoose Mongo ORM.
My Schema is as follows.
const chatMessageSchema = new Schema({
_id: {
type: String,
default: () => (uuidV1().replace(/-/g, ''))
},
roomid: String,
message: Schema.Types.Mixed,
type: String, // text, card, qrt, attachment
notes: [notesSchema],
posted_by_user: String,
}, {
timestamps: {
createdAt: 'created_at',
updatedAt: 'updated_at'
},
collection: 'ChatMessages'
});
The nested sub schema for notes is as follows;
const notesSchema = new Schema({
_id: {
type: String,
default: () => (uuidV1().replace(/-/g, ''))
},
roomid: String,
messageid: String,
message: Schema.Types.Mixed,
type: String, // text, attachment
posted_by_user: String,
}, {
timestamps: {
createdAt: 'created_at',
updatedAt: 'updated_at'
}});
Initially when a user creates a chatMessage the notes is default kept as an empty array initially [].
The user can explicitly create a note message after the message has been created, the notes is an array sub documents.
Now what happens is when I get a list of messages. I need to show posted_by_user which is an id of string. Against that id of string i perform a $lookup which gets the user profile. The aggregation is as follows.
return this.aggregate([
{ $match : {roomid: roomid} },
{ $sort: {created_at: -1} },
{
$lookup: {
from: "RefUserProfiles",
localField: "posted_by_user",
foreignField: "_id",
as: "posted_by_user"
}
},
{ $unwind: "$posted_by_user" },
{ $skip: pageOptions.page * pageOptions.limit },
{ $limit: pageOptions.limit },
{ $sort: {created_at: 1} },
{
$unwind: { path: "$notes", preserveNullAndEmptyArrays: true }
},
{
$lookup: {
from: "RefUserProfiles",
localField: "notes.posted_by_user",
foreignField: "_id",
as: "notes.posted_by_user"
}
},
{
$unwind: { path: "$notes.posted_by_user", preserveNullAndEmptyArrays: true }
},
{
$group: {
_id: "$_id",
created_at: { $last: "$created_at" },
roomid: { $last: "$roomid" },
message: { $last: "$message" },
notes: { $addToSet: "$notes" },
type: { $last: "$type" },
posted_by_user: { $last: "$posted_by_user" },
read_by_recipients: { $last: "$read_by_recipients" },
}
}
]);
Now since in some cases the notes for some messages is an empty array, so for that very particular case the object I get in responses is like [{}]. Is this how it is suppose to be. or is there a better use case involved.
For example the response I get based on this query is as follows.
"conversation": [
{
"_id": "7cdbf630e6ec11e79166559abfb987ac",
"created_at": "2017-12-22T07:48:23.956Z",
"roomid": "aacc6b70e68211e79166559abfb987ac",
"message": {
"text": "message 1"
},
"notes": [{}],
"type": "text",
"posted_by_user": {
"id": "4d6fd5a95d57436d944586a1d5b2b2ff",
"name": "Adeel Imran",
"profilePicture": "",
"phone": "",
"email": "",
"designation": "",
"type": "user"
},
},
{
"_id": "80d91470e6ec11e79166559abfb987ac",
"created_at": "2017-12-22T07:48:30.648Z",
"roomid": "aacc6b70e68211e79166559abfb987ac",
"message": {
"text": "message 4"
},
"notes": [
{
"posted_by_user": {
"id": "86049b3d28f640a8a6722c57b73a292e",
"name": "Paul Johnson",
"profilePicture": "",
"phone": "",
"email": "",
"designation": "",
"type": "sp"
},
"type": "text",
"message": {
"text": "yeah "
},
"messageid": "26401d30e5a511e7acc4c734d11adb25",
"roomid": "27d3ed80e0cc11e78318b3cd036890f2",
"updated_at": "2017-12-20T16:45:44.132Z",
"created_at": "2017-12-20T16:45:44.132Z",
"_id": "38ad3750e5a511e7acc4c734d11adb25"
}
],
"type": "text",
"posted_by_user": {
"id": "4d6fd5a95d57436d944586a1d5b2b2ff",
"name": "Adeel Imran",
"profilePicture":"",
"phone": "",
"email": "",
"designation": "",
"type": "user"
},
}
],
Any suggestions or directions for this will be highly appreciated. Thank you for taking the time out to review this question.
Cheers.

How can I make schema Given output using mongoose?

I have given my output for this how can I make schema.any one give example for this I have added my schema also
My output code:
$scope.countries = [{
"name": "India",
"states": [{
"name": "Maharashtra",
"cities": [{
"name": "Pune"
}, {
"name": "Mumbai"
}, {
"name": "Nagpur"
}, {
"name": "Akola"
}]
}, {
"name": "Madhya Pradesh",
"cities": [{
"name": "Indore"
}, {
"name": "Bhopal"
}, {
"name": "Jabalpur"
}]
}, {
"name": "Rajasthan",
"cities": [{
"name": "Jaipur"
}, {
"name": "Ajmer"
}, {
"name": "Jodhpur"
}]
}]
}, {
"name": "USA",
"states": [{
"name": "Alabama",
"cities": [{
"name": "Montgomery"
}, {
"name": "Birmingham"
}]
}, {
"name": "California",
"cities": [{
"name": "Sacramento"
}, {
"name": "Fremont"
}]
}, {
"name": "Illinois",
"cities": [{
"name": "Springfield"
}, {
"name": "Chicago"
}]
}]
}, {
"name": "Australia",
"states": [{
"name": "NewSouthWales",
"cities": [{
"name": "Sydney"
}]
}, {
"name": "Victoria",
"cities": [{
"name": "Melbourne"
}]
}]
}];
This is my routes:
app.route('/address')
.get(address.list)
.post(address.create);
Controller code:
/**
* Create a address
*/
exports.create = function(req, res) {
var address = new Address(req.body);
address.user = req.user;
address.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(address);
}
});
};
Schema code :
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var citySchema = {
name: {type:String, required: false}
};
citySchema = 'new Schema('+ citySchema +',{_id:false})';
var stateSchema = {
name: {type:String, required: false},
cities: [citySchema], default:[]
};
stateSchema = 'new Schema('+stateSchema +',{_id:false})';
var countrySchema = {
name: {type:String, required: false},
states: [stateSchema], default:[]
};
// dbObj is mongoose connection object
var collectionObj = dbObj.model('countries', countrySchema);
i have updated my routes and controller schema .how can i change to inser that object pls give some suggestion ?
Please try with following schema :-
var citySchema = {
name: {type:String, required: false}
};
citySchema = 'new Schema('+ citySchema +',{_id:false})';
var stateSchema = {
name: {type:String, required: false},
cities: [citySchema], default:[]
};
stateSchema = 'new Schema('+stateSchema +',{_id:false})';
var countrySchema = {
name: {type:String, required: false},
states: [stateSchema], default:[]
};
// dbObj is mongoose connection object
var collectionObj = dbObj.model('countries', countrySchema);
Now if you want to insert any single country data into db please go through with following procedure-
// Let us assume that we need to insert following data first
var data = {
"name": "India",
"states": [{
"name": "Maharashtra",
"cities": [{
"name": "Pune"
}, {
"name": "Mumbai"
}, {
"name": "Nagpur"
}, {
"name": "Akola"
}]
}, {
"name": "Madhya Pradesh",
"cities": [{
"name": "Indore"
}, {
"name": "Bhopal"
}, {
"name": "Jabalpur"
}]
}, {
"name": "Rajasthan",
"cities": [{
"name": "Jaipur"
}, {
"name": "Ajmer"
}, {
"name": "Jodhpur"
}]
}]
};
// add country
var insertObj = {
"name": "India",
"states": []
}
countryModel.create(insertObj, function (err, job) {
cb(err, job);
});
// add states
var states = [{
"name": "Maharashtra",
"cities": []
}, {
"name": "Madhya Pradesh",
"cities": []
}, {
"name": "Rajasthan",
"cities": []
}];
var stateCond = {"name": "India"};
var stateUpdate = {$push: {states: states}};
var stateOpts = {};
countryModel.findOneAndUpdate(stateCond, stateUpdate, stateOpts, function (err, resp) {
cb(err, resp);
});
// Now if you want to add "Maharastra" Cities
var cities = [{
"name": "Pune"
}, {
"name": "Mumbai"
}, {
"name": "Nagpur"
}, {
"name": "Akola"
}]
var cityCond = {"name": "India", "states.name": "Maharashtra"};
var cityUpdate = {$push: {cities: cities}};
var cityOpts = {};
countryModel.findOneAndUpdate(cityCond, cityUpdate, cityOpts, function (err, resp) {
cb(err, resp);
});
// Now same insertion query need to be run for "Madhya Pradesh" and "Rajasthan"
Thanks

Resources