Spiced
Spiced13mo ago

Custom FilterIf Function

creating a custom filter if funciton
48 Replies
Spiced
SpicedOP13mo ago
@lee here we go
lee
lee13mo ago
How does this look
let query = db.query("messages");
if (args.author) {
query = query.filter(q=>q.eq(q.field("author"), args.author);
}
const results = await query.collect();
let query = db.query("messages");
if (args.author) {
query = query.filter(q=>q.eq(q.field("author"), args.author);
}
const results = await query.collect();
Spiced
SpicedOP13mo ago
Okay so yes, however i want to use if kind of like the filter function
export const getDiagrams = query({
args: {
searchQ: v.optional(v.string()),
languageId: v.optional(v.string()),
},
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();

if (!identity) {
throw new Error("Unauthorized");
}
const userId = identity.subject;

let dashboard;
if (args.searchQ != null || args.searchQ != undefined) {
dashboard = await ctx.db
.query("diagrams")
.withSearchIndex("search_diagram_name", (q) =>
q
.search("title", args.searchQ ? args.searchQ : "")
.eq("userId", userId),
)
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filter((q) => q.eq(q.field("databaseTypeId"), args.languageId))
.collect();
} else {
dashboard = await ctx.db
.query("diagrams")
.withIndex("by_user", (q) => q.eq("userId", userId))
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filter((q) => q.eq(q.field("databaseTypeId"), args.languageId))
.collect();
}

return dashboard;
},
});
export const getDiagrams = query({
args: {
searchQ: v.optional(v.string()),
languageId: v.optional(v.string()),
},
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();

if (!identity) {
throw new Error("Unauthorized");
}
const userId = identity.subject;

let dashboard;
if (args.searchQ != null || args.searchQ != undefined) {
dashboard = await ctx.db
.query("diagrams")
.withSearchIndex("search_diagram_name", (q) =>
q
.search("title", args.searchQ ? args.searchQ : "")
.eq("userId", userId),
)
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filter((q) => q.eq(q.field("databaseTypeId"), args.languageId))
.collect();
} else {
dashboard = await ctx.db
.query("diagrams")
.withIndex("by_user", (q) => q.eq("userId", userId))
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filter((q) => q.eq(q.field("databaseTypeId"), args.languageId))
.collect();
}

return dashboard;
},
});
This is an example that me and you worked on not long ago So a more optimal way would be
lee
lee13mo ago
And you don't want to put an if condition in the filter?
Spiced
SpicedOP13mo ago
dashboard = await ctx.db
.query("diagrams")
.withIndex("by_user", (q) => q.eq("userId", userId))
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filterIf((q) => q.eq(q.field("databaseTypeId"), args.languageId))
.collect();
dashboard = await ctx.db
.query("diagrams")
.withIndex("by_user", (q) => q.eq("userId", userId))
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filterIf((q) => q.eq(q.field("databaseTypeId"), args.languageId))
.collect();
SO if the args.languageId is null, it gets ignored otherwise we filter
lee
lee13mo ago
There's no built-in way to do that. There needs to be an if condition somewhere. How about making a function like
function eqOrNull(f, val) {
return (q) => (val === null || q.eq(q.field(f), val));
}
function eqOrNull(f, val) {
return (q) => (val === null || q.eq(q.field(f), val));
}
and use it like filter(eqOrNull("databaseTypeId", args.languageId)) (I managed to not use an if condition 😁 )
Spiced
SpicedOP13mo ago
Haha, okay i got to give oyu that one issue, im using typescript
lee
lee13mo ago
You could do it inline? .filter(q=> args.languageId === null || q.eq(q.field("databaseTypeId"), args.languageId)) Which duplicates the value but it should typecheck
Spiced
SpicedOP13mo ago
Huh, that actually worked?
lee
lee13mo ago
You could add types to the eqOrNull helper. It does have well-defined types. But i'm no typescript expert
Spiced
SpicedOP13mo ago
Although i really wanted something not inline like a filterIf
lee
lee13mo ago
Well you could make a customQuery that does that. Sounds tricky but it should work
Spiced
SpicedOP13mo ago
I might need your help to start Any resources on custom query?
ballingt
ballingt13mo ago
Isn't TypeScript ok with @lee's original code?
let query = db.query("messages");
if (args.author) {
query = query.filter(q=>q.eq(q.field("author"), args.author);
}
const results = await query.collect();
let query = db.query("messages");
if (args.author) {
query = query.filter(q=>q.eq(q.field("author"), args.author);
}
const results = await query.collect();
Spiced
SpicedOP13mo ago
Yeah but we agreed not to use any ifs
ballingt
ballingt13mo ago
is this an interview challenge? 😛 ok I'm catching up on the thread now I see there are some viable things here, but you'd like to be able to compose these similarly to existing filters
Spiced
SpicedOP13mo ago
Haha, not really, just want to make a good function, rather create one now than have 50 if statements building my query ya know Ideally i would like to extend the current filter function with a custom filter that does the conditionals
ballingt
ballingt13mo ago
There's a bit in Anjana's section of this video https://youtu.be/UAjcJPMexfk?t=412 that might be interesting to you
Learn With Jason
YouTube
4 Web Devs, 1 App Idea (@WebDevCody, Anjana Vakil, Tom Ballinger)
Build a way to show real-time updates on the website for a Dungeons and Dragons-themed small business! 4 web devs built their own app based on this prompt. See what they built and watch their reactions to each other's work in this installment of the series. ---------------------------------------------------------------------- Huge thanks to Co...
ballingt
ballingt13mo ago
gist being that the partial query is an object that you can manipulate, so you can write functions that add things to it e.g.
let query = db.query("messages");
query = addAuthorFilter(query, author);
const results = await query.collect();
let query = db.query("messages");
query = addAuthorFilter(query, author);
const results = await query.collect();
Spiced
SpicedOP13mo ago
Ah, so filter as i go, collect last
ballingt
ballingt13mo ago
yeah you can compositionally build up your query, using whatever abstractions you desire, then run it at the end
Spiced
SpicedOP13mo ago
Interesting, i was trying to stray away from that as-well as a challenge because i found that too easy, suspiciously too easy
ballingt
ballingt13mo ago
your initial question made me think "why not use an if, that seems simplest" but if I follow you're writing a lot of queries (or anticipating writing a lot) and want to be able to change things in one spot — in which case helper functions like this that modify the query might be useful
Spiced
SpicedOP13mo ago
Exactly That was the whole point A single location to modify instead of searching everywhere’s
ballingt
ballingt13mo ago
I have to say I like @lee's .filter(q=> args.languageId === null || q.eq(q.field("databaseTypeId"), args.languageId)) but yeah if you're hoping to build your own abstractions, I'd do it as functions that modify the query in various ways. It's also possible to write custom filters by modifying the methods on db (that's what customFunction does) it's pretty involved, I'd recommend playing around composing functions at first then if you figure out a querying paradigm you like better writing a custom function that adds it to ctx.db
Spiced
SpicedOP13mo ago
Any resources on that? I wanna start playing around with some custom function Again ideally it would be like this Check the filterIf function BUT i dont know where to start
ballingt
ballingt13mo ago
Customizing serverless functions without middleware
Re-use code and centralize request handler definitions with discoverability and type safety and without the indirection of middleware or nesting of wr...
Spiced
SpicedOP13mo ago
Would the way im thinking be possible in the first place?
ballingt
ballingt13mo ago
Yes, things like
dashboard = await ctx.db
.query("diagrams")
.withIndex("by_user", (q) => q.eq("userId", userId))
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filterIf((q) => q.eq(q.field("databaseTypeId"), args.languageId))
.collect();
dashboard = await ctx.db
.query("diagrams")
.withIndex("by_user", (q) => q.eq("userId", userId))
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filterIf((q) => q.eq(q.field("databaseTypeId"), args.languageId))
.collect();
are possible: you do it by extending or replacing the Convex implementation of db (DatabaseReader in a query) to do more things. Here your new filterIf method could replace the passed in query with the one Lee wrote. https://github.com/get-convex/convex-js/blob/main/src/server/database.ts#L103
GitHub
convex-js/src/server/database.ts at main · get-convex/convex-js
TypeScript/JavaScript client library for Convex. Contribute to get-convex/convex-js development by creating an account on GitHub.
Spiced
SpicedOP13mo ago
Huh i see Can you provide me with some more info so i can have a rough plan to implement tomorrow
ballingt
ballingt13mo ago
Those two linked posts are the place to start
Spiced
SpicedOP13mo ago
Custom functions? Although i do think it would be a good thing to have out of the box
ballingt
ballingt13mo ago
Yep, custom functions are how to you implement new DB behavior. If I were starting down this path I'd start with a function that Lee recommended
function eqOrNull(f, val) {
return (q) => (val === null || q.eq(q.field(f), val));
}
function eqOrNull(f, val) {
return (q) => (val === null || q.eq(q.field(f), val));
}
it's almost the same syntax:
dashboard = await ctx.db
.query("diagrams")
.withIndex("by_user", (q) => q.eq("userId", userId))
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filter(eqOrNull("databaseTypeId", args.langaugeId))
.collect();
dashboard = await ctx.db
.query("diagrams")
.withIndex("by_user", (q) => q.eq("userId", userId))
.filter((q) => q.eq(q.field("isArchived"), false))
//Inseting If Here
.filter(eqOrNull("databaseTypeId", args.langaugeId))
.collect();
Spiced
SpicedOP13mo ago
But we dont have the filterIf yet
ballingt
ballingt13mo ago
Although i do think it would be a good thing to have out of the box
This is helpful to hear, it's added to the list of requests!
Spiced
SpicedOP13mo ago
Oh wow, i didnt think it would get there, just a thought Might help people scaling from writing a lot of functions and such
ballingt
ballingt13mo ago
I don't totally see it, but if enough people ask for something it's hard to ignore. You're the first for this, if we hear enough that that's something people want because it's how they think about queries and they don't like the .filter(eqOrNull("databaseTypeId", args.langaugeId)) syntax we can do this Is there another query language you're used to that has this? always helpful to link to another system as an example of people finding it useful
Spiced
SpicedOP13mo ago
I mean i do a lot of daily coding with c# .net and MSSQL Basically in the filter if anything is null it kind of gets ignored unless SPECIFICALLY specified to be null
ballingt
ballingt13mo ago
ah totally, yeah special SQL NULL is special (edit: although I don't quite follow this example) In JavaScript it's usually undefined that works like this
Spiced
SpicedOP13mo ago
Well, my args.languageJd was undefined, but still it broke the wuery It threw me off cuz im trying to use my SQL knowledge here 😂
lee
lee13mo ago
in SQL if you have a WHERE field = $1 clause and $1 is null, isn't that always false? Whereas in this case you would want it to be true
Spiced
SpicedOP13mo ago
If its null, it gets everything (In c# Linq) I dont know the inner deeper working of LinQ but yeah
lee
lee13mo ago
Interesting, i've never worked with LinQ. I don't think the query builders i've worked with would do this Good to know the example, thanks! Can you give the LinQ syntax you're referring to?
ballingt
ballingt13mo ago
@Spiced +1, would love to see what the LinQ syntax for this kind of filter is.
Spiced
SpicedOP13mo ago
This is an example. Sorry i fell asleep
var list = context.Obj.Where(o => A.HasValue ? o.a == A : true);
var list = context.Obj.Where(o => A.HasValue ? o.a == A : true);
lee
lee13mo ago
That doesn't look like the language prividing a filterIf. It looks very similar to .filter(q=> A !== null ? q.eq(q.field("a"), A) : true) which is code you could write in Convex, and is what i would recommend over messing with customQuery.
Spiced
SpicedOP13mo ago
I mean i understand, its just a small challenge for me Got the hang of the custom queries although got lost after i understood custom mutations and queries

Did you find this page helpful?