GraphQL with nested schema returning null - node.js

I am trying to return a mongodb document upon a graphql query but getting null value. No error is being shown. The mongodb query works fine with mongoshell or mongoose.
Here is the schema, typedef and resolver:
const unionSchema = new Schema(
{
geometry: mongoose.Schema.Types.MultiPolygon,
properties: {
Divi_name: String,
Dist_name: String,
Upaz_name: String,
Uni_namae: String,
},
},
{ collection: "unionbounds" }
);
const union = mongoose.model("Union", unionSchema);
const typeDefs = `
type Query {
data: Union
}
type Union {
properties: Props
}
type Props{
Dist_name: String,
}
`;
const resolvers = {
Query: {
data: () => {
union.findOne(
{
geometry: {
$geoIntersects: {
$geometry: {
type: "Point",
coordinates: [90, 22],
},
},
},
},
"properties"
);
},
},
};
Mongoshell query returns the document:
{
properties: {
Div_ID: '10',
Dist_ID: '04',
Upz_ID: '28',
Un_ID: '95',
Un_UID: '10042895',
Divi_name: 'Barisal',
Dist_name: 'Barguna',
Upaz_name: 'Barguna Sadar Upazila',
Uni_name: 'Naltona',
Area_SqKm: 45.7658667915
},
_id: 6001e54a51c6d49215322f94
}
My suspicion is that I am doing something wrong in the resolver function. I would appreciate any suggestion.

The problem was indeed the resolver function. Following code worked after returning the result from the callback function and using async .. await.
const resolvers = {
Query: {
data: async () => {
var value;
await union.findOne(
{
geometry: {
$geoIntersects: {
$geometry: {
type: "Point",
coordinates: [90, 22],
},
},
},
},
"properties",
function (err, result) {
value = result;
}
);
return value;
},
},
};

Related

Mongoose aggregation With GraphQL returning error

I'm working on a project and currently using Mongodb Time-Series and aggregation.
I connected my Apollo Graphql to retrieve the data but i'm stuck with an error that i can not solve no matter what.
Float cannot represent non numeric value:
On my db the numbers are saved as Double. If i try to run the query in MongoDB Aggregation with MongoDBCompass it's working perfectly but not on my Nodejs database.
I tried googling my problem first and looking ad docs but all the different solution i found do not solve my problem, any suggestions?
This is my Query:
module.exports = async (root, { limit }, { models }) => {
const keyx = await models.Weather.aggregate([
{
$group: {
_id: {
yearMonthDay: {
$dateToString: { format: "%Y-%m-%d", date: "$timestamp" },
},
},
temp: {
$push: { temp: "$temp" },
},
},
},
])
.exec();
return keyx;
};
This is my model:
const mongoose = require("mongoose");
const { Schema } = mongoose;
mongoose.pluralize(null);
const weather = new Schema({
timestamp: {
type: String,
trim: true,
},
temp: {
type: Number,
trim: true,
},
});
const Weather = mongoose.model("time_weather", weather);
module.exports = { Weather };
and this is my types:
const { gql } = require("apollo-server");
module.exports = gql`
type Weather {
timestamp: String
_id: ID
temp: Float
}
type Query {
weather(limit: Int): [Weather]
}
`;
and finally this is the response i'm getting from apollo studio:
{
"errors": [
{
"message": "Float cannot represent non numeric value: [{ temp: 11.7 }, { temp: 11.7 }, { temp: 12.01 }, { temp: 13.21 }, { temp: 13.21 }, { temp: 11.93 }, { temp: 13.21 }, { temp: 12.73 }, { temp: 14.21 }, { temp: 11.7 }, ... 14 more items]",
"locations": [
{
"line": 6,
"column": 5
}
],
"path": [
"weather",
0,
"temp"
],
}
},

Get all data from subdocument via graphql

Model
const mongoose=require('mongoose');
const CustomerGeneralInformation = require('./CustomerGeneralInformation').schema;
const CustomerContact = require('./CustomerContact').schema;
const CustomerFinance=require('./CustomerFinancial').schema;
const CustomerPersonalData=require('./CustomerPersonalData').schema;
const CustomerIdentification=require('./CustomerIdentification').schema;
const Schema=mongoose.Schema;
const CustomerSchema=new Schema({
generalInformation: [CustomerGeneralInformation],
contactData: [CustomerContact],
financialData:[CustomerFinance],
personalData:[CustomerPersonalData],
identificationData:[CustomerIdentification]
});
module.exports=mongoose.model('Customer',CustomerSchema);
Graphql Schema
const RootQuery=new GraphQLObjectType({
name:'RootQueryType',
fields:{
customers:{
type: new GraphQLList(CustomerType),
resolve(parent,args){
return customer.find({});
}
}
}
});
GraphiQl
{
customers
{
id
generalInformation{
purposeOfBusiness
}
contactData{
phone
email
countryResidence
mailbox
houseNo
zip
city
}
financialData{
taxNo
countryTaxation
refBankIban
StringrefBankBic
refBankIban
}
}
}
Result
{
"data": {
"customers": [
{
"id": "5fa0f8ea4e028a2cf8d24c3f",
"generalInformation": {
"purposeOfBusiness": null
},
"contactData": {
"phone": null,
"email": null,
"countryResidence": null,
"mailbox": null,
"houseNo": null,
"zip": null,
"city": null
},
"financialData": {
"taxNo": null,
"countryTaxation": null,
"refBankIban": null,
"StringrefBankBic": null
}
}
]
}
}
SO I have created a customer model which have an id(autogenerated by mongodb) and 5 other fields of schema type.
I have used graphql for creating apis.
Everything is working fine, I have created mutations to add data, its all tested in mongo that data is being saved.
But if you check my graphql output I am unable to query subdocuments, it shows null. I want list of all subdocuments with data.
PlEASE HELP
I had tried this way and its working perfectly,
Firstly as you are passing the whole model in your new Customer model so instead of that I just have stored the ID as a reference.
const CustomerSchema = new mongoose.Schema(
{
generalInformation: {
type: mongoose.Schema.Types.ObjectId,
ref: "GeneralInfo"
},
contactData: { type: mongoose.Schema.Types.ObjectId, ref: "ContactData" },
financialData: {
type: mongoose.Schema.Types.ObjectId,
ref: "FinacialData"
},
personalData: { type: mongoose.Schema.Types.ObjectId, ref: "PersonalData" }
},
{
timestamps: true
}
);
After that, for CustomerType, I had done this
const CustomerType = new GraphQLObjectType({
name: "Customer",
fields: () => ({
id: { type: GraphQLID },
customerContact: {
type: CustomerContactType,
resolve(parent, args) {
return CustomerContact.findById(parent.contactData);
}
},
generalInformation: {
type: CustomerGeneralType,
resolve(parent, args) {
return CustomerGeneral.findById(parent.generalInformation);
}
},
financialData: {
type: CustomerFinanceType,
resolve(parent, args) {
return CustomerFinance.findById(parent.financialData);
}
},
personalData: {
type: CustomerPersonalType,
resolve(parent, args) {
return CustomerPersonal.findById(parent.personalData);
}
}
})
});
And them for RootQuery
customers: {
type: new GraphQLList(CustomerType),
resolve(parent, args) {
return Customar.find({});
}
}
and here is my Query:
{
customers{
id
generalInformation{
purposeOfBusiness
}
customerContact{
phone
email
countryResidence
}
financialData{
taxNo
}
}
}
And Output
{
"data": {
"customers": [
{
"id": "5fa3fe066f0fda0568e56456",
"generalInformation": {
"purposeOfBusiness": "Test Purpose"
},
"customerContact": {
"phone": "123456798",
"email": "test#demo.com",
"countryResidence": "Pak"
},
"financialData": {
"taxNo": "120033244"
}
}
]
}
}

mongoDB find, update and pull in One Query

I want to do all the find the data from the collection and then want to update some field as well as depending on want to empty the array.
const addCityFilter = (req, res) => {
if (req.body.aCities === "") {
res.status(409).jsonp({ message: adminMessages.err_fill_val_properly });
return false;
} else {
var Cities = req.body.aCities.split(","); // It will make array of Cities
const filterType = { "geoGraphicalFilter.filterType": "cities", "geoGraphicalFilter.countries": [], "geoGraphicalFilter.aCoordinates": [] };
/** While using $addToset it ensure that to not add Duplicate Value
* $each will add all values in array
*/
huntingModel
.update(
{
_id: req.body.id,
},
{
$addToSet: {
"geoGraphicalFilter.cities": { $each: Cities }
}
},
{$set:{filterType}},
).then(function(data) {
res.status(200).jsonp({
message: adminMessages.succ_cityFilter_added
});
});
}
};
Collection
geoGraphicalFilter: {
filterType: {
type:String,
enum: ["countries", "cities", "polygons"],
default: "countries"
},
countries: { type: Array },
cities: { type: Array },
aCoordinates: [
{
polygons: { type: Array }
}
]
}
But as result, the only city array is getting an update. No changes in filterType.
You appear to be passing the $set of filterType as the options argument, not the update argument.
huntingModel
.update(
{
_id: req.body.id,
},
{
$addToSet: {
"geoGraphicalFilter.cities": { $each: Cities }
},
$set: {
filterType
}
}
).then(function(data) {
res.status(200).jsonp({
message: adminMessages.succ_cityFilter_added
});
});

Data null after saving entity with Moongose and GraphQL

When saving an entity with mongoose and graphql the following happens:
First method to save:
create(budgetProps){
const budget = new Budget(budgetProps);
return budget.save();
}
The result is as follows:
{
"data": {
"addBudget": {
"_id": "59fbdefaa7b0a81180dd2c9c",
"tiempoAproximado": 2245.5,
"User": {
"name": null,
"organization": null
},
"Vehicle": {
"name": null,
"type": null
}
}
}
}
Using this method:
create(budgetProps){
const budget = new Budget(budgetProps);
return budget.save().then((res)=>{
Budget.findById(res._id)
.populate('User')
.populate('Vehicle')
.exec((err, newBudget)=> {
return newBudget;
});
});
},
I get the following:
{
"data": {
"addBudget": null
}
}
This is the Schema:
const typeDefs = `
scalar Date
input UserInput {
_id: ID,
name: String,
organization: String,
phones: [String],
emails: [String],
type: String,
password: String,
percentaje: String
}
input VehicleDescriptionInput {
es: String,
en: String
}
input VehicleInput{
_id: ID,
name: String,
passengers: Int,
largeBags: Int,
smallBags: Int,
doors: Int,
type: String,
status: Boolean,
imagesUrls: [String],
description: VehicleDescriptionInput
}
input FinalTotalCostInput {
es: String,
en: String
}
input BudgetTotalCostInput {
es: String,
en: String
}
input BudgetInput {
finalTotalCost: FinalTotalCostInput,
budgetTotalCost: BudgetTotalCostInput,
destinoInicial: String,
destinoFinal: String,
tiempoAproximado: Float,
distancia: Float,
tollCost: Float,
tolls: [String],
budgetDate: Date,
aprove: Boolean,
User: UserInput,
Vehicle: VehicleInput
}
type Mutation {
addBudget(data: BudgetInput): Budget
}
`;
Here is the resolver:
Mutation: {
addBudget: (_, {data}) =>{
return BudgetController.create(data);
}
},
Finally here is the mutation with its variables:
mutation addBudget($budget: BudgetInput) {
addBudget(data: $budget) {
_id
User{
name
organization
}
Vehicle{
name
type
}
}
}
{
"budget": {
"finalTotalCost": {
"es": "100 peso",
"en": "10 dolars"
},
"budgetTotalCost": {
"es": "80 peso",
"en": "8 dolars"
},
"destinoInicial": "Queretaro",
"destinoFinal": "Sonora",
"tiempoAproximado": 2245.5,
"distancia": 100.565,
"tollCost": 20.5,
"tolls": [
"GDL",
"Marina",
"Culap",
"MalageƱa"
],
"budgetDate": "2017/07/21",
"aprove": false,
"User": {
"_id": "59fbcc42aa82460924e5fbad"
},
"Vehicle": {
"_id": "59fbcbe4aa82460924e5fbac"
}
}
}
The entity is stored properly in the database, when Console.log the result of the populated search results are correct then I do not understand what is happening.
You can find the whole app in the following link: GitHub Repo
You're mixing Promises and callbacks. exec() will return a Promise, but only if doesn't have any arguments passed to it. Additionally, you need to return the Promise that's returned by exec().
return budget.save().then((res) => {
return Budget.findById(res._id) // missing return here
.populate('User')
.populate('Vehicle')
.exec() // don't need anything else
})
You can clean this up a little more:
return budget.save()
.then(res => Budget.findById(res._id)
.populate('User')
.populate('Vehicle')
.exec())
If you need to transform the results returned by findById before turning them over to the client:
return budget.save()
.then(res => Budget.findById(res._id)
.populate('User')
.populate('Vehicle')
.exec())
.then(res => {
res.foo = 'Foo'
return res
})

Mongoose: select method does not return the include field

I have one document like that
var ConfigSchema = new Schema({
basicConfig: {
levelId: {
type: Number,
required: true
},
hostId: {
type: Number,
required: true
},
Name: {
type: String,
trim: true
},
Settings: {
Type1: {
// some types here...
}
Type2: {
// some types here...
}
}
},
enrolls: {
name: {type: String},
list: [String]
},
awards: {
enable: {type: Boolean},
Primary: {
amount: {type: Number},
type: {type: String}
}
}
Now I want to find configs with hostId matches 60, and selecting basicConfig field.
Config.findOne({ 'basicConfig.hostId': 60 })
.select('basicConfig').exec(function(err, configs) {
if (err) {
console.error(err);
return ;
}
console.log(configs);
});
However, all fields of this document will be returned. It seems that the select does NOT work? Why?
Output:
{ _id: 555c4144c0bff1541d0e4059,
enrolls: {},
awards: { primary: { PrimarySettings: {}, primaryAck: {} } },
basicConfig:
{ levelId: 24,
hostId: 60,
poolName: 'LC' } }
Also, those following codes have been test, it does not work.
BonusConfig.findOne({ 'basicConfig.hostId': 60 }, 'basicConfig', function(err, configs) {
if (err) {
console.error(err);
return ;
}
console.log(configs);
});
But, without the basicConfig field with select with the following codes, it work well.
BonusConfig.findOne({ 'basicConfig.hostId': 60 })
.select('-basicConfig').exec(function(err, configs) {
if (err) {
console.error(err);
return ;
}
console.log(configs);
});
What's wrong with my codes?
Mongoose version: 3.8.24
Mongodb version: 2.6.7
Edit 1
Here is the query log in mongoose debug mode.
Mongoose: configs.findOne({ 'basicConfig.hostId': 60 }) { fields: { basicConfig: 1 } }
Edit 2
After further investigation.
The output result of Config.findOne({ 'basicConfig.hostId': 60 }).select('basicConfig'):
{ _id: 555c4144c0bff1541d0e4059,
enrolls: {},
awards: { primary: { PrimarySettings: {}, primaryAck: {} } },
basicConfig:
{ levelId: 24,
hostId: 60,
poolName: 'LC' } }
Other fields are empty value except basicConfig. However, I want the result is
{ _id: 555c4144c0bff1541d0e4059,
basicConfig:
{ levelId: 24,
hostId: 60,
poolName: 'LC' } }
Your projection of fields that need to be returned is missing the additional parameter. Try this:
BonusConfig.findOne({ 'basicConfig.hostId': 60 }, {'basicConfig':1}, function(err, configs) {
if (err) {
console.error(err);
return ;
}
console.log(configs);
});
Quoting this
This behavior is by design, but admittedly it's not very well-designed. Mongoose is over-eager when it comes to creating sub-docs when loading from the database. Planning on changing that in v5.

Resources