I have a list of products, and a list of users and I want to be able to relate users to what products they have but also what quantity they have of each product.
I've setup my user schema like this:
model users {
id String #id #default(uuid())
email String #unique
password String
test_products test_products[]
users_to_test_products users_to_test_products[]
}
I've implicitly stated the relation so that I can add in a quantity field
model users_to_test_products {
users users #relation(fields: [user_id], references: [id])
user_id String
test_products test_products #relation(fields: [product_id], references: [product_id])
product_id String
quantity Int
##id([user_id])
}
and I've setup my product list like this:
model test_products {
product_id String #id
name String?
users users[]
users_to_test_products users_to_test_products []
}
I assumed I could make an update call, connecting the user to the test product and passing in the quantity but it seems that I can't and my approach is completely wrong.
await prisma.users.update({
data: {
test_products: {
connect: UserProducts.map((product) => {
return {
product_id: product.product_id,
quantity: product.quantity
}
}),
},
},
where: {
id: userId
},
})
note: UserProducts is just an object array eg [{ product_id: "tTer3434", quantity: 8 }]
Can anyone point me in the right direction on the approach I need to take please?
Related
I`m using Prisma with NestJS and PostgreSQL
In my schema.prisma i have this
model User {
userId Int #id #default(autoincrement())
firstName String? #db.VarChar(25)
lastName String? #db.VarChar(25)
login String #unique #db.VarChar(60)
email String #unique
password String
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
role Role? #default(ADMIN)
createdUsers User[] #relation("createdBy")
createdBy User? #relation("createdBy", fields: [creatorId], references: [userId])
creatorId Int?
}
enum Role {
ADMIN
MANAGER
WAREHOUSE
USER
}
So when a make create request like this
{
login: "A",
email: "A",
password: "A"
}
it saves in DB - that`s ok. By after the same request i get this error "Unique constraint failed on the (not available)". So shouldn't there be a not unique column name instead of (not available) or that is ok case? Where am i wrong?
I was trying drop table Users and make different unique combs, don`t know what else i can do...
UPD:
async create(createUserDto: CreateUserDto) {
return this.prisma.user.create({
data: {
...createUserDto
}
})
}
export class CreateUserDto {
#IsNotEmpty()
login: string;
#IsNotEmpty()
#IsEmail()
email: string;
#IsNotEmpty()
#MinLength(6)
password: string;
}
UPD 2.0
Finally, moved to TypeORM, it is bug like #num8er mentioned
https://github.com/prisma/prisma/issues/10829
Ty for all the replies
I use Postgres with Prisma and I want to ask if there is a way to use upsert or create functions and define relations without the connect field.
I have these models:
model User {
id Int #id #default(autoincrement())
posts Post[]
}
model Post {
id Int #id #default(autoincrement())
author User #relation(fields: [authorId], references: [id])
authorId Int // relation scalar field (used in the `#relation` attribute above)
}
and I want to do something like this:
prisma.Post.upsert({where: { authorId: 12 }, update: {}, create: { authorId: 12 })
assuming User with ID 12 does exists in the DB I want it to create a new record successfully but it fails because it wants it in this syntax:
prisma.Post.upsert({where: { authorId: 12 }, update: {}, create: { author: { connect: { id: 12}}}})
is this achievable somehow?
Thanks.
I'm trying to model a m:n relation which references entries which might not exist yet on one side.
I tried with the following schema (doesn't work):
model Foo {
id Int #id
}
model Bar {
id Int #id
}
model FooBar {
fooId Int
barId Int
foo Foo #relation(fields: [fooId], references: [id])
bar Bar? #relation(fields: [barId], references: [id])
##id([fooId, barId])
}
When trying to add a relation between fooId: 1 (existing) and barId: 1 (not existing (yet?)), I get the following error:
Type: undefined
Message:
Invalid prisma.fooBar.create() invocation:
An operation failed because it depends on one or more records that were required but not found. No 'Bar' record(s) (needed to inline the relation on 'fooBar' record(s)) was found for a nested connect on one-to-many relation 'fooBarToBar'.
Code: P2025
Is there a way to insert the relation anyway, and when querying Foo with { include: { fooBar: { include: { bar: true }}}} to receive an object like this when Bar doesn't exist:
{
id: 1,
fooBar: {
fooId: 1,
barId: 1,
bar: null
}
}
I tried with the above schema, but it doesn't work. I guess changing the relation to this would work:
model FooBar {
fooId Int
barId Int
barIdExisting Int?
foo Foo #relation(fields: [fooId], references: [id])
bar Bar? #relation(fields: [barIdExisting], references: [id])
}
And then only set barIdExisting to barId if bar exists, but this would mean to have redundant data.
My schema looks like this:
model Transaction {
id BigInt #id #default(autoincrement())
description String? #db.VarChar(256)
category Category? #relation(fields: [categoryId], references: [id])
createdById Int #map("fk_created_by")
createdBy UserAccount #relation("category_fk_created_byTouser_account", fields: [createdById], references: [id]
}
model Category {
id Int #id #default(autoincrement())
name String #db.VarChar(40)
categoryFilters CategoryFilter[]
}
model CategoryFilter {
id BigInt #id #default(autoincrement())
words String #db.VarChar(255)
categoryId Int? #map("fk_category_id")
}
My question is why this works:
await prisma.workspaceTransaction.create({
data: {
description: 'any',
createdBy: {
connect: {
id: 1
}
},
category: {
create: {
name: 'Este é um teste',
createdById: 1,
categoryFilters: {
createMany: {
data: [
{
words: 'Novo teste'
}
]
}
}
}
}
}
})
And this not?
await prisma.workspaceTransaction.create({
data: {
description: 'any',
createdById: 1,
category: {
create: {
name: 'Este é um teste',
createdById: 1,
categoryFilters: {
createMany: {
data: [
{
words: 'Novo teste'
}
]
}
}
}
}
}
})
The only difference between those two examples is in createdBy command. And i can create Transaction without nested objects with createdById argument. Any one know why this works this way? Or there is something i am missing?
The error given is:
Unknown arg createdById in data.createdById for type TransactionCreateInput
In the second example, you are directly writing a foreign key (createdById) which is considered unsafe, what if there is no UserAccount corresponding to createdById.
While using connect in the first example it would throw an error if Prisma cannot find an UserAccount corresponding to id 1.
First example is the preferred approach.
I'm new to Mongodb coming from relational databases and I'd also like to point out I'm using SpringBoot with JPA. If I were to build an automotive classified site where I would have thousands of Users and 100s of thousands of listings, how would I go about setting up the schema? I've read some articles that say normalizing nosql data is bad practices.
Anyhow lets say we have the following structure.
User
id
name
email
Cars
id
make
model
year
I would need to be able to list many cars with the User and what i've seen in my examples is it creates a nested array of cars within User. This would work great for user accounts where I'd like to provide the user with all their cars
Where I get a bit confused is with the cars. The cars need to be able to be searched very quickly and would not need the user info right away. In a sql db I would typically do a search against the cars (year, make, model) and grab the user later on if I needed it.
In mongodb, do you create a User document that contains a nested car array? or do you somehow create 2 documents that are both automatically maintained and search against the car document for performance reasons?
Sample code
#Document(collection = "person")
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
// #DBRef(lazy = true)
private List<Listing> listings;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
#Document(collection = "listing")
public class Listing {
#Id
public String id;
public String year;
public String make;
public String model;
public String trim;
public Listing(String year, String make, String model, String trim) {
this.year = year;
this.make = make;
this.model = model;
this.trim = trim;
}
}
#Override
public void run(String... args) throws Exception {
repository.deleteAll();
List<Listing> listings = new ArrayList<>();
Listing listing = new Listing("2008", "Ford", "Focus", "SE");
//listingRepository.save(listing);
listings.add(listing);
Person person = new Person("Alice", "Smith");
person.setListings(listings);
// save a couple of customers
repository.save(person);
person = new Person("Bob", "Smith");
listings = new ArrayList<>();
listings.add(new Listing("2018", "Chrysler", "300", "S"));
person.setListings(listings);
repository.save(person);
// fetch all customers
System.out.println("Customers found with findAll():");
System.out.println("-------------------------------");
for (Person _person : repository.findAll()) {
System.out.println(_person);
}
System.out.println();
// fetch an individual customer
System.out.println("Person found with findByFirstName('Alice'):");
System.out.println("--------------------------------");
System.out.println(repository.findByFirstName("Alice"));
System.out.println("Persons found with findByLastName('Smith'):");
System.out.println("--------------------------------");
for (Person _person : repository.findByLastName("Smith")) {
System.out.println(_person);
}
List<Listing> _listings = listingRepository.findAll();
System.out.println("listings " + _listings.size());
_listings.forEach(v -> {
System.out.println(v.toString());
});
}
Going by your entity model,I think what you are looking for is analogous to Many to Many/One to Many relationship in a relational database. So you can go for One way Embedding or Two way Embedding in MongoDb.
For One way embedding, You can create a Car collection like below:
db.carCollection.insertMany([{
_id:1,
make: 'porcha',
model:'qwerty',
year:'2018'
},
{
_id:2,
make: 'ferrara',
model:'uiop',
year:'2018'
}])
You can then go on to create user collection as below:
db.userCollection.insert({
_id:1,
user:'Tom',
email:'tom#tom.com',
car_ids:[1,2]
})
The car_ids is an array which will hold the ids of cars that belong to the user.
You can fetch the cars belonging to an user as(using findOne to fetch the user. Search parameter should be an unique id. I am considering email to be unique here.Ideally it should be user's id) :
var user=db.userCollection.findOne({email:'tom#tom.com'})
db.carCollection.find({_id:{$in:user.car_ids}})
This will fetch you all the cars per user
For fetching cars only you can simply do:
db.carCollection.find({})
For Two way embedding you can have similar array (as in user collection) inside cars collection so that each car can be identified to its user.