edproton
edproton
CCConvex Community
Created by edproton on 11/30/2024 in #support-community
Dynamic query builder in convex
I'm trying to do a dynamic query based on conditions
import { v } from "convex/values";
import { BookingStatus } from "./lib/bookings";
import { query } from "./_generated/server";

export const getBookings = query({
args: {
studentId: v.optional(v.id("users")),
tutorId: v.optional(v.id("users")),
status: v.optional(
v.union(
v.literal(BookingStatus.AWAITING_TUTOR_CONFIRMATION),
v.literal(BookingStatus.AWAITING_STUDENT_CONFIRMATION),
v.literal(BookingStatus.AWAITING_PAYMENT),
v.literal(BookingStatus.PAYMENT_FAILED),
v.literal(BookingStatus.SCHEDULED),
v.literal(BookingStatus.CANCELED),
v.literal(BookingStatus.COMPLETED)
)
),
startDate: v.optional(v.number()), // Unix timestamp in milliseconds
endDate: v.optional(v.number()), // Unix timestamp in milliseconds
},
handler: async (ctx, args) => {
let query = ctx.db.query("bookings");

if (args.studentId) {
query = query.withIndex("by_student", (q) =>
q.eq("student", args.studentId!)
);
}

if (args.tutorId) {
query = query.withIndex("by_tutor", (q) => q.eq("tutor", args.tutorId));
}

if (args.status) {
query = query.withIndex("by_status", (q) => q.eq("status", args.status));
}

if (args.startDate !== undefined) {
query = query.filter((q) => q.gte(q.field("startTime"), args.startDate));
}

if (args.endDate !== undefined) {
query = query.filter((q) => q.lte(q.field("startTime"), args.endDate));
}

return await query.collect();
},
});
import { v } from "convex/values";
import { BookingStatus } from "./lib/bookings";
import { query } from "./_generated/server";

export const getBookings = query({
args: {
studentId: v.optional(v.id("users")),
tutorId: v.optional(v.id("users")),
status: v.optional(
v.union(
v.literal(BookingStatus.AWAITING_TUTOR_CONFIRMATION),
v.literal(BookingStatus.AWAITING_STUDENT_CONFIRMATION),
v.literal(BookingStatus.AWAITING_PAYMENT),
v.literal(BookingStatus.PAYMENT_FAILED),
v.literal(BookingStatus.SCHEDULED),
v.literal(BookingStatus.CANCELED),
v.literal(BookingStatus.COMPLETED)
)
),
startDate: v.optional(v.number()), // Unix timestamp in milliseconds
endDate: v.optional(v.number()), // Unix timestamp in milliseconds
},
handler: async (ctx, args) => {
let query = ctx.db.query("bookings");

if (args.studentId) {
query = query.withIndex("by_student", (q) =>
q.eq("student", args.studentId!)
);
}

if (args.tutorId) {
query = query.withIndex("by_tutor", (q) => q.eq("tutor", args.tutorId));
}

if (args.status) {
query = query.withIndex("by_status", (q) => q.eq("status", args.status));
}

if (args.startDate !== undefined) {
query = query.filter((q) => q.gte(q.field("startTime"), args.startDate));
}

if (args.endDate !== undefined) {
query = query.filter((q) => q.lte(q.field("startTime"), args.endDate));
}

return await query.collect();
},
});
schema.ts
bookings: defineTable({
student: v.id("users"), // Reference to the student (user with role STUDENT)
tutor: v.id("users"), // Reference to the tutor (user with role TUTOR)
service: v.id("services"), // Reference to the service being booked
type: v.union(
v.literal(BookingType.FREE_MEETING),
v.literal(BookingType.LESSON)
), // Booking type
status: v.union(
v.literal(BookingStatus.AWAITING_TUTOR_CONFIRMATION),
v.literal(BookingStatus.AWAITING_STUDENT_CONFIRMATION),
v.literal(BookingStatus.AWAITING_PAYMENT),
v.literal(BookingStatus.PAYMENT_FAILED),
v.literal(BookingStatus.SCHEDULED),
v.literal(BookingStatus.CANCELED),
v.literal(BookingStatus.COMPLETED)
), // Booking status
startTime: v.number(), // Start time of the booking (timestamp)
endTime: v.number(), // End time of the booking (timestamp)
createdAt: v.number(), // Creation time of the booking (timestamp)
updatedAt: v.number(), // Last updated time of the booking (timestamp)
})
.index("by_student", ["student", "startTime"])
.index("by_tutor", ["tutor", "startTime"])
.index("by_status", ["status"]),
bookings: defineTable({
student: v.id("users"), // Reference to the student (user with role STUDENT)
tutor: v.id("users"), // Reference to the tutor (user with role TUTOR)
service: v.id("services"), // Reference to the service being booked
type: v.union(
v.literal(BookingType.FREE_MEETING),
v.literal(BookingType.LESSON)
), // Booking type
status: v.union(
v.literal(BookingStatus.AWAITING_TUTOR_CONFIRMATION),
v.literal(BookingStatus.AWAITING_STUDENT_CONFIRMATION),
v.literal(BookingStatus.AWAITING_PAYMENT),
v.literal(BookingStatus.PAYMENT_FAILED),
v.literal(BookingStatus.SCHEDULED),
v.literal(BookingStatus.CANCELED),
v.literal(BookingStatus.COMPLETED)
), // Booking status
startTime: v.number(), // Start time of the booking (timestamp)
endTime: v.number(), // End time of the booking (timestamp)
createdAt: v.number(), // Creation time of the booking (timestamp)
updatedAt: v.number(), // Last updated time of the booking (timestamp)
})
.index("by_student", ["student", "startTime"])
.index("by_tutor", ["tutor", "startTime"])
.index("by_status", ["status"]),
I get a huge error, sorry for the spam
Type 'Query<{ document: { _id: Id<"bookings">; _creationTime: number; type: BookingType; tutor: Id<"users">; student: Id<"users">; service: Id<"services">; status: BookingStatus; startTime: number; endTime: number; createdAt: number; updatedAt: number; }; fieldPaths: ExtractFieldPaths<...> | "_id"; indexes: { ...; }; sear...' is missing the following properties from type 'QueryInitializer<{ document: { _id: Id<"bookings">; _creationTime: number; type: BookingType; tutor: Id<"users">; student: Id<"users">; service: Id<"services">; status: BookingStatus; startTime: number; endTime: number; createdAt: number; updatedAt: number; }; fieldPaths: ExtractFieldPaths<...> | "_id"; indexes: { ....': fullTableScan, withIndex, withSearchIndexts(2739)
Type 'Query<{ document: { _id: Id<"bookings">; _creationTime: number; type: BookingType; tutor: Id<"users">; student: Id<"users">; service: Id<"services">; status: BookingStatus; startTime: number; endTime: number; createdAt: number; updatedAt: number; }; fieldPaths: ExtractFieldPaths<...> | "_id"; indexes: { ...; }; sear...' is missing the following properties from type 'QueryInitializer<{ document: { _id: Id<"bookings">; _creationTime: number; type: BookingType; tutor: Id<"users">; student: Id<"users">; service: Id<"services">; status: BookingStatus; startTime: number; endTime: number; createdAt: number; updatedAt: number; }; fieldPaths: ExtractFieldPaths<...> | "_id"; indexes: { ....': fullTableScan, withIndex, withSearchIndexts(2739)
and all the args need to be marked with !
14 replies
CCConvex Community
Created by edproton on 11/27/2024 in #support-community
Tanstack query with usePaginatedQuery is not working
I'm having trouble using TanStack Query's usePaginatedQuery. It doesn’t seem to behave as expected, and TypeScript is throwing errors. Here’s the code and the error I’m encountering: Working Example This works fine:
usePaginatedQuery(
api.tutors.list, // Query function
{}, // Query arguments
{ initialNumItems: 6 } // Options
);
usePaginatedQuery(
api.tutors.list, // Query function
{}, // Query arguments
{ initialNumItems: 6 } // Options
);
Problematic Code However, in this usage:
await opts.context.queryClient.ensureQueryData(
convexQuery(
usePaginatedQuery(api.tutors.list, {}, { initialNumItems: 6 })
)
);
await opts.context.queryClient.ensureQueryData(
convexQuery(
usePaginatedQuery(api.tutors.list, {}, { initialNumItems: 6 })
)
);
I get the following TypeScript error:
Expected 2 arguments, but got 1.ts(2554)
index.d.ts(101, 182): An argument for 'queryArgs' was not provided.
Expected 2 arguments, but got 1.ts(2554)
index.d.ts(101, 182): An argument for 'queryArgs' was not provided.
Observations - usePaginatedQuery seems to expect two required arguments: - A query function. - Query arguments (e.g., an object representing the filters, pagination params, etc.). - In the problematic code, it seems like TypeScript isn’t recognizing the query arguments or options properly, even though they’re provided. Questions 1. Am I misunderstanding the correct way to use usePaginatedQuery within convexQuery or ensureQueryData? 2. Is this related to how ensureQueryData works or some conflict with convexQuery? 3. Could this be a TypeScript inference issue, and if so, how can I resolve it? 4. Is usePaginatedQuery still supported in the latest version of TanStack Query? Any help or clarification would be greatly appreciated! If there’s a better pattern for combining usePaginatedQuery with ensureQueryData, I’d love to know!
6 replies
CCConvex Community
Created by edproton on 11/26/2024 in #support-community
Type inference is not ok. Is assuming the user properties are null
import { query } from "./_generated/server";
import { paginationOptsValidator } from "convex/server";
import { getAll } from "convex-helpers/server/relationships";

export const list = query({
args: { paginationOpts: paginationOptsValidator },
handler: async (ctx, { paginationOpts }) => {
// Fetch a page of tutors
const results = await ctx.db.query("tutors").paginate(paginationOpts);

// Extract user IDs from the tutors
const userIds = results.page.map((t) => t.user);

// Fetch all user documents corresponding to the user IDs
const users = await getAll(ctx.db, userIds); // Everything here is not null

// Combine tutor and user information
const page = await Promise.all(
results.page.map(async (tutor) => {
const user = users.find((u) => u._id === tutor.user); // THIS IS NOT NULLABLE...

// Generate the URL for the user's profile image
const imageUrl = await ctx.storage.getUrl(user!.image);

return {
...tutor,
user: {
...user,
image: imageUrl,
},
};
})
);

return {
...results,
page,
};
},
});
import { query } from "./_generated/server";
import { paginationOptsValidator } from "convex/server";
import { getAll } from "convex-helpers/server/relationships";

export const list = query({
args: { paginationOpts: paginationOptsValidator },
handler: async (ctx, { paginationOpts }) => {
// Fetch a page of tutors
const results = await ctx.db.query("tutors").paginate(paginationOpts);

// Extract user IDs from the tutors
const userIds = results.page.map((t) => t.user);

// Fetch all user documents corresponding to the user IDs
const users = await getAll(ctx.db, userIds); // Everything here is not null

// Combine tutor and user information
const page = await Promise.all(
results.page.map(async (tutor) => {
const user = users.find((u) => u._id === tutor.user); // THIS IS NOT NULLABLE...

// Generate the URL for the user's profile image
const imageUrl = await ctx.storage.getUrl(user!.image);

return {
...tutor,
user: {
...user,
image: imageUrl,
},
};
})
);

return {
...results,
page,
};
},
});
4 replies
CCConvex Community
Created by edproton on 11/25/2024 in #support-community
I'm trying to upload data but it says Property 'store' does not exist on type 'StorageWriter'
How can I upload stuff? I'm reading this https://docs.convex.dev/file-storage/store-files but the .store does not exists "convex": "^1.17.2",
export const seedTutors = internalMutation({
handler: async (ctx) => {
const data: TutorProfileJSON[] = JSON.parse(seedTutorsData);
const hasData = await ctx.db.query("tutors").take(1);
if (hasData.length > 0) {
console.log("Data already seeded");
return;
}

for (const row of data) {
if (
row.profileImageUrl.endsWith(".svg") ||
row.profileImageUrl.includes("placeholder")
) {
continue;
}

const response = await fetch(row.profileImageUrl);
const image = await response.blob();

const storageId = await ctx.storage.store(image);

const userId = await ctx.db.insert("users", {
name: row.name,
email: `${row.name.toLowerCase().replace(" ", ".")}@mtxceltutors.com`,
emailVerificationTime: Date.now(),
});

const tutorId = await ctx.db.insert("tutors", {
user: userId,
tags: row.tags,
bio: {
main: row.bio,
session: row.sessionBio,
short: row.bioShort,
},
});
}
},
});
export const seedTutors = internalMutation({
handler: async (ctx) => {
const data: TutorProfileJSON[] = JSON.parse(seedTutorsData);
const hasData = await ctx.db.query("tutors").take(1);
if (hasData.length > 0) {
console.log("Data already seeded");
return;
}

for (const row of data) {
if (
row.profileImageUrl.endsWith(".svg") ||
row.profileImageUrl.includes("placeholder")
) {
continue;
}

const response = await fetch(row.profileImageUrl);
const image = await response.blob();

const storageId = await ctx.storage.store(image);

const userId = await ctx.db.insert("users", {
name: row.name,
email: `${row.name.toLowerCase().replace(" ", ".")}@mtxceltutors.com`,
emailVerificationTime: Date.now(),
});

const tutorId = await ctx.db.insert("tutors", {
user: userId,
tags: row.tags,
bio: {
main: row.bio,
session: row.sessionBio,
short: row.bioShort,
},
});
}
},
});
14 replies