count self relation on Prisma error: table name specified more than once - prisma

I am trying to count a self relation (followers) in Prisma2 (using PostgreSQL)
Model:
model User {
id String #id #default(cuid())
following User[] #relation(name: "UserFollows")
followers User[] #relation(name: "UserFollows")
}
Query:
const user = await prisma.user.findUnique({
where: { id: userId },
include: {
_count: {
select: { followers: true, following: true },
},
},
});
(using previewFeatures = ["selectRelationCount"]) and getting the following error:
Invalid prisma.user.findUnique() invocation:
Error occurred during query execution: ConnectorError(ConnectorError
{ user_facing_error: None, kind: QueryError(Error { kind: Db, cause:
Some(DbError { severity: "ERROR", parsed_severity: Some(Error), code:
SqlState("42712"), message: "table name "User" specified more than
once", detail: None, hint: None, position: None, where_: None, schema:
None, table: None, column: None, datatype: None, constraint: None,
file: Some("parse_relation.c"), line: Some(423), routine:
Some("checkNameSpaceConflicts") }) }) })
Does anybody have any idea of what I am doing wrong?

This is a known issue with self-relations and we hope to fix it soon. If you want to track this bug, follow this github issue. Please feel free to add a comment to explain your use case/problem over there.
In the meantime, here are some workarounds that you can use:
Find count using nested read
You can use a nested read to return all the records in followers and following and find the length of those arrays to get the count. This seems like the most straightforward way, so long as you're okay with fetching all the followers/following records.
const user = await prisma.user.findUnique({
where: {
id: userId,
},
include: {
followers: true,
following: true,
},
});
let followerCount = user.followers.length;
let followingCount = user.following.length;
Find count using separate count queries.
Alternatively, you can use the count API to find followers and following counts for a certain user.
// number of followers for some user "x" = number of times x.id appaers in "following" relation of other users.
const followerCount = await prisma.user.count({
where: {
following: {
some: {
id: userId,
},
},
},
});
// number of users that user "x" is following = number of times x.id appaers in "followers" relation of other users.
const followingCount = await prisma.user.count({
where: {
followers: {
some: {
id: userId,
},
},
},
});
Change schema to use explicit many-to-many notation
If you're okay with slightly tweaking your schema, you can explicitly define the many-to-many relation table.
model Follows {
follower User #relation("follower", fields: [followerId], references: [id])
followerId String
following User #relation("following", fields: [followingId], references: [id])
followingId String
##id([followerId, followingId])
}
model User {
id String #id #default(cuid())
followers Follows[] #relation("follower")
following Follows[] #relation("following")
}
You should be able to run the count query without issues in this way.

Related

PRISMA 2.0 - I need help to delete a record inside a field with relationship list

i am on this challange since Monday... please help me? Check my models.
model User {
id String #id #default(cuid())
bookmarks Home[] #relation(name: "UserBookmarks")
}
model Home {
bookmarkedBy User? #relation(name: "UserBookmarks", fields: [userId], references: [id])
userId String?
}
This example is: ONE PERSON can have MULTIPLE Homes in their BOOKMARKS, ok?
My PATCH it works fine, check above.
const home = await prisma.home.update({ where: { id: "123" }, data: { userId: "my-user-id", }, });
When i see the content in PRISMA STUDIO... works fine... i have a list of Homes in bookmarks field. PERFECT.
BUT NOW... how i can DELETE a BOOKMARK in this CASE?
i tried eveything... but i cant understand how i can do that :-( I tried this code bellow:
const home = await prisma.home.delete({ where: { userId: "my-user-id", }, });
AND
const user = await prisma.user.delete({ where: { bookmarks: "my-home-id", }, });
In this case Above... appears this message:
{
"message": "\nInvalid prisma.home.delete() invocation:\n\n{\n where: {\n userId: 'cl3he5om60008you1geu5hbpa'\n ~~~~~~\n }\n}\n\nUnknown arg userId in where.userId for type HomeWhereUniqueInput. Did you mean id? Available args:\ntype HomeWhereUniqueInput {\n id?: String\n}\n\n"
}
HELP ME GUYS!
Thanks guys

Find record with an empty relation

Say I have this prisma schema with an implicit m:n-relation of Post and Tag
model Post {
id String #id
tags Tag[]
}
model Tag {
id Int #id #default(autoincrement())
posts Post[]
}
How do I find the first Post that has no associated Tags?
prisma.post.findFirst({
where: {
tags: {
// are nonexistent (something like count === 0?)
},
},
}),
Thanks for the help :)
You can probably use orderBy by count of tags in ascending order and get the first one? Like that:
prisma.post.findFirst({
orderBy: { tags: { _count: 'asc' } },
});
Searching on the internet I've found a link where the official documentation gives information on how to manage lists --> here
There it gives this example:
const posts = await prisma.post.findMany({
where: {
tags: {
isEmpty: true,
},
},
})
Try adapting that to your own situation.

Removing reference from a child collection in MongooseJS

I have a schema that includes an array of child references:
const schemaSet = {
userSchema: new Schema({
name: {
type: String,
required: true
}
}),
groupSchema: new Schema({
name: {
type: String,
required: true
},
members: [ {
type: Schema.Types.ObjectId
ref: 'User'
}]
})
}
This is working fine in terms of being able to create groups and add users to them, but I find I can't remove a user from the group.
The closest I have got so far is this:
async removeUser(group, userId) {
console.log("Before: group has "+group.members.length+" members");
await group.members.pull({ _id : userId });
console.log("After: group has "+group.members.length+" members");
await group.save();
}
This logs out the correct size before and after the call and runs with no errors, but the next time I retrieve that group, the member is still there. I have tried using remove as well with much the same outcome.
I only want to remove the reference from the members collection, the User needs to persist. How do I persist the removal of the reference to a document?
It took me a while to discover this. If you have a group model (created using const Group = mongoose.model("Group", groupSchema)), and the id of the user you want to remove from the group, you can use the following syntax: const updatedGroup = await Group.findOneAndUpdate({ members: userId }, { $pull: { members: userId }}, {new: true, useFindAndModify: false});
The third parameter in that function indicates that the updated document should be returned, and ensures that you don't get a warning. Actually, that syntax will only work if the group to user relationship is one to many (i.e. any user can only be in one group). If the relationships is many to many, I guess you would have to use the following:
await Group.where({ members: userId}).update({ $pull: { members: userId }});

How do I query a particular field in loopback 4 through the repository?

I want to enforce uniqueness so I would like to see if there are any other emails and usernames that are similar to the one posted to this route. How do I do that through the repository, it keeps on asking about a filter which I see but cannot get my head around it.
#post('/users', {
responses: {
'200': {
description: 'User model instance',
content: {'application/json': {schema: {'x-ts-type': User}}},
},
},
})
async create(#requestBody() user: User): Promise<User> {
//check : User= await this.userRepository.create(user);
//#param.query.object('filter', getFilterSchemaFor(User)) filter?: Filter;
// var check:any=await this.userRepository.find(filter);
//filter: Filter;
var check: User = await this.userRepository.find({email:user.email});
var isNotPresent: boolean = true;
// check.forEach(function(val){
// });
// if(isNotPresent)
return await this.userRepository.create(user);
}
A Filter object has the following properties that can be used to define a query and it's response:
where: Used to define a query. In your case, you would like to find existing users with the same email and username as provided in the request body.
fields: To specify fields that you would like to include or exclude in the response of your query. Every object in the array returned by find() will have only those fields which are set to true in the fields object.
offset, skip, limit and order: Used for pagination.
So, in your case, assuming a 'user' has an 'email' and an 'username', the filter object would look like the following:
const filter: Filter = {
where: {
'email': user.email,
'username': user.username
},
fields: {
email: true,
username: true
},
offset: 0,
limit: 10,
skip: 0,
order: [],
};
And your call to the repository method would look like the following:
var check: User = await this.userRepository.find(filter);
My first SO answer. Hope this helps.

Create unique multikey index via model settings

I am using Sails v1.1 -
I created a many-to-many through custom model association following the sails doc here - https://sailsjs.com/documentation/concepts/models-and-orm/associations/through-associations
The PetUser model has two columns pet and user, where each is the respective id. I want to create a unique multi-key index, meaning there cannot be two rows with the same combination of "pet and user". Meaning the second call should succeed, and third call should fail with uniqueness error:
await PetUser.create({ user: 1, pet: 33 }); // should succeed
await PetUser.create({ user: 1, pet: 44 }); // should succeed as user/pet combination is different
await PetUser.create({ user: 1, pet: 33 }); // should fail
I tried adding unique: true to both the owner and pet attribute on PetUser model below, but only the first unique: true gets respected.
So this is my code in myApp/api/models/PetUser.js
module.exports = {
attributes: {
owner: {
model:'user',
unique: true
},
pet: {
model: 'pet',
unique: true
}
}
}
For implementing similar behavior I added a combined attribute and mark it unique. Also, I added beforeCreate and beforeUpdate model hooks on which I generate my combined attribute to check is it unique or not.
const YourModel = {
attributes: {
owner: {
model: 'user',
},
pet: {
model: 'pet',
},
petOwner: {
type: 'string',
unique: true,
}
},
beforeCreate : function(values,cb) {
// TODO get ids from related records or reset to default on missed relation record if you need it
const petId = 35;
const ownerId = 8;
values.petOwner = `${petId}-${ownerId}`;
cb();
},
beforeUpdate : function(values,cb) {
YourModel.beforeCreate(values, cb)
},
};
module.exports = YourModel;
In result when you tries to add the record with the same relations, you will get E_UNIQUE as you expected.