-
Notifications
You must be signed in to change notification settings - Fork 46
Feat/latex tailored resume #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
00277d1
ab83744
0fd3b90
67b9f3f
e203391
4a7a8a1
cd82256
9d50a09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -245,6 +245,108 @@ export const getUserCoverLetters = query({ | |
| }, | ||
| }); | ||
|
|
||
| // ─── Tailored Resume Functions ─────────────────────────────────────── | ||
|
|
||
| export const saveTailoredResume = mutation({ | ||
| args: { | ||
| userId: v.string(), | ||
| resumeHash: v.string(), | ||
| jobDescriptionHash: v.string(), | ||
| templateId: v.string(), | ||
| jobTitle: v.optional(v.string()), | ||
| companyName: v.optional(v.string()), | ||
| resumeName: v.optional(v.string()), | ||
| jobDescription: v.optional(v.string()), | ||
| structuredData: v.string(), | ||
| latexSource: v.string(), | ||
| builderSlug: v.optional(v.string()), | ||
| version: v.optional(v.number()), | ||
| sourceAnalysisId: v.optional(v.string()), | ||
| customTemplateName: v.optional(v.string()), | ||
| customTemplateSource: v.optional(v.string()), | ||
| }, | ||
| handler: async (ctx, args) => { | ||
| const id = await ctx.db.insert("tailoredResumes", args); | ||
| const doc = await ctx.db.get(id); | ||
| return doc; | ||
| }, | ||
| }); | ||
|
|
||
| export const getTailoredResume = query({ | ||
| args: { | ||
| userId: v.string(), | ||
| resumeHash: v.string(), | ||
| jobDescriptionHash: v.string(), | ||
| templateId: v.string(), | ||
| }, | ||
| handler: async (ctx, args) => { | ||
| const doc = await ctx.db | ||
| .query("tailoredResumes") | ||
| .filter((q) => | ||
| q.and( | ||
| q.eq(q.field("userId"), args.userId), | ||
| q.eq(q.field("resumeHash"), args.resumeHash), | ||
| q.eq(q.field("jobDescriptionHash"), args.jobDescriptionHash), | ||
| q.eq(q.field("templateId"), args.templateId), | ||
| ) | ||
| ) | ||
| .order("desc") | ||
| .first(); | ||
| return doc; | ||
| }, | ||
| }); | ||
|
|
||
| export const getTailoredResumeById = query({ | ||
| args: { tailoredResumeId: v.id("tailoredResumes") }, | ||
| handler: async (ctx, args) => { | ||
| return await ctx.db.get(args.tailoredResumeId); | ||
| }, | ||
| }); | ||
|
|
||
| export const deleteTailoredResume = mutation({ | ||
| args: { tailoredResumeId: v.id("tailoredResumes") }, | ||
| handler: async (ctx, args) => { | ||
| await ctx.db.delete(args.tailoredResumeId); | ||
| return { success: true }; | ||
| }, | ||
| }); | ||
|
|
||
| export const getUserTailoredResumes = query({ | ||
| args: { | ||
| userId: v.string(), | ||
| limit: v.optional(v.number()), | ||
| }, | ||
| handler: async (ctx, args) => { | ||
| const limit = args.limit ?? 20; | ||
| const docs = await ctx.db | ||
| .query("tailoredResumes") | ||
| .withIndex("by_userId", (q) => q.eq("userId", args.userId)) | ||
| .order("desc") | ||
| .take(limit); | ||
| return docs; | ||
| }, | ||
| }); | ||
|
|
||
| export const getTailoredResumeVersionsBySlug = query({ | ||
| args: { | ||
| userId: v.string(), | ||
| builderSlug: v.string(), | ||
| limit: v.optional(v.number()), | ||
| }, | ||
| handler: async (ctx, args) => { | ||
| const limit = args.limit ?? 30; | ||
| const docs = await ctx.db | ||
| .query("tailoredResumes") | ||
| .withIndex("by_userId_builderSlug", (q) => | ||
| q.eq("userId", args.userId) | ||
| ) | ||
| .filter((q) => q.eq(q.field("builderSlug"), args.builderSlug)) | ||
| .order("desc") | ||
| .take(limit); | ||
| return docs; | ||
| }, | ||
| }); | ||
|
Comment on lines
+330
to
+348
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find the by_userId_builderSlug index definition
rg -n 'by_userId_builderSlug' --type=ts -C 5Repository: Aditya190803/Application-Tracking-System Length of output: 1201 🏁 Script executed: #!/bin/bash
# Look for index definitions in the schema/database setup
fd . -type f -name "*.ts" | head -20
git ls-files | grep -E "(schema|index|table)" | head -20Repository: Aditya190803/Application-Tracking-System Length of output: 353 🏁 Script executed: #!/bin/bash
# Find other withIndex usages to see the pattern
rg -n 'withIndex' --type=ts -A 3 | head -50Repository: Aditya190803/Application-Tracking-System Length of output: 2518 🏁 Script executed: #!/bin/bash
# Search for examples of multiple .eq() chaining in the codebase
rg -n '\.eq\(' --type=ts -B 1 -A 1 | grep -A 2 "\.eq" | head -40Repository: Aditya190803/Application-Tracking-System Length of output: 2121 🏁 Script executed: #!/bin/bash
# Look for the complete context around lines 126-130 to understand the .and() pattern
sed -n '124,131p' convex/functions.tsRepository: Aditya190803/Application-Tracking-System Length of output: 441 🏁 Script executed: #!/bin/bash
# Search for .and() usage with compound index or withIndex
rg -n '\.and\(' convex/functions.ts -B 2 -A 2 | head -60Repository: Aditya190803/Application-Tracking-System Length of output: 775 🏁 Script executed: #!/bin/bash
# Search for any examples of withIndex with multiple conditions
rg -n 'withIndex.*\(.*q\s*=>' convex/functions.ts -A 5 | head -80Repository: Aditya190803/Application-Tracking-System Length of output: 66 🏁 Script executed: #!/bin/bash
# Check if there's any documentation or test files showing Convex API usage
fd . -type f \( -name "*.md" -o -name "*.json" \) | xargs grep -l "withIndex" 2>/dev/nullRepository: Aditya190803/Application-Tracking-System Length of output: 256 🏁 Script executed: #!/bin/bash
# Search for documentation in package.json or convex config
cat convex.json 2>/dev/null || echo "No convex.json"
cat package.json | grep -A 5 "convex" || echo "No convex in package.json"Repository: Aditya190803/Application-Tracking-System Length of output: 451 🏁 Script executed: #!/bin/bash
# Look at the complete function to understand the context better
sed -n '330,350p' convex/functions.tsRepository: Aditya190803/Application-Tracking-System Length of output: 752 🏁 Script executed: #!/bin/bash
# Check if there are any README files or documentation
find . -name "README*" -o -name "*.md" | head -10Repository: Aditya190803/Application-Tracking-System Length of output: 731 🏁 Script executed: #!/bin/bash
# Search online for Convex compound index documentation pattern
# Let's also check the actual Convex version to understand capabilities
cat package.json | grep '"convex"'Repository: Aditya190803/Application-Tracking-System Length of output: 108 🌐 Web query:
💡 Result: In Convex, compound (multi-field) indexes are queried by chaining comparisons inside 1) Define a compound indexdefineTable({
teamId: v.id("teams"),
status: v.string(),
}).index("by_teamId_status", ["teamId", "status"]);(Example pattern shown in Convex guidance.) [1] 2) Query it with multiple
|
||
|
|
||
| // ─── Stats ─────────────────────────────────────────────────────────── | ||
|
|
||
| export const getUserStats = query({ | ||
|
|
@@ -320,6 +422,12 @@ export const getSearchHistory = query({ | |
| .order("desc") | ||
| .take(limit * 2); | ||
|
|
||
| const tailoredResumesRaw = await ctx.db | ||
| .query("tailoredResumes") | ||
| .withIndex("by_userId", (q) => q.eq("userId", args.userId)) | ||
| .order("desc") | ||
| .take(limit * 2); | ||
|
|
||
| const analyses = cursorTime | ||
| ? analysesRaw.filter((doc) => doc._creationTime < cursorTime) | ||
| : analysesRaw; | ||
|
|
@@ -328,6 +436,10 @@ export const getSearchHistory = query({ | |
| ? coverLettersRaw.filter((doc) => doc._creationTime < cursorTime) | ||
| : coverLettersRaw; | ||
|
|
||
| const tailoredResumes = cursorTime | ||
| ? tailoredResumesRaw.filter((doc) => doc._creationTime < cursorTime) | ||
| : tailoredResumesRaw; | ||
|
|
||
| const history = [ | ||
| ...analyses.map((doc) => ({ | ||
| id: doc._id, | ||
|
|
@@ -349,6 +461,19 @@ export const getSearchHistory = query({ | |
| createdAt: new Date(doc._creationTime).toISOString(), | ||
| result: doc.result, | ||
| })), | ||
| ...tailoredResumes.map((doc) => ({ | ||
| id: doc._id, | ||
| type: "resume" as const, | ||
| companyName: doc.companyName, | ||
| resumeName: doc.resumeName, | ||
| jobTitle: doc.jobTitle, | ||
| jobDescription: doc.jobDescription, | ||
| templateId: doc.templateId, | ||
| builderSlug: doc.builderSlug, | ||
| version: doc.version, | ||
| createdAt: new Date(doc._creationTime).toISOString(), | ||
| result: doc.latexSource, | ||
| })), | ||
| ]; | ||
|
|
||
| history.sort( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: Aditya190803/Application-Tracking-System
Length of output: 100
🏁 Script executed:
Repository: Aditya190803/Application-Tracking-System
Length of output: 5460
🏁 Script executed:
Repository: Aditya190803/Application-Tracking-System
Length of output: 1887
getTailoredResumeperforms a full table scan — use theby_lookupindex instead.The schema defines a
by_lookupindex on[userId, resumeHash, jobDescriptionHash, templateId], but this query uses.filter()which scans the entire table. For any non-trivial data volume this will degrade performance.⚡ Proposed fix
handler: async (ctx, args) => { const doc = await ctx.db .query("tailoredResumes") - .filter((q) => - q.and( - q.eq(q.field("userId"), args.userId), - q.eq(q.field("resumeHash"), args.resumeHash), - q.eq(q.field("jobDescriptionHash"), args.jobDescriptionHash), - q.eq(q.field("templateId"), args.templateId), - ) - ) + .withIndex("by_lookup", (q) => + q.eq("userId", args.userId) + .eq("resumeHash", args.resumeHash) + .eq("jobDescriptionHash", args.jobDescriptionHash) + .eq("templateId", args.templateId) + ) .order("desc") .first(); return doc; },🤖 Prompt for AI Agents