How to implement GraphQL Scalar for MongoDB sort property found in collection.find() function of MongoDB's? - graphql-js

I'm trying to create an argument type using type-graphql to accept arguments through the GraphQL query. The sort I'm talking about is a property of the options found on MongoDB's native NodeJS driver documentation.
#ArgsType()
export class FindAllArgs implements FindOneOptions {
#Field(type => Int, { defaultValue: 0, description: 'Sets the limit of documents returned in the query.' })
#Min(0)
limit?: number;
// This is where the custom sort would go about.
#Field(type => SortScalar)
sort?: sortScalar;
#Field(type => Int, { defaultValue: 0, description: 'Set to skip N documents ahead in your query (useful for pagination).' })
#Min(0)
skip?: number;
}
As you can see, simple types are fine, but when it comes to something like the sort object, I'm not sure how to go on about it.
NestJS implements a date scalar like so:
import { Scalar, CustomScalar } from '#nestjs/graphql';
import { Kind, ValueNode } from 'graphql';
#Scalar('Date', type => Date)
export class DateScalar implements CustomScalar<number, Date> {
description = 'Date custom scalar type';
parseValue(value: number): Date {
return new Date(value); // value from the client
}
serialize(value: Date): number {
return value.getTime(); // value sent to the client
}
parseLiteral(ast: ValueNode): Date {
if (ast.kind === Kind.INT) {
return new Date(ast.value);
}
return null;
}
}
Even in the example, it uses a returnTypeFunction #Scalar('Date', type => Date). What am I supposed to replace Date with? What do I put in for parseValue, serialize, and parseLiteral?

You can just use #InputType to nest objects in args - you don't need scalars for that, they are designed only for serializable things like timestamp -> number, mongo id -> ObjectId instance, etc.

Related

how to use dynamic field type in nestjs

I have a DTO structured like below, the field children I want to it to be number[] when client save the data.My database is MongoDB and I use Mongoose to manage my model.
//create-menu.dto.ts
export class CreateMenuDto {
...
#IsArray()
childrens: number[];
}
now I want it's type to be menu[] when the server response the client's query.So I define my Menu schema like below:
//menu.schema.ts
#Schema({ versionKey: false })
export class Menu {
...
#Prop({ type: Number, ref: 'Menu' })
childrens: Menu[];
}
menu.controller.ts:
#Put(':id')
update(#Param('id') id: string, #Body() updateMenuDto: UpdateMenuDto) {
return this.menuService.update(+id, updateMenuDto);
}
menu.service.ts:
update(id: number, updateMenuDto: UpdateMenuDto) {
const menu = this.menuModel.findOneAndUpdate({ _id: id }, updateMenuDto);
return menu;
}
when I use REST Client to test:
Put http://127.0.0.1:3000/api/menu/1
Content-Type: application/json
{
"childrens":[2]
}
the server throws 500 exception, so how can i implement dynamic field type? any idea is welcome, thanks!
The Menu Class has property childrens of type Menu[] while 'UpdateMenuDto' class has property childrens of type number[] so here :
const menu = this.menuModel.findOneAndUpdate({ _id: id }, updateMenuDto);
You got the error, because Nestjs cannot update the found menu document with the recieved updateMenuDto, I don't see any relation between an array of Menu and an array of number so I don't think the field childrens should exist in the UpdateMenuDto class neither in the PUT request body, but maybe you need these numbers to create a Menu array, in this case you can create a new object from updateMenuDto :
update(id: number, updateMenuDto: UpdateMenuDto) {
var newMenuDto: any = {...updateMenuDto};
newMenuDto.childrens = // use updateMenuDto.childrens to create newMenuDto.childrens of type Menu[]
const menu = this.menuModel.findOneAndUpdate({ _id: id }, newMenuDto);
return menu;
}

Meteor Mongo Collection is not using typescript interface

I want to describe Mongo.Collection schema via typescript interface to have a strict check for type on fetch, forEach, etc.
interface IChat {
_id: string;
name?: string
};
const Chats = new Mongo.Collection<IChat>('chat');
// (method) Mongo.Cursor<IChat>.forEach(callback: <T>(doc: T, index: number, cursor: Mongo.Cursor<T>) => void, thisArg?: any): void
Chats.find().forEach(c => {
console.log(c._id); // Property '_id' does not exist on type 'T'.
// Why "type T" if it should be the type IChat???
});
But facing next error: Property '_id' does not exist on type 'T'.
What I'm doing wrong?
Link to typescript playground
Error
Collection.find().forEach() definition
In this case you're missing the .fetch() call after .find().
It may be that mathamagically Meteor+Mongo can handle the syntax as you have it now but the easy way to make Typescript happy here is adjusting it to be the following:
interface IChat {
_id: string;
name?: string
};
const Chats = new Mongo.Collection<IChat>('chat');
Chats.find().fetch().forEach(c => {
console.log(c._id);
});
link to the solution in Typescript Playground

"QueryFailedError: operator does not exist", How to use PostgreSql array contains operator with typeorm?

I am using TypeOrm with TypeScript and I have a products table like this.
export enum ColorTone {
Cambridge = 'GREY_TONE',
Nicolock = 'RED_TONE',
Keystone = 'BLUE_TONE',
TechoBloc = 'YELLOW_TONE',
}
#Entity('product')
export class Product {
#Column()
name: string;
#Column({
type: 'enum',
enum: Brand,
array: true,
default: [],
})
colorTones: ColorTone[]; // available colors for a product
}
I want to find all products containing a specific color tone available.
I tried this using TypeOrm query.
...
constructor(#InjectRepository(Product) private productsRepository: Repository<Product>) {}
...
findProducts(colorTone: ColorTone): Promise<Product[]> {
return this.productsRepository.createQueryBuilder('product')
.where('product.colorTones #> ARRAY[:colorTone]', { colorTone });
}
But I get this console error.
QueryFailedError: operator does not exist: product_colortones_enum[] #> text[]
...
Not sure if there is a way to do the typecasting.
I tried to find the solution all day and any relevant link or reference link will be appreciated.
You need to cast to the correct type.
.where('product.colorTones #> ARRAY[:colorTone::product_colortones_enum]', { colorTone });
If typeorm gets confused by the colon salad, move the cast to the array
.where('product.colorTones #> ARRAY[:colorTone]::product_colortones_enum[]', { colorTone });

must have a selection of subfields. Did you mean \"createEvent { ... }\"?", [graphql] [duplicate]

Hi I am trying to learn GraphQL language. I have below snippet of code.
// Welcome to Launchpad!
// Log in to edit and save pads, run queries in GraphiQL on the right.
// Click "Download" above to get a zip with a standalone Node.js server.
// See docs and examples at https://github.com/apollographql/awesome-launchpad
// graphql-tools combines a schema string with resolvers.
import { makeExecutableSchema } from 'graphql-tools';
// Construct a schema, using GraphQL schema language
const typeDefs = `
type User {
name: String!
age: Int!
}
type Query {
me: User
}
`;
const user = { name: 'Williams', age: 26};
// Provide resolver functions for your schema fields
const resolvers = {
Query: {
me: (root, args, context) => {
return user;
},
},
};
// Required: Export the GraphQL.js schema object as "schema"
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
// Optional: Export a function to get context from the request. It accepts two
// parameters - headers (lowercased http headers) and secrets (secrets defined
// in secrets section). It must return an object (or a promise resolving to it).
export function context(headers, secrets) {
return {
headers,
secrets,
};
};
// Optional: Export a root value to be passed during execution
// export const rootValue = {};
// Optional: Export a root function, that returns root to be passed
// during execution, accepting headers and secrets. It can return a
// promise. rootFunction takes precedence over rootValue.
// export function rootFunction(headers, secrets) {
// return {
// headers,
// secrets,
// };
// };
Request:
{
me
}
Response:
{
"errors": [
{
"message": "Field \"me\" of type \"User\" must have a selection of subfields. Did you mean \"me { ... }\"?",
"locations": [
{
"line": 4,
"column": 3
}
]
}
]
}
Does anyone know what I am doing wrong ? How to fix it ?
From the docs:
A GraphQL object type has a name and fields, but at some point those
fields have to resolve to some concrete data. That's where the scalar
types come in: they represent the leaves of the query.
GraphQL requires that you construct your queries in a way that only returns concrete data. Each field has to ultimately resolve to one or more scalars (or enums). That means you cannot just request a field that resolves to a type without also indicating which fields of that type you want to get back.
That's what the error message you received is telling you -- you requested a User type, but you didn't tell GraphQL at least one field to get back from that type.
To fix it, just change your request to include name like this:
{
me {
name
}
}
... or age. Or both. You cannot, however, request a specific type and expect GraphQL to provide all the fields for it -- you will always have to provide a selection (one or more) of fields for that type.

Mongoose, does this model already exist in the collection

I'm using Mongoose on a Node.js server to save data into MongoDB. What I want to do is to check and see if a model object exists in the collection already.
For example heres my model:
var ApiRequest = new Schema({
route: String,
priority: String,
maxResponseAge: String,
status: String,
request: Schema.Types.Mixed,
timestamp: { type: Date, default: Date.now }
});
And here's what I would like to do:
var Request = mongoose.model('api-request', ApiRequest);
function newRequest(req, type) {
return new Request({
'route' : req.route.path,
'priority' : req.body.priority,
'maxResponseAge' : req.body.maxResponseAge,
'request' : getRequestByType(req, type)
});
}
function main(req, type, callback) {
var tempReq = newRequest(req, type);
Request.findOne(tempReq, '', function (err, foundRequest) {
// Bla bla whatever
callback(err, foundRequest);
});
}
The big issues I'm finding are that the tempReq which is a model has an _id variable and a timestamp that is going to be different from what is saved in the database. So I would like to ignore those fields and compare by everything else.
As a note my actual models have more variables than this hence the reason I don't want to use .find({ param : val, ....})..... and instead would like to use the existing model for comparison.
Any ideas? Thanks!
You need to use plain JS objects instead of Mongoose model instances as query objects (the first parameter to find).
So either:
Change newRequest to return a plain object and later pass that into a new Request() call if you need to add it to the database.
OR
In your main function turn tempReq into a query object like this:
var query = tempReq.toObject();
delete query._id;
Request.findOne(query, ...