hello every body i have entity that contain special property used for translation, when i try to get this special property usin typeorm i have this error: (node:3712) UnhandledPromiseRejectionWarning: QueryFailedError: syntax error at or near "[">>>
the entity is:
import {
Entity,
PrimaryGeneratedColumn,
Column,
BaseEntity,
OneToOne,
JoinColumn,
} from "typeorm";
import {TextData} from "./TextData"
#Entity()
export class ContactUsDataNature extends BaseEntity {
#PrimaryGeneratedColumn()
id:number
#OneToOne((type) => TextData, (text) => text.id, { cascade: true })
#JoinColumn()
dataNature:TextData
}
the text data is:
import { Entity, Column, PrimaryGeneratedColumn, BaseEntity } from "typeorm";
#Entity()
export class TextData extends BaseEntity{
#PrimaryGeneratedColumn()
id: number;
#Column({ type: "text", nullable: true })
ar: string;
#Column({ type: "text", nullable: true })
en: string;
}
i usually reach it like
const contact=new ContactUSDataNature()
console.log(contact.dataNature[en])
i tried the way to search but it doesn't work :
export const getContactDataNaturebyData = async (dataNature: string,language="en") =>{
const container=await ContactUsDataNature.find({
where: (qb) => {
if (true) {
qb.andWhere("ContactUsDataNature__dataNature.:lan = :parentId", {
lan:"en",
parentId: 1,
});
}
},
order: { id: "DESC" },
skip: 10,
take: 0,
});
console.log(container)
return container
}
I need to get search in database for ContactUsDataNature if i get the final string value
You cannot parameterize the column names in a query.
You can build the where by concatenating the column name:
qb.andWhere("ContactUsDataNature__dataNature." + lan + " = :parentId", { parentId: 1 });
When you test this, I recommend that you turn on TypeOrm full logging so you can see the actual generated SQL and you be able to quickly solve any problems. See TypeOrm logging.
Related
I'm working on the following code:
import { prop, modelOptions, DocumentType, getModelForClass } from "#typegoose/typegoose";
import dbConnect from "src/lib/dbConnect";
import mongoose from 'mongoose';
#modelOptions({ schemaOptions: { timestamps: true } })
class Kitten {
#prop()
public name?: string;
#prop({ type: String, required: true, default: [] })
public events!: mongoose.Types.Array<[string, Date]>;
// also doesn't work:
// public events!: [string, Date][];
// instance method:
public async addEvent(this: DocumentType<Kitten>, _eventName: string) {
const tuple : [string, Date] = [_eventName, new Date()];
this.events.push(tuple);
await dbConnect();
await this.save();
}
}
export const KittenModel = getModelForClass(Kitten);
and when I call addEvent I get the following error when executing this.events.push(tuple); :
error - CastError: Cast to string failed for value "[
'0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001',
2023-01-11T05:02:21.462Z
]" (type Array)
It seems that the method push is trying to convert the tuple into a string for some reason...
My environment is:
"#typegoose/typegoose": "^10.0.0",
"mongoose": "^6.8.3",
nodejs --version :v16.18.1
Mongoose (and typegoose by extension) dont support tuple arrays unless the field is Mixed (which is not recommended to be used).
In your case a Map would probably what you search for, if the event names are unique, if they are not unique you may wanna use a nested class to keep the fields explicitly, like:
class EventDate {
#prop()
public eventName: string;
#prop()
public date: Date;
}
class Kitten {
#prop({ type: EventDate, required: true, default: [] })
public events!: mongoose.Types.Array<EventDate>;
public async addEvent(this: DocumentType<Kitten>, _eventName: string) {
this.events.push({ eventName: _eventName, date: new Date() });
await dbConnect();
await this.save();
}
}
The issue is that the decorator #prop was not being well defined.
It should be:
#prop({ type: mongoose.Types.Array<[string, Date]>, required: true, default: [] })
public events!: mongoose.Types.Array<[string, Date]>;
The complete code is:
import { prop, modelOptions, DocumentType, getModelForClass } from "#typegoose/typegoose";
import dbConnect from "src/lib/dbConnect";
import mongoose from 'mongoose';
#modelOptions({ schemaOptions: { timestamps: true } })
class Kitten {
#prop()
public name?: string;
#prop({ type: mongoose.Types.Array<[string, Date]>, required: true, default: [] })
public events!: mongoose.Types.Array<[string, Date]>;
// instance method:
public async addEvent(this: DocumentType<Kitten>, _eventName: string) {
const tuple : [string, Date] = [_eventName, new Date()];
this.events.push(tuple);
await dbConnect();
await this.save();
}
}
export const KittenModel = getModelForClass(Kitten);
I am trying to implement server-side column filtering in TanStack Table (React-Table V8). The problem is that I don't know how to convert the ColumnFiltersState object to the right format for the backend. My backend uses nestjs-paginate which uses the following operators: $eq, $not, $null, $in, $gt, $gte, $lt, $lte, $btw, $ilike for filtering based on the query. The ColumnFiltersState contains a value field, which is of type unknown, and the value it contains will depend on the filter component being used, I plan to use select, date inputs, range inputs for filtering. Also, some fields can be filtered as 'contains', 'equals' etc.
Additionally, another problem is that I need to support both client-side and server-side column filtering depending on the manualFiltering prop passed to my custom table component.
Here is a piece of code from my UsersList component which use my custom table:
...
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[],
);
const { data, isFetching, isLoading, isSuccess, isError } = useGetUsersQuery({
search: globalFilter,
page: pageIndex + 1,
limit: pageSize,
sortBy: sorting.map((s) => `${s.id}:${s.desc ? 'DESC' : 'ASC'}`).join(','),
columnFilters: columnFilters.map((columnFilter) => { // Here is the problem
...
return {
column: columnFilter.id,
filter: {
operator: ...,
value: ...,
},
};
}),
});
...
So then I can send it to the server in the needed format:
// user.api.ts
...
query: ({ search, page, limit, sortBy, columnFilters }) => {
const params: any = {
page,
limit,
search,
sortBy,
};
// example query URL with filters:
// http://localhost:3000/cats?filter.age=$gte:3
if (columnFilters) {
columnFilters.forEach(({ column, filter: { operator, value } }) => {
params[`filter.${column}`] = `${operator}:${value}`;
});
}
return {
url: 'users/',
params,
};
},
...
Here are the nestjs-paginate types:
// Available filter operators:
export enum FilterOperator {
EQ = '$eq',
GT = '$gt',
GTE = '$gte',
IN = '$in',
NULL = '$null',
LT = '$lt',
LTE = '$lte',
BTW = '$btw',
NOT = '$not',
ILIKE = '$ilike',
}
// Return data type from the backend
export interface Paginated<T> {
data: T[];
meta: {
itemsPerPage: number;
totalItems: number;
currentPage: number;
totalPages: number;
sortBy: SortBy<T>;
searchBy: Column<T>[];
search: string;
filter?: {
[column: string]: string | string[];
};
};
links: {
first?: string;
previous?: string;
current: string;
next?: string;
last?: string;
};
}
A possible solution is to add a custom prop to ColumnMeta by which it will be possible to determine which filtering operator to use, but then I donβt know how to do it correctly or maybe I could determine it using the built-in solutions. I would appreciate any help and ideas π.
RedwoodJS automatically maps GraphQL queries resolvers to api/src/services. How do I create a field resolver for a given GraphQL type?
Suppose I have this schema:
type Person {
name: string!
birthDate: DateTime!
age: Int!
}
But only name and birthDate are stored in the database.
Using graphql-tools I would write my resolvers like this:
const resolvers = {
Query: { ... },
Mutation: { ... },
Person: {
age(person) {
return new Date().getFullYear() - person.birthDate.getFullYear();
},
},
};
PS: I know the age formula is wrong.
PS2: I'm using age here for the sake of simplicity, imagine this is expensive to compute or get from database.
It's almost identical to the way you do it with graphql-tools.
You export an object with the same name as your type in your service:
// services/person.js
export const Person = {
age: (_args, { root }) {
return new Date().getFullYear() - root.birthDate.getFullYear();
},
}
As an aside, you could also export a resolvers in the person.sdl.js file (But services take precendence):
// graphql/person.sdl.js
export const schema = gql`/* ... */`
export const resolvers = {
Query: {},
Mutation: {},
Person: {},
}
Edit: I misunderstood the question, this answer just covers creating query + mutation resolvers, not a resolver for a computed field.
To create a field resolver, you'll need to decide whether you're creating a resolver for a query, or a handler for a mutation.
We can use the following schema as an example:
export const schema = gql`
type Person {
id: String!
name: String!
age: Int!
}
type PersonInput {
name: String
age: Int
}
type Mutation {
createPerson(input: PersonInput!): Person
}
type Query {
people: [Person]
person(id: String!): Person
}
`
If the above schema is stored in a file called persons.sdl.js, in the api/src/graphql directory, you can implement the queries and mutations in a file called persons.js in the api/src/services/persons directory.
// implements Mutation.createPerson(...)
export const createPerson({ input }) => {
return db.person.create({
data: input
})
}
// implements Query.people
export const people = () => {
return db.person.findMany()
}
// implements Query.person(...)
export const person = ({ id }) => {
return db.person.findOne({
where: { id }
})
}
I am using class-validator package with NestJS and I am looking to validate an array of objects that need to have exactly 2 objects with the same layout:
So far I have:
import { IsString, IsNumber } from 'class-validator';
export class AuthParam {
#IsNumber()
id: number;
#IsString()
type: string;
#IsString()
value: string;
}
and
import { IsArray, ValidateNested } from 'class-validator';
import { AuthParam } from './authParam.model';
export class SignIn {
#IsArray()
#ValidateNested({ each: true })
authParameters: AuthParam[];
}
per #kamilg response (I am able to enforce exacly 2 elements):
import { IsArray, ValidateNested, ArrayMinSize, ArrayMaxSize } from 'class-validator';
import { AuthParam } from './authParam.model';
export class SignInModel {
#IsArray()
#ValidateNested({ each: true })
#ArrayMinSize(2)
#ArrayMaxSize(2)
authParameters: AuthParam[];
}
I still can pass an empty array or an array with some other objects not related to AuthParam.
How I should modify it get validation?
Also how I can enforce mandatory 2 elements in the array? MinLength(2) seems to be regarding string... (resolved)
Add #Type(() => AuthParam) to your array and it should be working. Type decorator is required for nested objects(arrays). Your code becomes
import { IsArray, ValidateNested, ArrayMinSize, ArrayMaxSize } from 'class-validator';
import { AuthParam } from './authParam.model';
import { Type } from 'class-transformer';
export class SignInModel {
#IsArray()
#ValidateNested({ each: true })
#ArrayMinSize(2)
#ArrayMaxSize(2)
#Type(() => AuthParam)
authParameters: AuthParam[];
}
Be careful if you are using any exception filter to modify the error reponse. Make sure you understand the structure of the class-validator errors.
I Know I Am Late But Facing Some Issue With Type, Then Try Another Way To Implement This:
export class AuthParam {
#IsNumber()
id: number;
#IsString()
type: string;
#IsString()
value: string;
}
Validation function
#ValidatorConstraint()
export class IsAuthArray implements ValidatorConstraintInterface {
public async validate(authData: AuthParam[], args: ValidationArguments) {
return Array.isArray(authData) && authData.reduce((a, b) => a && (typeof b.id === "number") && typeof b.type === "string" && typeof b.field === "string", true);
}
}
export class SignInModel {
#IsNotEmpty()
#IsArray()
#ArrayMinSize(2)
#ArrayMaxSize(2)
#Validate(IsAuthArray, {
message: "Enter valid value .",
})
authParameters: AuthParam[];
}
Maybe It Will Help Someone π
You can use the following:
validator.arrayNotEmpty(array); // Checks if given array is not empty.
validator.arrayMinSize(array, min); // Checks if array's length is at least `min` number.
(https://github.com/typestack/class-validator#manual-validation)
You may want to consider writing custom validator which would better reflect the business requirement you have.
const param1: AuthParam = Object.assign(new AuthParam(), {
id: 1,
type: 'grant',
value: 'password'
})
const param2: AuthParam = Object.assign(new AuthParam(), {
id: 1,
type: 4,
value: 'password'
})
const signInTest = new SignInModel()
signInTest.authParameters = [param1, param2]
validate(signInTest).then(e => {
console.log(e[0].children[0].children[0])
})
This works correctly, this is:
ValidationError {
target: AuthParam { id: 1, type: 4, value: 'password' },
value: 4,
property: 'type',
children: [],
constraints: { isString: 'type must be a string' } }
so I may only assume that object which is being validated, is not an instance of AuthParam
const param2: AuthParam = {
id: 1,
type: 4,
value: 'password'
} as any
as expected, there aren't any decorators on this object (which may be true for Nest.js controllers and nested objects from body/req) - so validation is ignored.
Please check this (tl;dr - #Type decorator form class-transformer)
I have this simple query which works fine in my Graphql but I cannot pass data using relay to components and I don't know why :(
{
todolist { // todolist returns array of objects of todo
id
text
done
}
}
this is my code in an attempt to pass data in components using relay:
class TodoList extends React.Component {
render() {
return <ul>
{this.props.todos.todolist.map((todo) => {
<Todo todo={todo} />
})}
</ul>;
}
}
export default Relay.createContainer(TodoList, {
fragments: {
todos: () => Relay.QL`
fragment on Query {
todolist {
id
text
done
}
}
`,
},
});
And lastly my schema
const Todo = new GraphQLObjectType({
name: 'Todo',
description: 'This contains list of todos which belong to its\' (Persons)users',
fields: () => {
return {
id: {
type: GraphQLInt,
resolve: (todo) => {
return todo.id;
}
},
text: {
type: GraphQLString,
resolve: (todo) => {
return todo.text;
}
},
done: {
type: GraphQLBoolean,
resolve: (todo) => {
return todo.done;
}
},
}
}
});
const Query = new GraphQLObjectType({
name: 'Query',
description: 'This is the root query',
fields: () => {
return {
todolist: {
type: new GraphQLList(Todo),
resolve: (root, args) => {
return Conn.models.todo.findAll({ where: args})
}
}
}
}
});
This code looks simple and I cannot see why this won't work and I have this error Uncaught TypeError: Cannot read property 'todolist' of undefined, but I configure todolist and I can query in my graphql, you can see the structure of the query is same, I don't know why this is not working?
todolist should be a connection type on Query. Also, your ids should be Relay global IDs. You will not have access to your objects' raw native id fields in Relay.
import {
connectionArgs,
connectionDefinitions,
globalIdField,
} from 'graphql-relay';
// I'm renaming Todo to TodoType
const TodoType = new GraphQLObjectType({
...,
fields: {
id: uidGlobalIdField('Todo'),
...
},
});
const {
connectionType: TodoConnection,
} = connectionDefinitions({ name: 'Todo', nodeType: TodoType });
// Also renaming Query to QueryType
const QueryType = new GraphQLObjectType({
...,
fields: {
id: globalIdField('Query', $queryId), // hard-code queryId if you only have one Query concept (Facebook thinks of this top level field as being a user, so the $queryId would be the user id in their world)
todos: { // Better than todoList; generally if it's plural in Relay it's assumed to be a connection or list
type: TodoConnection,
args: connectionArgs,
},
},
});
// Now, to be able to query off of QueryType
const viewerDefaultField = {
query: { // Normally this is called `viewer`, but `query` is ok (I think)
query: QueryType,
resolve: () => ({}),
description: 'The entry point into the graph',
}
};
export { viewerDefaultField };
The above is not fully complete (you'll likely also need to setup a node interface on one or more of your types, which will require node definitions), but it should answer your basic question and get you started.
It's a huge, huge pain to learn, but once you struggle through it it starts to make sense and you'll begin to love it over RESTful calls.