ampp
ampp12mo ago

ent filtering

trying to do this but it never returns a result unless args.name is null, tried several variations. Looked through the saas starter for examples like this. the goal is to find any channel names so you cant enter a duplicate name within the same team. const channel = await ctx.table("teams") .get(args.teamId) .edge("chatChannels") .filter((q) => q.gt(q.field("name"), args.name)); --- chatChannels: defineEnt({ name: v.string(), }) .index("name", ["name"]) <- added this too .edge("team") .edges("chatMessages", { ref: true }), --- orginal code: const channel = await ctx.db .query("chatChannels") .filter((q) => q.eq(q.field("name"), name)) .filter((q) => q.eq(q.field("teams"), teamId)) .first();
28 Replies
erquhart
erquhart12mo ago
It sounds like you just want to return all of the existing channel names for the team, why the filter?
ampp
amppOP12mo ago
I'm checking for uniqueness of the channels name basically just a isunqiue internal query to stop the user from duplicating channel names
erquhart
erquhart12mo ago
Looks like you want that, except using an edge But I don't know if what's returned there has a get method disregard that, wrong thing this is really simple to do in a regular convex function, trying to figure out the ents way Yeah I guess just use .eq(), although that's inefficient and you should just be able to index and call .unique(). That looks like:
const channel = await ctx.table("teams")
.get(args.teamId)
.edge("chatChannels")
.filter((q) => q.eq(q.field("name"), args.name));
const channel = await ctx.table("teams")
.get(args.teamId)
.edge("chatChannels")
.filter((q) => q.eq(q.field("name"), args.name));
That will get you the channel by that name or null Still figuring out how to do this with just an index via ents Does chatChannels table have a teamId field?
ampp
amppOP12mo ago
Yeah, with ents from my understanding the edge("team") makes the teamId and i see it in the blank schema changing it from q.qt to q.eq fixed it
erquhart
erquhart12mo ago
Yeah, but you're fetching every channel when you only need one. Probably fine for now if you're early on, but I want to understand the proper solution and leave it here for others at least. Need to improve my Entish lol
ampp
amppOP12mo ago
its only returning a single channel name, the one im attempting to search for
erquhart
erquhart12mo ago
In a plain convex function that looks like this:
const channel = await ctx.db.query('chatChannels').withIndex('by_teamId_name', q => q.eq('teamId', args.teamId).eq('name', args.name)).unique()
const channel = await ctx.db.query('chatChannels').withIndex('by_teamId_name', q => q.eq('teamId', args.teamId).eq('name', args.name)).unique()
It's fetching all of them from the db, and then basically using a js filter to remove all except the one you want Again, 100% effective for your immediate purpose, but a simple index does this with the efficiency of a point query.
ampp
amppOP12mo ago
and it still queries a lot if i use .unique too
erquhart
erquhart12mo ago
If you use a filter like my last bit of code there, and the name/teamid combo is unique in your database, the index will contain 0 or 1 rows Any other approach has to scan more rows, likely all of the channels for a given team. Which, again, is totally fine! I just want to leave the actual right answer here for others that find it in the future. For others running into this the tradeoff could be an issue.
ampp
amppOP12mo ago
well its at least not giving me all results across all teams, so the hack is worthy for the moment.
erquhart
erquhart12mo ago
100%! Isn't even a hack really, just not perfectly efficient, more than fine for most cases Would you mind trying this? I'm betting it will show a typescript error right away anyhow, but curious if it works:
const channel = await ctx.table("teams")
.get(args.teamId)
.edge("chatChannels")
.get('name', args.name)
const channel = await ctx.table("teams")
.get(args.teamId)
.edge("chatChannels")
.get('name', args.name)
ampp
amppOP12mo ago
it doesn't, it was something i was trying before
erquhart
erquhart12mo ago
ah okay alright @Michal Srb I'm throwing in the towel. What am I missing?
ampp
amppOP12mo ago
in a perfect world syntax wise, const channel = await ctx.table("chatChannels") .get("team", args.teamId) .get('name', args.name)
erquhart
erquhart12mo ago
@Michal Srb tl;dr: what's the Ent way to do this:
const channel = await ctx.db
.query('chatChannels')
.withIndex('by_teamId_name', q => q
.eq('teamId', args.teamId)
.eq('name', args.name))
.unique()
)
const channel = await ctx.db
.query('chatChannels')
.withIndex('by_teamId_name', q => q
.eq('teamId', args.teamId)
.eq('name', args.name))
.unique()
)
Yeah using table('teams') to get chat channels feels like indirection
ampp
amppOP12mo ago
well it saves code other places
erquhart
erquhart12mo ago
no I'm agreeing with you your example is closer to what I would expect, using ctx.table('chatChannels') since that's what you're after, and that's how the underlying convex query should work
Michal Srb
Michal Srb12mo ago
@erquhart it is
const channel = await ctx
.table("chatChannels")
.get("by_teamId_name", args.teamId, args.name);
const channel = await ctx
.table("chatChannels")
.get("by_teamId_name", args.teamId, args.name);
But it's been blocked by a TS bug and is currently sitting on a branch, see https://github.com/xixixao/convex-ents/issues/7 So meanwhile the normal approach works fine:
const channel = await ctx
.table("chatChannels", "by_teamId_name", (q) =>
q.eq("teamId", args.teamId).eq("name", args.name),
)
.unique();
const channel = await ctx
.table("chatChannels", "by_teamId_name", (q) =>
q.eq("teamId", args.teamId).eq("name", args.name),
)
.unique();
as documented here: https://labs.convex.dev/convex-ents/read#listing-ents-filtered-by-index
GitHub
get() with index should allow indexes with multiple fields · Issue ...
It should be possible to use a compound index to query unique ents: ctx.table("users").get("firstNameLastName", firstName, lastName) I had trouble getting the types to oblige wh...
Reading Ents from the Database - Convex Ents
Relations, default values, unique fields and more for Convex
erquhart
erquhart12mo ago
Ah, did not realize .table() takes more args, that makes a lot of sense. So with this signature you're effectively just dropping straight down to ctx.db() right? Oh no you're still using .get() though argh looking at the wrong thing nvm lol got it
Michal Srb
Michal Srb12mo ago
(the viaradic get() call will also translate to the exact same ctx.db query. It's all just syntactic sugar)
erquhart
erquhart12mo ago
Okay that's really handy
ampp
amppOP12mo ago
This does work, in my efforts to reduce the use of by_ in the chatchannels schema: .index("teamId_name", ["teamId", "name"]) gave only "compile time" typescript errors and .index("by_teamId_name", ["teamId", "name"]) didn't. Just thought that was worth noting if someone else vists this thread.
Michal Srb
Michal Srb12mo ago
@ampp The index name should not lead to typescript errors. What probably happened is that you changed two files (schema and functions), then saved only one of them, so the TypeScript in your terminal showed you an error. I suggest during development to use npx convex dev --typecheck=disable to avoid being confused by the terminal.
Michal Srb
Michal Srb10mo ago
@ampp @erquhart this has been released, make sure you're on convex-ents@latest and using TypeScript 5.4 or later:
const channel = await ctx
.table("chatChannels")
.get("by_teamId_name", args.teamId, args.name);
const channel = await ctx
.table("chatChannels")
.get("by_teamId_name", args.teamId, args.name);
Docs: https://labs.convex.dev/convex-ents/read#reading-a-single-ent-via-a-compound-index
Reading Ents from the Database - Convex Ents
Relations, default values, unique fields and more for Convex
gabrielw
gabrielw2mo ago
Hey just circling back to this thread - having a similar issue with filtering an edge with ents. I am on convex-ents@latest and Typescript 5.7. here is my query:
export const getEventsByTeamId = query({
args: {
teamId: v.id("teams"),
paginationOpts: paginationOptsValidator,
status: vEventStatus,
},
async handler(ctx, { teamId, paginationOpts, status }) {
return await ctx
.table("teams")
.getX(teamId)
.edge("events")
.filter((q) => q.eq(q.field("status"), status))
.order("asc", "startAtInMsSinceEpoch")
.paginate(paginationOpts);
},
});
export const getEventsByTeamId = query({
args: {
teamId: v.id("teams"),
paginationOpts: paginationOptsValidator,
status: vEventStatus,
},
async handler(ctx, { teamId, paginationOpts, status }) {
return await ctx
.table("teams")
.getX(teamId)
.edge("events")
.filter((q) => q.eq(q.field("status"), status))
.order("asc", "startAtInMsSinceEpoch")
.paginate(paginationOpts);
},
});
The .filter is throwing a Type Error: Parameter 'q' implicitly has an 'any' type. Thanks all.
erquhart
erquhart2mo ago
@gabrielw I would open a separate support post for your specific issue, this is an old one that's already resolved and doesn't mention the error you're encountering.
gabrielw
gabrielw2mo ago
Thanks @erquhart , I opened up a support post

Did you find this page helpful?