GraphQL Expected Iterable but did not find field - javascript

I am trying to implement GraphQL but always get the error of Expected iterable. As I understand there can be an error with the types, but i tried everything and don't know what to do
that's what my API returns
{
"data": [
{
"id": "bitcoin",
"rank": "1",
"symbol": "BTC",
"name": "Bitcoin",
"supply": "17193925.0000000000000000",
"maxSupply": "21000000.0000000000000000",
"marketCapUsd": "119150835874.4699281625807300",
"volumeUsd24Hr": "2927959461.1750323310959460",
"priceUsd": "6929.8217756835584756",
"changePercent24Hr": "-0.8101417214350335",
"vwap24Hr": "7175.0663247679233209"
},
......
]
}
and here is my code
const coinType = new GraphQLObjectType({
name: 'Coin',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
})
});
// Root Query
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: () => ({
coin: {
type: new GraphQLList(launchType),
resolve(parent, args) {
return axios
.get('https://api.coincap.io/v2/assets')
.then(res => res.data);
}
},
})
});
module.exports = new GraphQLSchema({
query: RootQuery
});

Usually this is because mismatch between the return type of the resolver and your schema. Check for returning Object instead of Array or something in that sense. You may post the schema just in case.

Related

How To Push mutiple array elements into mongod by mongoose

There is a mongodb Schema which include these field, its type is array
......
orderlist: [
{
id: String,
price: Number,
photo: String,
name: String,
num: Number
}
]
......
The frontend post me the data such as this,this array has lots of array elements
goodslist:[
{
goodsid: '10001',
goodsprice: 20,
goodsphoto: '/goodsimg/upload_1843.jpg',
goodsname: 'goods1',
goodsnum: 2
},
{
goodsid: '10002',
goodsprice: 30,
goodsphoto: '/goodsimg/upload_1845.jpg',
goodsname: 'goods2',
goodsnum: 4
},
........(etc)
]
what can I do to push this 'goodslist' data into 'orderlist' field by mongoose without changing mongodb field, thanks
You must use mongoose virtuals to achieve this issue.
Your schema must like this:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const collectionName = 'orderlist';
const OrderSchema = new Schema({
id: String,
price: Number,
photo: String,
name: String,
num: Number
}, { minimize: false });
const OrderlistSchema = new Schema({
orderList: [OrderSchema]
}, { minimize: false, toJSON: { virtuals: true } });
OrderlistSchema.virtual('goodslist').
get(function () {
return this.orderList.map(order => ({
goodsid: order.id,
goodsprice: order.price,
goodsphoto: order.photo,
goodsname: order.name,
goodsnum: order.num
}))
}).
set(function (v) {
this.set({
orderList: v.map(good => ({
id: good.goodsid,
price: good.goodsprice,
photo: good.goodsphoto,
name: good.goodsname,
num: good.goodsnum
}))
});
});
module.exports = mongoose.model('Orderlist', OrderlistSchema, collectionName);
goodslist is virtual field here.
With this schema, you can set order field with your format without changing anything in mongodb.
Example posting document:
{
"goodslist": [
{
"goodsid": 2,
"goodsprice": 200,
"goodsphoto": "photo2",
"goodsname": "name2",
"goodsnum": 1234
}
]
}
you can also get order data in goodlist format
{
"_id": "5e9d8c0e27c7a813840c9ff0",
"orderList": [
{
"_id": "5e9d8c0e27c7a813840c9ff1",
"id": "2",
"price": 200,
"photo": "photo2",
"name": "name2",
"num": 1234
}
],
"__v": 0,
"goodslist": [
{
"goodsid": "2",
"goodsprice": 200,
"goodsphoto": "photo2",
"goodsname": "name2",
"goodsnum": 1234
}
],
"id": "5e9d8c0e27c7a813840c9ff0"
}
It going to be something like the following:
//Update order | create if does not exist
orderDB.updateOne({ _id: 'xxxx' }, {
//Push the list into order array
$push: {
orderlist: [{
id: goodlist[0][0],
price: goodlist[0][1],
photo: goodlist[0][2],
name: goodlist[0][3],
num: goodlist[0][4],
}]
}
//Upsert => update / create
}, { upsert: true })
However, you might need to loop through the goodslist.

mongodb after updating document, returns old values

router.delete('/deletepost', (req, res) => {
// console.log(req.query.postid)
if (req.query.category === 'forsale') {
ForSalePosts.findById(req.query.postid)
// .then(post => console.log(post))
.deleteOne()
.catch(err => console.log(err))
AllPosts.updateOne({ user: req.query.userid },
{ $pull: { posts: { postid: req.query.postid } } })
.catch(err => console.log(err))
AllPosts.aggregate(
[
{ $match: { user: ObjectId(req.query.userid) } },
{ $unwind: '$posts' },
{ $sort: { 'posts.date': -1 } }
]
)
.then(posts => {
// console.log(posts)
res.json(posts)
})
.catch(err => res.status(404).json({ nopostfound: 'There is no posts' }))
}
})
this is my route. i am trying to delete an item in my document. the item is being deleted however it returns old values. for example :
Allposts has an array with posts:[postid:{type:String}, ...]
I am trying to delete a specific postid by using $pull,
postid is being deleted however when I aggregate the same model, .then(posts=> console.log(posts)) returns old values on first call, doesnt update the component.
EDIT: just realized sometimes it returns the right values but sometimes it returns the old values as well. does anyone know why and what can i do to solve it ?
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const AllPostsSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
posts: [{
postid: {
type: String
},
title: {
type: String
},
category: {
type: String
},
subcategory: {
type: String
}, category: {
type: String
},
description: {
type: String
},
name: {
type: String
},
price: {
type: Number
},
email: {
type: String
},
phonenumber: {
type: Number
},
language: {
type: String
},
make: {
type: String
},
model: {
type: Number
},
odometer: {
type: Number
},
condition: {
type: String
},
state: {
type: String
},
town: {
type: String
},
city: {
type: String
},
links: [{ type: String }],
date: {
type: Date,
default: Date.now
}
}]
})
module.exports = AllPosts = mongoose.model('allposts', AllPostsSchema)
REACT FUNCTION CALL :
deletePost = (category, postid) => {
const postinfo = {
category: category.toLowerCase(),
postid: postid,
userid: this.props.auth.user.id
}
this.props.deletePost(postinfo)
}
You need to add options parameter to delete like:
AllPosts.updateOne({ user: req.query.userid },
{
$pull: { posts: { postid: req.query.postid } }
},
{ new: true }
);
This will return the new object after performing the operation. Hope this works for you.
All the mongo queries return partial promise. You have to use .then in order to resolve each promises.
Here you are running all the queries in series without using .then or async-await. So whenever you $pull from AllPosts after that immediately you call the AllPosts aggregate query which sometimes get executed and sometimes it doesn't.
So in order to make it run one by one you have to use either .then or async-await.
router.delete("/deletepost", (req, res) => {
if (req.query.category === "forsale") {
ForSalePosts.findById(req.query.postid)
.deleteOne()
.then(() => {
AllPosts.updateOne(
{ "user": req.query.userid },
{ "$pull": { "posts": { "postid": req.query.postid } } }
)
.then(() => {
AllPosts.aggregate([
{ "$match": { "user": ObjectId(req.query.userid) } },
{ "$unwind": "$posts" },
{ "$sort": { "posts.date": -1 } }
]).then(posts => {
// console.log(posts)
res.json(posts);
});
})
.catch(err =>
res.status(404).json({ "nopostfound": "There is no posts" })
);
});
}
})

How to return an array of objects in GraphQL, possibly using the same endpoint as the one that returns a single object?

I am making a GraphQL API where I would be able to retrieve a car object by its id or retrieve all the cars when no parameter is provided.
Using the code below, I am successfully able to retrieve a single car object by supplying id as a parameter.
However, in the case where I would expect an array of objects i.e. when I supply no parameter at all, I get no result on GraphiQL.
schema.js
let cars = [
{ name: "Honda", id: "1" },
{ name: "Toyota", id: "2" },
{ name: "BMW", id: "3" }
];
const CarType = new GraphQLObjectType({
name: "Car",
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString }
})
});
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
cars: {
type: CarType,
args: {
id: { type: GraphQLString }
},
resolve(parent, args) {
if (args.id) {
console.log(cars.find(car => car.id == args.id));
return cars.find(car => car.id == args.id);
}
console.log(cars);
//***Problem Here***
return cars;
}
}
}
});
Test queries and their respective results:
Query 1
{
cars(id:"1"){
name
}
}
Query 1 Response (Success)
{
"data": {
"cars": {
"name": "Honda"
}
}
}
Query 2
{
cars{
name
}
}
Query 2 Response (Fail)
{
"data": {
"cars": {
"name": null
}
}
}
Any help would be much appreciated.
A Car and a List of Cars are effectively two separate types. A field cannot resolve to a single Car object one time, and an array of Car object another.
Your query is returning null for the name because you told it the cars field would resolve to a single object, but it resolved to an array instead. As a result, it's looking for a property called name on the array object and since one doesn't exist, it's returning null.
You can handle this in a couple of different ways. To keep things to one query, you can use filter instead of find and change the type of your query to a List.
cars: {
type: new GraphQLList(CarType), // note the change here
args: {
id: {
type: GraphQLString
},
},
resolve: (parent, args) => {
if (args.id) {
return cars.filter(car => car.id === args.id);
}
return cars;
}
}
Alternatively, you could split this into two separate queries:
cars: {
type: new GraphQLList(CarType),
resolve: (parent, args) => cars,
},
car: {
type: CarType,
args: {
id: {
// example of using GraphQLNonNull to make the id required
type: new GraphQLNonNull(GraphQLString)
},
},
resolve: (parent, args) => cars.find(car => car.id === args.id),
}
Check the docs for more examples and options.

How to remove an element from an array within Mongo

I'm completely stuck with Mongoose and remove method.
I have a page with comments and form with a button Delete. My goal is to delete only that comment which was clicked. Below is my MongoDB file (By the way I use method override of the express library to handle both request post and delete).
{
"_id": {
"$oid": "5a455cf460414f548f3d1afb"
},
"title": "Tets",
"body": "tes",
"user": {
"$oid": "5a440bae124b7e4626aeeb70"
},
"date": {
"$date": "2017-12-28T21:07:00.194Z"
},
"comments": [
{
"commentBody": "ets",
"commentUser": {
"$oid": "5a440bae124b7e4626aeeb70"
},
"_id": {
"$oid": "5a455cf660414f548f3d1afc"
},
"commentDate": {
"$date": "2017-12-28T21:07:02.143Z"
}
}
],
"allowComments": true,
"status": "public",
"__v": 1
}
my Schema
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
//Create Schema
const StorySchema = new Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true
},
status: {
type: String,
default: 'public'
},
allowComments: {
type: Boolean,
default: true
},
comments: [{
commentBody: {
type: String,
required: true
},
commentDate: {
type: Date,
default: Date.now
},
commentUser: {
type: Schema.Types.ObjectId,
ref: 'users'
}
}],
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
date: {
type: Date,
default: Date.now
}
});
mongoose.model('stories',StorySchema, 'stories');
And my JS file ,my post method works exactly how I wish but delete doesn't work at all (Cannot read property 'comments' of undefined)
router.post('/comment/:id' , (req , res) => {
Story.findOne({
_id: req.params.id
})
.then(story => {
const newComment = {
commentBody: req.body.commentBody,
commentUser: req.user.id
}
//Push to comments array
story.comments.unshift(newComment);
story.save()
.then(story => {
res.redirect(`/stories/show/${story.id}`)
})
});
})
router.delete('/comment/:id', (req, res) => {
Story.remove({
_id: req.body.id.comments
})
.then(() => {
req.flash('success_msg', 'Comments Removed!');
res.redirect('/dashboard');
})
});
here is my handlebars file with form
{{#each story.comments}}
<form action="/stories/comment/{{id}}?_method=DELETE" method="post" id="delete-form">
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="btn red"><i class="fa fa-remove"></i> Delete</button>
</form>
{{/each}}
The error I got
TypeError: Cannot read property 'comments' of undefined
at router.delete (/Users/ar2z/Desktop/fierce-caverns-70427/routes/stories.js:197:20)
Help me please. I'm completely lost.
I actually encountered the same error recently. I have found you need to do this:
router.delete("/comments/:id", function(req,res){
var Model = require("myModel");
//Run a find and update query to delete the comment
Model.findOne({_id:req.params.id}, function(err,doc){
if(doc && !err){
doc.comments = doc.comments.filter(function(comment){
//This will filter out the comment you want to delete
return comment._id != req.body.commentId
})
}
res.redirect("/comments/")
})
}
My fault was from the beginning when I was trying work with array element the same way as i'm working with Mongo object
Story.remove({
_id: req.body.id.comments
})
Code above is not working for array element it's working with object but for delete element from array I use:
Story.update( { }, { $pull: { comments: { _id: req.params.id }}}, { multi: true } )
This code Remove Items from an Array of Documents
$pull MongoDb documentation

Populate Only When Conditions Are Met

I have a mongodb database and I use mongoose with nodejs.
I need return data from the next query populating "tabela_tuss" only if I have the field "temtussvinculado=true".
Here is what I am doing:
ConvProced.find({'convenioId':new ObjectId(req.params.id)})
.populate('convenioId')
.populate({
path:'procedId',
populate:{
path:'tabela_tuss',
match: { 'procedId.temtussvinculado': true}
}
})
.exec( (err,data) => {
callback(err,data,res)
})
My problem is that my match with "procedId.temtussvinculado:true" has no effect and "tabela_tuss" is never populated.
What am I doing wrong?
Here is my schemas:
////
var conveniosSchema = new mongoose.Schema({
nome: {type: String, unique:true},
ativo: {type: Boolean}
});
module.exports = mongoose.model('Convenio', conveniosSchema,'convenios' );
////
////
const agProcedimentosSchema = new mongoose.Schema({
ativo:{type:Boolean},
temtussvinculado:{type:Boolean},
tabela_tuss:{type:mongoose.Schema.Types.ObjectId, ref:'Tuss_22'}
});
module.exports = mongoose.model('Ag_procedimento', agProcedimentosSchema,'ag_procedimentos' );
///
////
const tuss_22Schema = new mongoose.Schema({
codigo: {type: String, unique:true},
descricao:{type: String},
tabela:{type: String}
});
module.exports = mongoose.model('Tuss_22', tuss_22Schema,'tuss_22' );
////
//../models/convenioprocedimento
var conveniosProcedsSchema = new mongoose.Schema({
convenioId:{type:mongoose.Schema.Types.ObjectId, ref:'Convenio'},
procedId:{type:mongoose.Schema.Types.ObjectId, ref:'Ag_procedimento'},
valor_particular:{type:Number},
valor_convenio:{type:Number},
});
module.exports = mongoose.model('ConvenioProcedimento', conveniosProcedsSchema,'conveniosprocedimentos' );
//my query:
const ConvProced = require('../models/convenioprocedimento');
ConvProced.find({'convenioId':new ObjectId(req.params.id)})
.populate('convenioId')
.populate({
path:'procedId',
populate:{
path:'tabela_tuss',
match: { 'procedId.temtussvinculado': true}
}
})
.exec( (err,data) => {
callback(err,data,res)
})
What you are actually asking here is to "Only populate where a condition within the data says to do so", which is something that is not actually a "directly" supported action of .populate() or usage of the "nested populate" syntax.
So if you want to impose "conditions" on which items are actually populated or not, then you must handle the populate calls "manually".
The basic premise in your case is that you would need to inspect the value which you need to get from the "initial" top level .populate() call, but then "only" call the "inner" populate when the given condtions actually allow it.
So your code should then probably look like this using "Promises" using Promise.all() where you basically "loop" or .map() each query result and test the proceedid.temtussvinculado to see if it is true/false, and where true we actually issue a Model.populate() call, otherwise just return the data in it's present state:
ConvProced.find({'convenioId':new ObjectId(req.params.id)})
.populate('convenioId procedId')
.exec()
.then(data =>
Promise.all(
data.map( d =>
( d.proceedid.temtussvinculado )
? mongoose.model('Tuss_22').populate(d,{ path: 'proceedId.tabela_tuss' })
: d
)
)
)
)
// Populated conditionally
.then( data =>
// Do something with data
)
.catch(err => console.error(err)); // or something else with error
There are different options available other than 'Promises', but it is the no dependency option. Alternate cases such as async.map to do much the same thing, but is an additional dependency if you do not already have it:
ConvProced.find({'convenioId':new ObjectId(req.params.id)})
.populate('convenioId procedId')
.exec((err,data) => {
if (err) throw err;
async.map(data,(d,callback) =>
( d.proceedid.temtussvinculado )
? mongoose.model('Tuss_22').populate(d,{ path: 'proceedId.tabela_tuss' },callback)
: callback(null,d)
(err,data) => {
if (err) throw err; // or something
// Conditionally populated
}
)
})
Also demonstrated with a full working example, which is actually a little more complicated than what you need to do, since the "condition" is nested within another array in this example:
const async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
mongoose.connect('mongodb://localhost/test');
const subInnerSchema = new Schema({
label: String
});
const innerSchema = new Schema({
name: String,
populate: Boolean,
subs: [{ type: Schema.Types.ObjectId, ref: 'Sub' }]
});
const outerSchema = new Schema({
title: String,
inners: [{ type: Schema.Types.ObjectId, ref: 'Inner' }]
});
const Sub = mongoose.model('Sub', subInnerSchema);
const Inner = mongoose.model('Inner', innerSchema);
const Outer = mongoose.model('Outer', outerSchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
async.series(
[
// Clean data
(callback) =>
async.each(mongoose.models,(model,callback) =>
model.remove({},callback),callback),
// Insert some data
(callback) =>
async.waterfall(
[
(callback) =>
Sub.create([1,2,3,4].map( label => ({ label })),callback),
(subs,callback) =>
Inner.create(
[0,2].map(x => subs.slice(x,x+2))
.map((el,i) => ({
name: i+i,
populate: i == 1,
subs: el
})),
callback
),
(inners,callback) =>
Outer.create(
inners.map((inner,i) => ({
title: i+1,
inners: [inner]
})),
callback
),
],
callback
),
// Conditional populate async.map version
(callback) =>
Outer.find().populate('inners').exec((err,outers) => {
if (err) callback(err);
async.map(
outers,
(outer,callback) =>
async.map(
outer.inners,
(inner,callback) =>
(inner.populate)
? Inner.populate(inner,{ path: 'subs' },callback)
: callback(null,inner),
(err,inners) => {
if (err) callback(err);
outer.inners = inners
callback(null,outer);
}
),
(err,outers) => {
if (err) callback(err);
log(outers);
callback();
}
);
}),
// Conditional populate Promise
(callback) =>
Outer.find().populate('inners').exec()
.then(outers =>
Promise.all(
outers.map( outer =>
new Promise((resolve,reject) => {
Promise.all(
outer.inners.map( inner =>
(inner.populate)
? Inner.populate(inner,{ path: 'subs' })
: inner
)
).then(inners => {
outer.inners = inners;
resolve(outer)
})
.catch(reject)
})
)
)
)
.then(outers => {
log(outers);
callback();
})
.catch(err => callback(err))
],
(err) => {
if (err) throw err;
mongoose.disconnect();
}
);
Which produces the output showing the "conditional" selection, from using either approach of course:
Mongoose: subs.remove({}, {})
Mongoose: inners.remove({}, {})
Mongoose: outers.remove({}, {})
Mongoose: subs.insert({ label: '1', _id: ObjectId("5961830256bf9e2d0fcf13b3"), __v: 0 })
Mongoose: subs.insert({ label: '2', _id: ObjectId("5961830256bf9e2d0fcf13b4"), __v: 0 })
Mongoose: subs.insert({ label: '3', _id: ObjectId("5961830256bf9e2d0fcf13b5"), __v: 0 })
Mongoose: subs.insert({ label: '4', _id: ObjectId("5961830256bf9e2d0fcf13b6"), __v: 0 })
Mongoose: inners.insert({ name: '0', populate: false, _id: ObjectId("5961830256bf9e2d0fcf13b7"), subs: [ ObjectId("5961830256bf9e2d0fcf13b3"), ObjectId("5961830256bf9e2d0fcf13b4") ], __v: 0 })
Mongoose: inners.insert({ name: '2', populate: true, _id: ObjectId("5961830256bf9e2d0fcf13b8"), subs: [ ObjectId("5961830256bf9e2d0fcf13b5"), ObjectId("5961830256bf9e2d0fcf13b6") ], __v: 0 })
Mongoose: outers.insert({ title: '1', _id: ObjectId("5961830256bf9e2d0fcf13b9"), inners: [ ObjectId("5961830256bf9e2d0fcf13b7") ], __v: 0 })
Mongoose: outers.insert({ title: '2', _id: ObjectId("5961830256bf9e2d0fcf13ba"), inners: [ ObjectId("5961830256bf9e2d0fcf13b8") ], __v: 0 })
Mongoose: outers.find({}, { fields: {} })
Mongoose: inners.find({ _id: { '$in': [ ObjectId("5961830256bf9e2d0fcf13b7"), ObjectId("5961830256bf9e2d0fcf13b8") ] } }, { fields: {} })
Mongoose: subs.find({ _id: { '$in': [ ObjectId("5961830256bf9e2d0fcf13b5"), ObjectId("5961830256bf9e2d0fcf13b6") ] } }, { fields: {} })
[
{
"_id": "5961830256bf9e2d0fcf13b9",
"title": "1",
"__v": 0,
"inners": [
{
"_id": "5961830256bf9e2d0fcf13b7",
"name": "0",
"populate": false,
"__v": 0,
"subs": [
"5961830256bf9e2d0fcf13b3",
"5961830256bf9e2d0fcf13b4"
]
}
]
},
{
"_id": "5961830256bf9e2d0fcf13ba",
"title": "2",
"__v": 0,
"inners": [
{
"_id": "5961830256bf9e2d0fcf13b8",
"name": "2",
"populate": true,
"__v": 0,
"subs": [
{
"_id": "5961830256bf9e2d0fcf13b5",
"label": "3",
"__v": 0
},
{
"_id": "5961830256bf9e2d0fcf13b6",
"label": "4",
"__v": 0
}
]
}
]
}
]
Mongoose: outers.find({}, { fields: {} })
Mongoose: inners.find({ _id: { '$in': [ ObjectId("5961830256bf9e2d0fcf13b7"), ObjectId("5961830256bf9e2d0fcf13b8") ] } }, { fields: {} })
Mongoose: subs.find({ _id: { '$in': [ ObjectId("5961830256bf9e2d0fcf13b5"), ObjectId("5961830256bf9e2d0fcf13b6") ] } }, { fields: {} })
[
{
"_id": "5961830256bf9e2d0fcf13b9",
"title": "1",
"__v": 0,
"inners": [
{
"_id": "5961830256bf9e2d0fcf13b7",
"name": "0",
"populate": false,
"__v": 0,
"subs": [
"5961830256bf9e2d0fcf13b3",
"5961830256bf9e2d0fcf13b4"
]
}
]
},
{
"_id": "5961830256bf9e2d0fcf13ba",
"title": "2",
"__v": 0,
"inners": [
{
"_id": "5961830256bf9e2d0fcf13b8",
"name": "2",
"populate": true,
"__v": 0,
"subs": [
{
"_id": "5961830256bf9e2d0fcf13b5",
"label": "3",
"__v": 0
},
{
"_id": "5961830256bf9e2d0fcf13b6",
"label": "4",
"__v": 0
}
]
}
]
}
]
So you can see there that in much the same way there is a "boolean" field which is being tested to determine whether to perform a .populate() or just return the plain data instead.

Categories

Resources