Что не так с запросом для Many-To-Many связи?
Хочу реализовать связь Many-To-Many (использую type-graphql, graphql, typeorm), нужно создать игру, при этом поместить её в массив игр у игроков:
@ObjectType()
@Entity()
export class User extends BaseEntity {
...
@ManyToMany(() => Game, game => game.players, { cascade: true }) @JoinTable()
games: Game[];
}
@ObjectType()
@Entity()
export class Game extends BaseEntity {
// От этих p1id и p2id хочу отказаться, чтобы можно было ссылаться на связь с User
@Field(() => Int, {nullable: false})
@Column()
p1id: number;
@Field(() => Int, {nullable: true, defaultValue: 0})
@Column({nullable: true, default: 0})
p2id: number;
...
@ManyToMany(() => User, user => user.games)
players: User[]
}
Для этого есть следующая функция в Resolver:
@Mutation(() => Game)
async createGame(@Ctx() { req }: MyContext) {
const initialWord = await generateRandomWord();
const currUser = await User.findOne({ id: req.session.userId });
if (currUser) {
let userGames = [];
if (currUser.games !== undefined) {
currUser.games.map((i) => userGames.push(i));
}
const game = await getConnection()
.createQueryBuilder()
.insert()
.into(Game)
.values({
status: false,
initialWord,
p1id: req.session.userId,
})
.returning("*")
.execute();
const res = game.raw[0] as Game;
userGames.push(res);
await getConnection()
.createQueryBuilder()
.update(User)
.set({games: userGames as Game[]})
.where("id = :id", {id: req.session.userId})
.execute();
return game.raw[0] as Game;
}
throw new UnauthorizedError();
}
Не понимаю, почему при запросе выводит следующее:
query failed: UPDATE "user" SET "userId" = $1, "updatedAt" = CURRENT_TIMESTAMP WHERE "id" = $2 -- PARAMETERS: [null,1]
[0] error: error: столбец "userId" в таблице "user" не существует
Откуда появляется этот userId в запросе? Почему не обновляется то, что нужно?
Ответы (1 шт):
Автор решения: nörbörnën
→ Ссылка
Не обновляется тут, скорее всего, из-за использования .set. Нужно было использовать .add.
В .d.ts написано:
/**
* Adds (binds) given value to entity relation.
* Value can be entity, entity id or entity id map (if entity has composite ids).
* Value also can be array of entities, array of entity ids or array of entity id maps (if entity has composite ids).
* Works only for many-to-many and one-to-many relations.
* For many-to-one and one-to-one use #set method instead.
*/
add(value: any | any[]): Promise<void>;
Вот, рабочий код:
import 'reflect-metadata';
import {
createConnection,
Entity,
getRepository,
PrimaryGeneratedColumn,
BaseEntity,
ManyToMany,
JoinTable
} from 'typeorm';
@Entity()
class User1 extends BaseEntity {
@PrimaryGeneratedColumn('increment')
id: number;
@ManyToMany(() => Game1, (game) => game.players, { cascade: true })
@JoinTable()
games?: Game1[];
}
@Entity()
class Game1 extends BaseEntity {
@PrimaryGeneratedColumn('increment')
id: number;
@ManyToMany(() => User1, (user) => user.games)
players?: User1[];
}
(async () => {
const connection = await createConnection({
type: 'postgres',
username: process.env.POSTGRES_LOCAL_DBUSER,
database: 'test',
synchronize: true,
logging: true,
entities: [
User1, Game1
]
});
try {
const game1 = await getRepository(Game1).save({});
const user = await getRepository(User1).save({ games: [game1] });
const game2 = await getRepository(Game1).save({});
user.games = [ ...user.games, game2 ];
await getRepository(User1).save(user);
const game3 = await getRepository(Game1).save({});
await getRepository(User1)
.createQueryBuilder()
.relation(User1, 'games')
.of(user)
.add([ game3 ]);
const item = await getRepository(User1).findOne(user.id, { relations: ['games']});
console.log(item);
} catch (err) {
console.error(err);
}
await connection.close();
})();
User1 {
id: 15,
games: [ Game1 { id: 1 }, Game1 { id: 2 }, Game1 { id: 3 } ]
}
И, кстати, в твоём коде не нужно было использовать QueryBuilder, вполне хватило бы .find-ов и .save. Я показал в примере их использование.
node.js v16.2.0, typescript v4.2.3, typeorm v0.2.32