How I can solve $near query in mongoddb adbpter query building issue? - node.js

I'm trying to create a nearby searching platform with keystone.js
But the nearby queries not working with the keystone js.
How I can run mongoose $nearSphere query or aggregation queries in the keystone?
Hereby added my best try and errors.
Index.js
require('dotenv').config({
path: './.env'
});
const {
Keystone
} = require('#keystonejs/keystone');
const {
GraphQLApp
} = require('#keystonejs/app-graphql');
const {
AdminUIApp
} = require('#keystonejs/app-admin-ui');
const {
PasswordAuthStrategy
} = require('#keystonejs/auth-password');
const {
MongooseAdapter
} = require('#keystonejs/adapter-mongoose');
const initialiseData = require('./initial-data');
const {
Post,
User,
Category,
SubCategory,
Comment,
ForgottenPasswordToken,
} = require('./schema');
const PROJECT_NAME = 'rentospot';
const keystone = new Keystone({
appVersion: {
version: '1.0.0',
addVersionToHttpHeaders: false,
access: false,
},
cookie: {
secure: process.env.NODE_ENV === 'production', // Default to true in production
maxAge: 1000 * 60 * 60 * 24 * 30, // 30 days
sameSite: false,
},
cookieSecret: process.env.COOKIE_SECRET,
adapter: new MongooseAdapter({
mongoUri: 'mongodb+srv://*******:******.mongodb.net/my-app-key-stone'
}),
onConnect: process.env.CREATE_TABLES !== 'true' && initialiseData,
});
const postList = keystone.createList('Post', Post);
keystone.createList('Category', Category);
keystone.createList('SubCategory', SubCategory);
keystone.createList('Comment', Comment);
keystone.createList('User', User);
keystone.createList('ForgottenPasswordToken', ForgottenPasswordToken);
const authStrategy = keystone.createAuthStrategy({
type: PasswordAuthStrategy,
list: 'User',
});
const extendSchema = {
queries: [{
schema: 'nearBy: [Post]',
resolver: async(_) => {
try {
const {
adapter
} = postList;
const result = await adapter.find({
// "feature": true,
"venue.location": {
"$nearSphere": {
"$geometry": {
"type": "Point",
"coordinates": [12, 75],
},
"$minDistance": 0,
"$maxDistance": 500,
},
}
});
console.log(result)
return result;
} catch (e) {
console.log("errrrrrrrrrrrrrror: ", e)
return null
}
},
}, ]
};
keystone.extendGraphQLSchema(extendSchema)
module.exports = {
keystone,
apps: [new GraphQLApp(),
new AdminUIApp({
name: PROJECT_NAME,
enableDefaultRoute: true,
authStrategy
})
],
};
Schema.js
const fetch = require('node-fetch');
const { v4: uuid } = require('uuid');
const { sendEmail } = require('./emails');
const {
File,
Text,
Slug,
Relationship,
Select,
Password,
Checkbox,
CalendarDay,
DateTime,
Integer
} = require('#keystonejs/fields');
const { S3Adapter } = require('#keystonejs/file-adapters');
const { AuthedRelationship } = require('#keystonejs/fields-authed-relationship');
const { formatISO } = require('date-fns');
const { Wysiwyg } = require('#keystonejs/fields-wysiwyg-tinymce');
const LocationGoogle = require('./LocationGoogle');
const fileAdapter = new S3Adapter({
bucket: process.env.BUCKET_NAME,
folder: process.env.S3_PATH,
publicUrl: ({ filename }) =>
// `https://${process.env.CF_DISTRIBUTION_ID}.cloudfront.net/${process.env.S3_PATH}/${filename}`,
`https://${process.env.CF_DISTRIBUTION_ID}/${process.env.S3_PATH}/${filename}`,
s3Options: {
// Optional paramaters to be supplied directly to AWS.S3 constructor
apiVersion: '2006-03-01',
accessKeyId: process.env.API_KEY_ID,
secretAccessKey: process.env.SECRET_ACCESS_KEY,
region: process.env.REGION
},
uploadParams: ({ id }) => ({
Metadata: {
keystone_id: `${id}`,
},
}),
});
// Access control functions
const userIsAdmin = ({ authentication: { item: user } }) => Boolean(user && user.isAdmin);
const userOwnsItem = ({ authentication: { item: user } }) => {
if (!user) {
return false;
}
return { id: user.id };
};
const userIsCurrentAuth = ({ authentication: { item: user } }) => Boolean(user); // item will be undefined for anonymous user
const userIsAdminOrOwner = auth => {
const isAdmin = access.userIsAdmin(auth);
const isOwner = access.userOwnsItem(auth);
return isAdmin ? isAdmin : isOwner;
};
const access = { userIsAdmin, userIsCurrentAuth, userOwnsItem, userIsAdminOrOwner };
// Read: public / Write: admin
const DEFAULT_LIST_ACCESS = {
create: access.userIsCurrentAuth,
read: true,
update: access.userIsAdminOrOwner,
delete: access.userIsAdmin,
};
exports.User = {
access: {
update: access.userIsCurrentAuth,
delete: access.userIsAdmin,
},
fields: {
name: { type: Text },
dob: {
type: CalendarDay,
format: 'do MMMM yyyy',
dateFrom: '1901-01-01',
dateTo: formatISO(new Date(), { representation: 'date' }),
},
phone: {
type: Text,
isUnique: true,
access: { read: access.userIsCurrentAuth },
// hooks: {
// validateInput:
// }
},
email: { type: Text, isUnique: true, access: { read: access.userIsCurrentAuth } },
password: { type: Password, isRequired: true },
isAdmin: { type: Checkbox, access: { update: access.userIsAdmin } },
twitterHandle: { type: Text },
image: { type: File, adapter: fileAdapter },
},
hooks: {
afterChange: async ({ updatedItem, existingItem }) => {
if (existingItem && updatedItem.password !== existingItem.password) {
const url = process.env.SERVER_URL || 'http://localhost:3000';
const props = {
recipientEmail: updatedItem.email,
signinUrl: `${url}/signin`,
};
const options = {
subject: 'Your password has been updated',
to: updatedItem,
from: process.env.MAILGUN_FROM,
domain: process.env.MAILGUN_DOMAIN,
apiKey: process.env.MAILGUN_API_KEY,
};
await sendEmail('password-updated.jsx', props, options);
}
},
},
};
// exports.Organiser = {
// access: DEFAULT_LIST_ACCESS,
// fields: {
// user: { type: Relationship, ref: 'User' },
// order: { type: Integer },
// role: { type: Text },
// },
// };
// TODO: We can't access the existing item at the list update level yet,
// read access needs to check if event is "active" or if the user is admin
// read: ({ existingItem, authentication }) => access.userIsAdmin({ authentication }) || !!(existingItem && existingItem.status === 'active'),
exports.Post = {
access: DEFAULT_LIST_ACCESS,
fields: {
title: { type: Text },
slug: { type: Slug, from: 'title' },
author: {
type: AuthedRelationship,
ref: 'User',
isRequired: true,
access: {
read: userIsCurrentAuth,
update: userIsAdmin,
},
},
status: {
type: Select,
defaultValue: 'published',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
],
},
feature: { type: Checkbox, access: { update: access.userIsAdmin } },
assured: { type: Checkbox, access: { update: access.userIsAdmin } },
posted: { type: DateTime, format: 'dd/MM/yyyy' },
categories: {
type: Relationship,
ref: 'Category',
many: false,
},
subCategories: {
type: Relationship,
ref: 'SubCategory',
many: true,
},
description: { type: Wysiwyg },
venue: {
type: LocationGoogle,
googleMapsKey: process.env.GOOGLE_MAPS_KEY,
// hooks: {
// resolveInput: async ({ originalInput }) => {
// try {
// const placeId = originalInput.venue;
// if (typeof placeId === 'undefined') {
// // Nothing was passed in, so we can bail early.
// return undefined;
// }
// const r = await fetch(
// `https://maps.googleapis.com/maps/api/geocode/json?place_id=${placeId}&key=${process.env.GOOGLE_MAPS_KEY}`
// )
// const response = await r.json()
// if (response.results && response.results[0]) {
// const { place_id, formatted_address } = response.results[0];
// const { lat, lng } = response.results[0].geometry.location;
// return {
// googlePlaceID: place_id,
// formattedAddress: formatted_address,
// lat,
// lng,
// loc: {
// type: "Point",
// coordinates: [lng, lat]
// }
// };
// }
// return null;
// }
// catch (e) {
// console.log("error:", e)
// }
// },
// validateInput: async (x) => {
// console.log("---------------------------------item 2-------------------------------------- : ",x);
// },
// beforeChange: async (x) => {
// console.log("========", x)
// },
// afterChange: async (x) => {
// console.log("++++", x)
// }
// },
},
locationAddress: { type: Text },
locationDescription: { type: Text },
createdby: {
type: AuthedRelationship,
ref: 'User',
isRequired: true,
access: {
read: userIsCurrentAuth,
update: userIsAdmin,
},
},
image: {
type: File,
adapter: fileAdapter,
hooks: {
beforeChange: async ({ existingItem }) => {
if (existingItem && existingItem.image) {
await fileAdapter.delete(existingItem.image);
}
},
},
},
},
hooks: {
afterDelete: ({ existingItem }) => {
if (existingItem.image) {
fileAdapter.delete(existingItem.image);
}
},
},
adminConfig: {
defaultPageSize: 20,
defaultColumns: 'title, status',
defaultSort: 'title',
},
labelResolver: item => item.title,
};
exports.Category = {
// access: userIsAdmin,
fields: {
name: { type: Text },
slug: { type: Slug, from: 'name' },
image: {
type: File,
adapter: fileAdapter,
hooks: {
beforeChange: async ({ existingItem }) => {
if (existingItem && existingItem.image) {
await fileAdapter.delete(existingItem.image);
}
},
},
},
},
};
exports.SubCategory = {
// access: userIsAdmin,
fields: {
name: { type: Text },
slug: { type: Slug, from: 'name' },
categories: {
type: Relationship,
ref: 'Category',
many: false,
},
image: {
type: File,
adapter: fileAdapter,
hooks: {
beforeChange: async ({ existingItem }) => {
if (existingItem && existingItem.image) {
await fileAdapter.delete(existingItem.image);
}
},
},
},
},
};
exports.Comment = {
access: {
create: userIsCurrentAuth,
update: userIsAdminOrOwner,
},
fields: {
body: { type: Text, isMultiline: true },
originalPost: {
type: Relationship,
ref: 'Post',
},
author: {
type: AuthedRelationship,
ref: 'User',
isRequired: true,
access: {
create: userIsAdmin,
update: userIsAdmin,
},
},
posted: { type: CalendarDay },
},
labelResolver: item => item.body,
};
// exports.Talk = {
// access: DEFAULT_LIST_ACCESS,
// fields: {
// name: { type: Text },
// event: { type: Relationship, ref: 'Event.talks' },
// speakers: { type: Relationship, ref: 'User.talks', many: true },
// isLightningTalk: { type: Checkbox },
// description: { type: Wysiwyg },
// },
// };
// exports.Rsvp = {
// access: {
// create: true,
// read: true,
// update: ({ authentication: { item } }) => {
// if (!item) {
// return false;
// }
// return { user: { id: item.id } };
// },
// delete: access.userIsAdmin,
// },
// fields: {
// event: { type: Relationship, ref: 'Event' },
// user: { type: Relationship, ref: 'User' },
// status: { type: Select, options: 'yes, no' },
// },
// hooks: {
// validateInput: async ({ context, resolvedData, existingItem }) => {
// const { status } = resolvedData;
// const { event: eventId } = existingItem ? existingItem : resolvedData;
// if (status === 'no') {
// return;
// }
// const { data } = await context.executeGraphQL({
// query: `query {
// event: Event(where: { id: "${eventId}" }) {
// id
// startTime
// maxRsvps
// isRsvpAvailable
// }
// allRsvps(where: { event: { id: "${eventId}" }}) {
// id
// }
// }`,
// });
// const { event, allRsvps } = data;
// if (
// !event ||
// !event.isRsvpAvailable ||
// !event.startTime ||
// new Date() > new Date(event.startTime) ||
// allRsvps.length >= event.maxRsvps
// ) {
// throw 'Error rsvping to event';
// }
// },
// },
// };
// exports.Sponsor = {
// access: DEFAULT_LIST_ACCESS,
// fields: {
// name: { type: Text },
// website: { type: Text },
// logo: { type: CloudinaryImage, adapter: cloudinaryAdapter },
// },
// };
exports.ForgottenPasswordToken = {
access: {
create: true,
read: true,
update: access.userIsAdmin,
delete: access.userIsAdmin,
},
fields: {
user: {
type: Relationship,
ref: 'User',
access: {
read: access.userIsAdmin,
},
},
token: {
type: Text,
isRequired: true,
isUnique: true,
access: {
read: access.userIsAdmin,
},
},
requestedAt: { type: DateTime, isRequired: true },
accessedAt: { type: DateTime },
expiresAt: { type: DateTime, isRequired: true },
},
hooks: {
afterChange: async ({ context, updatedItem, existingItem }) => {
if (existingItem) return null;
const now = new Date().toISOString();
const { errors, data } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `
query GetUserAndToken($user: ID!, $now: DateTime!) {
User( where: { id: $user }) {
id
email
}
allForgottenPasswordTokens( where: { user: { id: $user }, expiresAt_gte: $now }) {
token
expiresAt
}
}
`,
variables: { user: updatedItem.user.toString(), now },
});
if (errors) {
console.error(errors, `Unable to construct password updated email.`);
return;
}
const { allForgottenPasswordTokens, User } = data;
const forgotPasswordKey = allForgottenPasswordTokens[0].token;
const url = process.env.SERVER_URL || 'http://localhost:3000';
const props = {
forgotPasswordUrl: `${url}/change-password?key=${forgotPasswordKey}`,
recipientEmail: User.email,
};
const options = {
subject: 'Request for password reset',
to: User.email,
from: process.env.MAILGUN_FROM,
domain: process.env.MAILGUN_DOMAIN,
apiKey: process.env.MAILGUN_API_KEY,
};
await sendEmail('forgot-password.jsx', props, options);
},
},
};
exports.customSchema = {
mutations: [
{
schema: 'startPasswordRecovery(email: String!): ForgottenPasswordToken',
resolver: async (obj, { email }, context) => {
const token = uuid();
const tokenExpiration =
parseInt(process.env.RESET_PASSWORD_TOKEN_EXPIRY) || 1000 * 60 * 60 * 24;
const now = Date.now();
const requestedAt = new Date(now).toISOString();
const expiresAt = new Date(now + tokenExpiration).toISOString();
const { errors: userErrors, data: userData } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `
query findUserByEmail($email: String!) {
allUsers(where: { email: $email }) {
id
email
}
}
`,
variables: { email: email },
});
if (userErrors || !userData.allUsers || !userData.allUsers.length) {
console.error(
userErrors,
`Unable to find user when trying to create forgotten password token.`
);
return;
}
const userId = userData.allUsers[0].id;
const result = {
userId,
token,
requestedAt,
expiresAt,
};
const { errors } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `
mutation createForgottenPasswordToken(
$userId: ID!,
$token: String,
$requestedAt: DateTime,
$expiresAt: DateTime,
) {
createForgottenPasswordToken(data: {
user: { connect: { id: $userId }},
token: $token,
requestedAt: $requestedAt,
expiresAt: $expiresAt,
}) {
id
token
user {
id
}
requestedAt
expiresAt
}
}
`,
variables: result,
});
if (errors) {
console.error(errors, `Unable to create forgotten password token.`);
return;
}
return true;
},
},
{
schema: 'changePasswordWithToken(token: String!, password: String!): User',
resolver: async (obj, { token, password }, context) => {
const now = Date.now();
const { errors, data } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `
query findUserFromToken($token: String!, $now: DateTime!) {
passwordTokens: allForgottenPasswordTokens(where: { token: $token, expiresAt_gte: $now }) {
id
token
user {
id
}
}
}`,
variables: { token, now },
});
if (errors || !data.passwordTokens || !data.passwordTokens.length) {
console.error(errors, `Unable to find token`);
throw errors.message;
}
const user = data.passwordTokens[0].user.id;
const tokenId = data.passwordTokens[0].id;
const { errors: passwordError } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `mutation UpdateUserPassword($user: ID!, $password: String!) {
updateUser(id: $user, data: { password: $password }) {
id
}
}`,
variables: { user, password },
});
if (passwordError) {
console.error(passwordError, `Unable to change password`);
throw passwordError.message;
}
await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `mutation DeletePasswordToken($tokenId: ID!) {
deleteForgottenPasswordToken(id: $tokenId) {
id
}
}
`,
variables: { tokenId },
});
return true;
},
},
],
};
LocationGoogle is a directory of the files exactly #keystonejs/fields-location-google and small changes in its Implementation.js
Implementation.js
const { Implementation } = require('#keystonejs/fields');
const { MongooseFieldAdapter } = require('#keystonejs/adapter-mongoose');
const fetch = require('node-fetch');
// Disabling the getter of mongoose >= 5.1.0
// https://github.com/Automattic/mongoose/blob/master/migrating_to_5.md#checking-if-a-path-is-populated
class LocationGoogleImplementation extends Implementation {
constructor(_, { googleMapsKey }) {
super(...arguments);
this.graphQLOutputType = 'LocationGoogle';
if (!googleMapsKey) {
throw new Error(
'You must provide a `googleMapsKey` to LocationGoogle Field. To generate a Google Maps API please visit: https://developers.google.com/maps/documentation/javascript/get-api-key'
);
}
this._googleMapsKey = googleMapsKey;
}
get _supportsUnique() {
return false;
}
extendAdminMeta(meta) {
return {
...meta,
googleMapsKey: this._googleMapsKey,
};
}
gqlOutputFields() {
return [`${this.path}: ${this.graphQLOutputType}`];
}
gqlQueryInputFields() {
return [...this.equalityInputFields('String'), ...this.inInputFields('String')];
}
getGqlAuxTypes() {
return [
`
type Location{
coordinates: [Float]
},
type ${this.graphQLOutputType} {
location: Location
googlePlaceID: String
formattedAddress: String
lat: Float
lng: Float
}
`,
];
}
// Called on `User.avatar` for example
gqlOutputFieldResolvers() {
return {
[this.path]: item => {
const itemValues = item[this.path];
if (!itemValues) {
return null;
}
return itemValues;
},
};
}
async resolveInput({ resolvedData }) {
const placeId = resolvedData[this.path];
// NOTE: The following two conditions could easily be combined into a
// single `if (!inputId) return inputId`, but that would lose the nuance of
// returning `undefined` vs `null`.
// Premature Optimisers; be ware!
if (typeof placeId === 'undefined') {
// Nothing was passed in, so we can bail early.
return undefined;
}
if (placeId === null) {
// `null` was specifically set, and we should set the field value to null
// To do that we... return `null`
return null;
}
const response = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?place_id=${placeId}&key=${this._googleMapsKey}`
).then(r => r.json());
if (response.results && response.results[0]) {
const { place_id, formatted_address } = response.results[0];
const { lat, lng } = response.results[0].geometry.location;
return {
location: {
type: "Point",
coordinates: [lng, lat]
},
googlePlaceID: place_id,
formattedAddress: formatted_address,
lat: lat,
lng: lng,
};
}
return null;
}
gqlUpdateInputFields() {
return [`${this.path}: String`];
}
gqlCreateInputFields() {
return [`${this.path}: String`];
}
getBackingTypes() {
const type = `null | {
location: {
type: "Point",
coordinates: [lng, lat]
};
googlePlaceID: string;
formattedAddress: string;
lat: number;
lng: number;
}
`;
return { [this.path]: { optional: true, type } };
}
}
const CommonLocationInterface = superclass =>
class extends superclass {
getQueryConditions(dbPath) {
return {
...this.equalityConditions(dbPath),
...this.inConditions(dbPath),
};
}
};
class MongoLocationGoogleInterface extends CommonLocationInterface(MongooseFieldAdapter) {
addToMongooseSchema(schema) {
const schemaOptions = {
type: {
location: {
type: { type: String, default: "Point" },
coordinates: [Number]
},
googlePlaceID: String,
formattedAddress: String,
lat: Number,
lng: Number,
},
};
schema.add({
[this.path]: this.mergeSchemaOptions(schemaOptions, this.config)
});
}
}
module.exports = {
LocationGoogleImplementation,
MongoLocationGoogleInterface
}
package.json
{
"name": "#keystonejs/example-projects-blank",
"description": "A blank KeystoneJS starter project.",
"private": true,
"version": "5.0.15",
"author": "The KeystoneJS Development Team",
"repository": "https://github.com/keystonejs/keystone/tree/master/packages/create-keystone-app/example-projects/blank",
"homepage": "https://github.com/keystonejs/keystone",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"scripts": {
"dev": "cross-env NODE_ENV=development ENABLE_DEV_FEATURES=false DISABLE_LOGGING=false nodemon --exec keystone dev --harmony",
"build": "cross-env NODE_ENV=production ENABLE_DEV_FEATURES=false keystone build",
"start": "cross-env NODE_ENV=production keystone start",
"create-tables": "cross-env keystone create-tables"
},
"dependencies": {
"#arch-ui/layout": "^0.2.14",
"#arch-ui/typography": "^0.0.18",
"#keystonejs/adapter-mongoose": "^10.1.2",
"#keystonejs/app-admin-ui": "^7.3.13",
"#keystonejs/app-graphql": "^6.2.1",
"#keystonejs/auth-password": "^6.0.0",
"#keystonejs/email": "^5.2.0",
"#keystonejs/fields-authed-relationship": "^1.0.16",
"#keystonejs/fields-location-google": "^3.2.1",
"#keystonejs/fields-wysiwyg-tinymce": "^5.3.15",
"#keystonejs/file-adapters": "^7.0.8",
"#keystonejs/keystone": "^18.1.0",
"cross-env": "^7.0.3",
"date-fns": "^2.16.1",
"dotenv": "^8.2.0",
"google-maps-react": "^2.0.6",
"node-fetch": "^2.6.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"uuid": "^8.3.2"
},
"devDependencies": {
"nodemon": "^2.0.7"
}
}
The Error screenshot attached bellow

Related

Node-orcaledb: execute() and executeMany() crash without error

I am building an api rest with node-oracledb, I can retrieve all kind of data but the program breaks every time I try to do an insert.
This is the generic method I use to get a connection from the pool and perform the query.
import oracledb from "oracledb";
export const executeQuery = async ({ query, binds, options, type, res }) => {
let connection = null;
try {
connection = await oracledb.getConnection();
} catch (error) {
console.log("Error al conectar OracleDB");
}
let result = null;
try {
result =
type === "insertOne"
? await connection.execute(query, binds, options)
: type === "insertMany"
? await connection.executeMany(query, binds, options)
: null;
console.log(result);
} catch (err) {
console.error("error", err.message);
res.status(500).json("Error recuperando datos");
} finally {
if (connection) {
try {
res.status(200).json(result.rows);
// Always release the connection back to the pool
await connection.close();
} catch (err) {
return console.error(err.message);
}
}
}
};
This is the controller method with which I am trying to insert a single record, in production the bind data would come from a post request.
insertOneExample: async (req, res) => {
const { items } = req.body;
const query = `MERGE INTO SCHEMA.TABLE USING dual ON (CODIGO_HOSPI = :CODIGO_HOSPI AND CENTRO_ID = :CENTRO_ID)
WHEN MATCHED THEN UPDATE SET PREV1 = :PREV1, PREV2 = :PREV2, PREV3 = :PREV3, PREV4 = :PREV4, PREV5 = :PREV5, PREV6 = :PREV6, PREV7 = :PREV7, PREV8 = :PREV8, PREV9 = :PREV9, PREV10 = :PREV10, PREV11 = :PREV11, PREV12 = :PREV12,
WHEN NOT MATCHED THEN INSERT (CODIGO_HOSPI, PREV1, PREV2, PREV3, PREV4, PREV5, PREV6, PREV7, PREV8, PREV9, PREV10, PREV11, PREV12, CENTRO_ID)
VALUES (:CODIGO_HOSPI, :PREV1, :PREV2, :PREV3, :PREV4, :PREV5, :PREV6, :PREV7, :PREV8, :PREV9, :PREV10, :PREV11, :PREV12, :CENTRO_ID)`
const options = {
autoCommit: true,
bindDefs: {
CODIGO_HOSPI: { type: oracledb.STRING, maxSize: 20 },
PREV1: { type: oracledb.NUMBER },
PREV2: { type: oracledb.NUMBER },
PREV3: { type: oracledb.NUMBER },
PREV4: { type: oracledb.NUMBER },
PREV5: { type: oracledb.NUMBER },
PREV6: { type: oracledb.NUMBER },
PREV7: { type: oracledb.NUMBER },
PREV8: { type: oracledb.NUMBER },
PREV9: { type: oracledb.NUMBER },
PREV10: { type: oracledb.NUMBER },
PREV11: { type: oracledb.NUMBER },
PREV12: { type: oracledb.NUMBER },
CENTRO_ID: { type: oracledb.STRING, maxSize: 10 },
},
};;
executeQuery({
query,
binds: {
CODIGO_HOSPI: "101",
PREV1: 52600,
PREV2: 870,
PREV3: 123,
PREV4: 564,
PREV5: 846,
PREV6: 625,
PREV7: 897,
PREV8: 124,
PREV9: 656,
PREV10: 456,
PREV11: 324,
PREV12: 212,
CENTRO_ID: "10346",
},
options,
type: "insertOne",
res,
});
}
When executing the method, the server crashes without any error message.
no error crash
*** sql statement is not the problem, it also crashes with a simple insert.
Try sending parameter values as strings instead of numbers, that fixed my issue.
Reference: https://github.com/oracle/node-oracledb/issues/1377#issuecomment-1346171847

Mongoose / Node : How to add an object to an array?

I have a problem when I try to update an array with Mongoose/Node.
I want to add my new price for example, req.body value is : { price: 12 } or req.body is : { description: 'my description' } but when I do this the total array is replace by just my new object ! :/
Here is my model:
const restaurantSchema = mongoose.Schema({
userId: { type: Object, required: true },
name: { type: String },
menus: [{
name: { type: String },
price: { type: String },
description: { type: String },
}],
})
And my node Js code :
const menuUpdate = req.body;
const menuId = req.params.menuId;
const userId = userIdFromToken(req);
const filter = {
userId: userId,
"menus._id": menuId
};
const update = { $set: { "menus.$": menuUpdate } };
const options = {
upsert: true,
new: true
};
Restaurant.findOneAndUpdate(filter, update, options).then(() => {
return res.status(204).json({ message: "Menus updated " });
});
Thanks for your help,
David
====
I change my code with the help of #aks, like this...
const menuUpdate = req.body;
for (const [key, value] of Object.entries(menuUpdate)) {
this.menuKey = `${key}`;
this.menuValue = `${value}`;
}
if (this.menuKey === 'name') {
this.update = { $set: { "menus.$.name": this.menuValue } };
}
if (this.menuKey === 'price') {
this.update = { $set: { "menus.$.price": this.menuValue } };
}
if (this.menuKey === 'description') {
this.update = { $set: { "menus.$.description": this.menuValue } };
}
const menuId = req.params.menuId;
const userId = userIdFromToken(req);
const filter = {
userId: userId,
'menus._id': menuId,
};
const options = {
upsert: true,
new: true
};
Restaurant
.findOneAndUpdate(
filter,
this.update,
options,
)
.then ( () => {
return res.status(204).json({ message: 'Menus updated ' });
});
Is there a way to simplify that without if ?
Your Node code
menus: [{
name: "toto",
price: 25,
description: "custom sweet",
}]
Now You have to update only the price from 25 to 45 for that you have to send the whole array.
So you have to simple set the array with this value
And if you go to other approach
then on req.body add one more parameter i.e menuIndex: 2
And on you update request make the condition if menuIndex is 2 then update specific column
const menuUpdate = req.body;
const menuId = req.params.menuId;
const userId = userIdFromToken(req);
const filter = {
userId: userId,
"menus._id": menuId
};
let update = {};
if (req.body.menuIndex === 1) {
update = { $set: { "menus.$.name": req,body.val} };
}
if (req.body.menuIndex === 2) {
update = { $set: { "menus.$.price": req,body.val
} };
}
if (req.body.menuIndex === 3) {
update = { $set: { "menus.$.description": req,body.val} };
}
const options = {
upsert: true,
new: true
};
Restaurant.findOneAndUpdate(filter, update, options).then(() => {
return res.status(204).json({ message: "Menus updated " });
});

Show specific columns with dataloader-sequelize in nested tables

Currently I have some models. I'm using graphql with dataloader-sequelize and it works fine as long as I show associated tables without third level.
My models:
"articulo.js"
'use strict';
module.exports = (sequelize, DataTypes) => {
const Articulo = sequelize.define(
'articulos',
{
art_codigo: {
type: DataTypes.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true
},
art_nombre: DataTypes.STRING(255),
art_longitud: DataTypes.STRING(250),
art_latitud: DataTypes.STRING(250),
.....[more columns]
art_contenido: DataTypes.TEXT,
},
{
timestamps: false,
freezeTableName: true,
name: {
singular: 'Articulo',
plural: 'Articulos',
},
indexes: [
{
unique: true,
fields: ['art_codigo'],
},
],
}
);
Articulo.associate = (models) => {
Articulo.belongsTo(models.canalizados,
{
foreignKey: 'art_canalizado',
as:"Canalizado",
}
);
Articulo.belongsTo(
models.articulos_tipos,
{
foreignKey: 'art_tipo'
}
);
};
return Articulo;
};
articulo_tipo.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const ArticuloTipo = sequelize.define('articulos_tipos', {
ari_codigo: {
type: DataTypes.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true
},
ari_nombre: DataTypes.STRING(255),
}, {
timestamps: false,
freezeTableName: true,
name: {
singular: 'ArticuloTipo',
plural: 'ArticulosTipos',
},
indexes: [
{
unique: true,
fields: ['ari_codigo'],
},
],
});
ArticuloTipo.associate = (models) => {
ArticuloTipo.hasMany(models.articulos)
};
return ArticuloTipo;
};
canalizado.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const Canalizado = sequelize.define('canalizados', {
cnl_codigo: {
type: DataTypes.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true
},
cnl_fecha_alta: DataTypes.DATE,
...... [more columns]
cnl_revisado: DataTypes.BOOLEAN,
}, {
timestamps: false,
freezeTableName: true,
name: {
singular: 'Canalizado',
plural: 'Canalizados',
},
indexes: [
{
unique: true,
fields: ['cnl_codigo'],
},
],
}
);
Canalizado.associate = (models) => {
Canalizado.hasMany(models.articulos);
Canalizado.belongsTo(
models.canalizados_tipos,
{
foreignKey: 'cnl_tipo',
}
);
};
return Canalizado;
};
canalizado_tipo.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const CanalizadoTipo = sequelize.define('canalizados_tipos', {
cai_codigo: {
type: DataTypes.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true
},
cai_nombre: DataTypes.STRING(50)
}, {
timestamps: false,
freezeTableName: true,
tableName: "canalizados_tipos",
name: {
singular: 'CanalizadoTipo',
plural: 'CanalizadosTipo',
},
indexes: [
{
unique: true,
fields: ['cai_codigo'],
},
],
});
CanalizadoTipo.associate = (models) => {
CanalizadoTipo.hasMany(models.canalizados)
};
return CanalizadoTipo;
};
My resolvers:
articulo.js
const Sequelize = require('sequelize');
const {detectarCampos} = require('../_extra/comunes'); //Optimize which columns you want to use in graphql
const Op = Sequelize.Op;
const resolvers = {
Articulo:{
art_tipo: (parent, args, { models, options }, info) => {
return parent.getArticuloTipo(options); //It's an internal getter from sequelize, isn't it?
},
art_canalizado: (parent, args, { models, options }, info) => {
return parent.getCanalizado(options); //It's an internal getter from sequelize, isn't it?
},
},
Query: {
async getArticulo(root, { codigo }, { models }, info) {
return models.articulos.findByPk(
codigo,
{attributes: detectarCampos(info),}
);
},
async getArticulos(root, { nombre, tipo}, { models, options }, info) {
var whereStatement = {};
if(nombre){
whereStatement.art_nombre = {[Op.like]: '%' + nombre + '%'};
}
if (tipo){
whereStatement.art_tipo = tipo;
}
return models.articulos.findAll({
attributes: detectarCampos(info),
where: whereStatement,
//limit: 10,
options
});
},
async getAllArticulos(root, args, { models }, info) {
return models.articulos.findAll( {
attributes: detectarCampos(info),
limit: 10,
});
},
},
Mutation: {
},
}
module.exports = resolvers
canalizado.js
const {detectarCampos} = require('../_extra/comunes');
const resolvers = {
Canalizado:{
cnl_tipo: (parent, args, { models, options }, info) => {
return parent.getCanalizadoTipo(options)
},
},
Query: {
async getCanalizado(root, { codigo }, { models, context }, info) {
return await models.canalizados.findByPk(codigo,
{attributes: detectarCampos(info),});
},
async getCanalizados(root, { tipo }, { models, options }, info) {
var whereStatement = {};
if (tipo)
whereStatement.cnl_tipo = tipo;
return models.canalizados.findAll({
attributes: detectarCampos(info),
where: whereStatement,
limit: 2,
options
});
},
async getAllCanalizados(root, args, { models, options }) {
return models.canalizados.findAll({
attributes: detectarCampos(info),
limit: 100,
options
});
},
},
Mutation: {
},
}
module.exports = resolvers
It works fine if I search in graphql with this sentence:
query{
getArticulos(tipo:2){
art_codigo
art_nombre
art_tipo{
ari_nombre
}
art_latitud
art_longitud
}
}
Executing (default): SELECT [art_codigo], [art_nombre], [art_tipo], [art_latitud], [art_longitud] FROM [articulos] AS [articulos] WHERE [articulos].[art_tipo] = 2;
Executing (default): SELECT [ari_codigo], [ari_nombre] FROM [articulos_tipos] AS [articulos_tipos] WHERE [articulos_tipos].[ari_codigo] IN (2);
On the other hand, if I try to look for in a deeper level, I get automatic names from columns I don't need to use:
query{
getArticulos(tipo:2){
art_codigo
art_nombre
art_tipo{
ari_nombre
}
art_canalizado{
cnl_codigo
}
art_latitud
art_longitud
}
}
Executing (default): SELECT [art_codigo], [art_nombre], [art_tipo], [art_latitud], [art_longitud] FROM [articulos] AS [articulos] WHERE [articulos].[art_tipo] = 2;
Executing (default): SELECT [ari_codigo], [ari_nombre] FROM [articulos_tipos] AS [articulos_tipos] WHERE [articulos_tipos].[ari_codigo] IN (2);
Executing (default): SELECT [cnl_codigo], [cnl_fecha_alta], [........], [cnl_revisado], [cnl_tipo], [cnl_fuente], [cnl_autor], [CanalizadoTipoCaiCodigo] FROM [canalizados] AS [canalizados] WHERE [canalizados].[cnl_codigo] IN (51357, 51365, 51379, [........], 63910);
In this case, in Graphql returns this error:
"message": "Invalid column name 'CanalizadoTipoCaiCodigo'.",
How can I ommite that field?? Could I use something like "attributes" to specify which attributes I'd like to show?? I tried to use it in resolvers, models... but always with no success
This error is the same if I look for a deep level:
query{
getArticulos(relevancia:2){
art_codigo
art_nombre
art_tipo{
ari_nombre
}
art_canalizado{
cnl_codigo
cnl_tipo{
cai_nombre
}
}
art_latitud
art_longitud
}
}
Any idea about my problem? Everrything is wellcome!!
UPDATE
server.js
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const typeDefs = require('./configuracion/schema/typeDefs')
const resolvers = require('./configuracion/schema/resolvers')
const models = require('./configuracion/models')
const { createContext, EXPECTED_OPTIONS_KEY } = require('dataloader-sequelize');
const dataloaderContext = createContext(models.sequelize);
//const server = new ApolloServer({ typeDefs, resolvers, context: { models } });
const server = new ApolloServer({
typeDefs,
resolvers,
context: async () => ({
models,
options: { [ EXPECTED_OPTIONS_KEY ]: dataloaderContext },
}),
});
const app = express();
server.applyMiddleware({ app });
models.sequelize.authenticate().then((err) => {
console.log('*** MSG [server.js]: Successful Connection');
})
.catch((err) => {
console.log('*** ERROR [server.js]: No ha sido posible conectarse a la base de datos', err);
})
//models.sequelize.sync();
app.listen({ port: 3000 }, () =>
console.log(`** API ready at http://localhost:3000${server.graphqlPath} `)
);
configuracion/models/index.js
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
//const env = process.env.NODE_ENV || 'development';
const config = require('../config_sqlserver')
const db = {};
const sequelize = new Sequelize(config.db_database, config.db_user, config.db_password,
{
host: config.db_host,
port: config.DB_PORT, // <----------------The port number you copied
dialect: "mssql",
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
}
);
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
//const model = sequelize['import'](path.join(__dirname, file));
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
resolver > articulo_tipo.js
const Sequelize = require('sequelize');
const {detectarCampos} = require('../_extra/comunes');
const Op = Sequelize.Op;
const resolvers = {
Query: {
async getArticuloTipo(root, { codigo }, { models, context }, info) {
return await models.articulos_tipos.findByPk(codigo, { attributes: detectarCampos(info)},);
},
async getArticulosTipos(_, { nombre, tipo }, { models }, info) {r
var whereStatement = {};
if(nombre)
whereStatement.ari_nombre = {[Op.like]: '%' + nombre + '%'};
if(tipo)
whereStatement.ari_codigo = tipo;
return models.articulos_tipos.findAll({
attributes: detectarCampos(info),
where: whereStatement,
});
},
async getAllArticulosTipos(root, args, { models }) {
return models.articulos_tipos.findAll()
},
},
Mutation: {
},
}
module.exports = resolvers
I don't use sequelize ... but I probably can point you in the right direction:
attributes are used already...
maybe not exactly the way you need ...
check what is returned from detectarCampos(info) in resolvers
Probably you'll find that info is undefined ... sometimes info is missing... why!?
art_canalizado: (parent, args, { models, options }, info) => {
return parent.getCanalizado(options); //It's an internal getter from sequelize, isn't it?
},
getCanalizado is called with options while normally it should be called with more arguments:
async getCanalizado(root, { codigo }, { models, context }, info) {
Fix:
Pass missging arguments - it should work (if detectarCampos works, of course).

How to create comment object type to todos object using graphql and react?

I am making a simple react-native app based on todos sample app. GraphQL and parse are used to manage data. I am trying to modify the schema and add a comment object to the todo objects but not sure what is the best way to do it.
How can I make it so when I add a new comment with a supplied todo id then it adds under the todo item?
I appreciate any help to fix comment object schema and add comment mutation.
const TODOS = Parse.Object.extend('Todos');
let getTodos = (status, context) => {
console.log("context: " + JSON.stringify(context));
let q = new Parse.Query(TODOS);
if (status !== undefined && status.toLowerCase() !== 'any')
{
q.startsWith("status", status);
}
return q.find();
};
let getTodo = (id) => {
let q = new Parse.Query(TODOS);
return q.get(id);
};
const todoType = new GraphQLObjectType({
name: 'Todo',
description: 'Todo type',
fields: () =>({
id: globalIdField('Todo'),
_id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => obj.id
},
title: {
type: GraphQLString,
resolve: (obj) => obj.get('title')
},
completed: {
type: GraphQLBoolean,
resolve: (obj) => obj.get('completed')
}
}),
interfaces: [nodeDefs.nodeInterface]
});
const todoConnection = connectionDefinitions({
name: 'Todo',
nodeType: todoType
});
let viewerType = new GraphQLObjectType({
name: 'Viewer',
fields: () => ({
id: globalIdField("Viewer"),
todoConnection: {
type: todoConnection.connectionType,
args: {
status: {
type: GraphQLString,
defaultValue: 'any'
},
...connectionArgs
},
resolve: (_, {status, ...args}, context) => {
return connectionFromPromisedArray(getTodos(status, context), args)
}
},
}),
interfaces: [nodeDefs.nodeInterface]
});
I tried to add following to todoType in fields object but I am not sure if it would work.
comment:{
type: commentConnection.connectionType,
args: {
status: {
type: GraphQLString,
defaultValue: 'any'
},
...connectionArgs
},
resolve: (_, {status, ...args}, context) => {
return connectionFromPromisedArray(getComments(status, context), args)
}
}
Where
let getComments = (status, context) => {
console.log("context: " + JSON.stringify(context));
let c = new Parse.Query(COMMENTS);
if (status !== undefined && status.toLowerCase() !== 'any')
{
c.startsWith("status", status);
}
return c.find({ sessionToken: context.parseSessionToken });
};
let getComment = (id) => {
let c = new Parse.Query(COMMENTS);
return c.get(id);
};
const commentType = new GraphQLObjectType({
name: 'Comment',
description: 'Comment type',
fields: () =>({
id: globalIdField('Comment'),
_id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => obj.id
},
comment: {
type: GraphQLString,
resolve: (obj) => obj.get('comment')
}
}),
interfaces: [nodeDefs.nodeInterface]
});
const commentConnection = connectionDefinitions({
name: 'Comment',
nodeType: commentType
});
Here is the graphql query:
query{
viewer{
todoConnection {
edges
{
node {
id,
title,
comment {
edges
{
node{
id,
comment
}
}
}
}
}
}
}
}
And this is todo mutation:
let addTodoMutation = mutationWithClientMutationId({
name: 'AddTodo',
inputFields: {
title: { type: new GraphQLNonNull(GraphQLString) },
},
outputFields: {
todoEdge: {
type: todoConnection.edgeType,
resolve: (obj) => ({node: obj, cursor: obj.id})
},
viewer: {
type: viewerType,
resolve: () => viewer
}
},
mutateAndGetPayload: ({title}) => {
let newTodo = new TODOS();
newTodo.set('title', title);
newTodo.set('completed', false);
return newTodo.save();
}
});

Abstract type Node must resolve to an Object type at runtime for field Root.node with value \"\",received \"null\"."

I am implementing a search feature with react and relay.
Below is my schema.js
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
}else if (type === 'Post') {
return getPost(id);
}else if (type === 'Setting') {
return getSetting(id);
}
return null;
},
(obj) => {
if (obj instanceof User) {
return userType;
}else if (obj instanceof Post) {
return postType;
}else if (obj instanceof Setting) {
return settingType;
}
return null;
}
);
var postType = new GraphQLObjectType({
name: 'Post',
fields: {
_id: {
type: new GraphQLNonNull(GraphQLID)
},
createdAt: {
type: GraphQLString
},
id: globalIdField('Post'),
title: {
type: GraphQLString
},
color: {
type: GraphQLString
},
userId: globalIdField('User'),
username: {
type: GraphQLString,
resolve: (post) => getUserById(post.userId),
},
content: {
type: GraphQLString
},
images: {
type: postImageType,
description: "Post's main image links"
}
},
interfaces: [nodeInterface]
});
const {
connectionType: postConnection,
} = connectionDefinitions({name: 'Post', nodeType: postType});
var settingType = new GraphQLObjectType({
name: 'Setting',
fields: {
_id: {
type: new GraphQLNonNull(GraphQLID)
},
id: globalIdField('Setting'),
amount: {
type: GraphQLString
},
all_posts: {
type: postConnection,
args: {
...connectionArgs,
query: {type: GraphQLString}
},
resolve: (rootValue, args) => connectionFromPromisedArray(
getAllPosts(rootValue, args),
args
),
},
},
interfaces: [nodeInterface]
});
var Root = new GraphQLObjectType({
name: 'Root',
fields: () => ({
node: nodeField,
setting: {
type: settingType,
args: {
...connectionArgs,
currency: {type: GraphQLString}
},
resolve: (rootValue, args) => {
return getSetting(args.currency).then(function(data){
return data[0];
}).then(null,function(err){
return err;
});
}
},
})
});
Below is my database.js
export function getAllPosts(params,args) {
let findTitle = {};
let findContent = {};
if (args.query) {
findTitle.title = new RegExp(args.query, 'i');
findContent.content = new RegExp(args.query, 'i');
}
console.log("getAllPosts",args)
return new Promise((resolve, reject) => {
Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec({}, function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
})
}
Now I want to fetch all posts by $query variable
So in view I wrote like this
import React, { Component } from 'react';
import Relay from 'react-relay';
class BlogList extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(){
this.props.relay.setVariables({query: this.state.query});
}
render() {
return (
<div className="input-group col-md-12">
<input type="text" onChange={this.handleChange.bind(this,"query")} value={this.state.query} name="query" placeholder="Enter Title or content"/><br/>
<span className="input-group-btn">
<button type="button" onClick={this.handleSubmit} className="btn btn-info btn-lg">
<i className="glyphicon glyphicon-search"></i>
</button>
</span>
</div>
)
}
};
export default Relay.createContainer(BlogList, {
initialVariables: {
query: ''
},
fragments: {
viewer: () => Relay.QL`
fragment on Setting {
id,
all_posts(first: 10000000,query: $query) {
edges {
node {
id,
_id,
title,
content,
createdAt,
username,
color,
images{
full
}
}
}
}
}
`,
},
});
And in routes I have
const SettingQueries = {
viewer: () => Relay.QL`query{
setting(currency: "USD")
}`,
}
export default [{
path: '/',
component: App,
queries: UserQueries,PostQueries,SettingQueries,
indexRoute: {
component: IndexBody,
},
childRoutes: [
,{
path: 'settings',
component: Setting,
queries: SettingQueries,
}]
}]
Things are working on /graphql as
but when I search from website it generates error in response
{
"data": {
"node": null
},
"errors": [
{
"message": "Abstract type Node must resolve to an Object type at runtime for field Root.node with value \"\",received \"null\".",
"locations": [
{
"line": 2,
"column": 3
}
]
}
]
}
as my web-browser is sending requests as below
Please suggest me what am I missing?
Also If I need to add some additional information please let me know.
The problem might be in your nodeDefinitions() function. First callback, also named idFetcher must return a single object. However, i see in your definition that you return a collection
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
...
}else if (type === 'Post') {
return getPosts(); // this should be getPost(id)
}
);
And thats why your next callback, known as typeResolver fails and returns you a null.
var { nodeInterface, nodeField } = nodeDefinitions(
...
(obj) => {
...
// here you get Promise/Collection instead of single Post instance, therefore condition failed
}else if (obj instanceof Post) {
return postType;
}
return null;
}
);
LordDave's answer revealed one problem in your code. As you commented in his answer, all_posts field of settingType was not working.
If you used mongoose library in your DB code, I see a problem with your query:
Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec({}, function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
Based on documentation of exec, change your query to
return Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec(function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
As exec returns a promise, you can even do
return Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec();
Finally I got it working by creating a new type 'postList' and defined it as below
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
}else if (type==='postList') {
return getpostList(id);
} else{
return null;
}
},
(obj) => {
if (obj instanceof User) {
return userType;
}else if (obj instanceof postList) {
return postListType;
}else{
return null;
}
}
);
In database.js
class postList {}
postList.id = "Post_id";
export {postList}
export function getpostList(id) {
return new postList
}
and under root fields as below
var postListType = new GraphQLObjectType({
name: 'postList',
description: 'List of posts',
  fields: () => ({
  id: globalIdField('postList'),
  posts: {
  type: postConnection,
  description: 'List of posts',
  args: {
...connectionArgs,
query: {type: GraphQLString}
},
  resolve: (_, args) => connectionFromPromisedArray(getAllPosts(_,args), args),
  },
}),
interfaces: [nodeInterface],
});
var Root = new GraphQLObjectType({
name: 'Root',
fields: () => ({
node: nodeField,
postList: {
type: postListType,
resolve:(rootValue)=> {
return getpostList()
}
},
})
});
I ran into this issue when I was using an InterfaceType and checked for the InterfaceType before the specialized ObjectType in the if-elseif-else of my TypeResolver

Resources