kstulgys
kstulgys5mo ago

Why sometime ents one-to-one with optional: true work, sometimes not?

so my schema is this:
wallets: defineEnt({
privateKey: v.string(),
})
.edge("balance", { optional: true })
.edge("user")
.edge("store"),

balances: defineEnt({
balance: v.any() as BalanceMap,
})
.edge("wallet")
.edge("store")
.edge("user"),
wallets: defineEnt({
privateKey: v.string(),
})
.edge("balance", { optional: true })
.edge("user")
.edge("store"),

balances: defineEnt({
balance: v.any() as BalanceMap,
})
.edge("wallet")
.edge("store")
.edge("user"),
I first create wallet then I create balance
// create wallet
const walletId = await ctx.db.insert("wallets", {
privateKey: wallet.privateKey,
userId: ctx.userId,
storeId,
});

// create balance
const balanceId = await ctx.db.insert("balances", {
walletId,
balance: initialBalance,
userId: ctx.userId,
storeId,
});

// connect balance to the wallet
await ctx.db.patch(walletId, { balanceId });

// ❌ this is where ts and console is not happy. no balanceId field on wallet table, but walletId field on balance seem to be fine
// create wallet
const walletId = await ctx.db.insert("wallets", {
privateKey: wallet.privateKey,
userId: ctx.userId,
storeId,
});

// create balance
const balanceId = await ctx.db.insert("balances", {
walletId,
balance: initialBalance,
userId: ctx.userId,
storeId,
});

// connect balance to the wallet
await ctx.db.patch(walletId, { balanceId });

// ❌ this is where ts and console is not happy. no balanceId field on wallet table, but walletId field on balance seem to be fine
In other cases I also use this approach with optional: true and it seem to work fine
17 Replies
Michal Srb
Michal Srb5mo ago
The field storing the edge is on the required end of the edge. So in your case it's walletId stored on balances.
kstulgys
kstulgysOP5mo ago
ah i thought it will store Ids on both ends. If I want relation on both ends I should use: in balances table: walletId: Id<"wallets"> in wallet table: balanceId: Id<"balances"> right? these are indexed automatically right? The reason I need this is because I want to query/get one when I know the other and vice versa is that the right path to think about this case?
Michal Srb
Michal Srb5mo ago
The reason I need this is because I want to query/get one
You don't need to store the IDs in both tables. You can traverse the edge from either side: https://labs.convex.dev/convex-ents/read#traversing-11-edge
Reading Ents from the Database - Convex Ents
Relations, default values, unique fields and more for Convex
Michal Srb
Michal Srb5mo ago
And you couldn't make both IDs required, since you have to create one of the documents first.
kstulgys
kstulgysOP5mo ago
I use regular querying so this should work right?
// defining tables

wallets: defineEnt({
privateKey: v.string(),
})
.edge("balance", { optional: true })
.edge("user")
.edge("store"),

balances: defineEnt({
balance: v.any() as BalanceMap,
})
.edge("wallet")
.edge("store")
.edge("user")

// querying

const wallet = await ctx.db
.query("wallets")
.withIndex("balanceId", (q) => q.eq("balanceId", balanceId))
.unique();

const balance = await ctx.db
.query("balances")
.withIndex("walletId", (q) => q.eq("walletId", walletId))
.unique();
// defining tables

wallets: defineEnt({
privateKey: v.string(),
})
.edge("balance", { optional: true })
.edge("user")
.edge("store"),

balances: defineEnt({
balance: v.any() as BalanceMap,
})
.edge("wallet")
.edge("store")
.edge("user")

// querying

const wallet = await ctx.db
.query("wallets")
.withIndex("balanceId", (q) => q.eq("balanceId", balanceId))
.unique();

const balance = await ctx.db
.query("balances")
.withIndex("walletId", (q) => q.eq("walletId", walletId))
.unique();
Michal Srb
Michal Srb5mo ago
const wallet = await ctx.db.get((await ctx.db.get(balanceId)).walletId);
const balance = await ctx.db
.query("balances")
.withIndex("walletId", (q) => q.eq("walletId", walletId))
.unique();
const wallet = await ctx.db.get((await ctx.db.get(balanceId)).walletId);
const balance = await ctx.db
.query("balances")
.withIndex("walletId", (q) => q.eq("walletId", walletId))
.unique();
kstulgys
kstulgysOP5mo ago
Is this an alternative or essentially only this will work?
Michal Srb
Michal Srb5mo ago
Only this will work with the way ents model the edges. But you can add the extra field in if you want. But then you're on the hook on keeping the fields in-sync (writes). I'm curious how/why you're using the Ents schema but not ctx.table?
kstulgys
kstulgysOP5mo ago
queries like ctx.table... requires additional setup and I need to maintain that setup, for now regular queries and mutations works just fine,
Michal Srb
Michal Srb5mo ago
Interesting, so you find the schema valuable enough on its own (mainly for edges?)? You won't get typechecked reads for many:many edges.
kstulgys
kstulgysOP5mo ago
Yeah, I think ents makes it nice to define schema. I've been reading some ents docs and actually thinking to migrate to ctx.table... thing since it brings additional benefits. Any chance to include these helpers (functions) in convex-helpers package?
Michal Srb
Michal Srb5mo ago
Which helpers would you like to see? There are some relationship helpers already in convex-helpers.
kstulgys
kstulgysOP5mo ago
functions are required to declaredin functions.ts file to use ents querying, mutations and actions
Michal Srb
Michal Srb5mo ago
@kstulgys I don't understand. The functions.ts file name is just a suggested convention, you can put custom functions wherever you like.
kstulgys
kstulgysOP5mo ago
those functions that are inside functions.ts... I would like to import these from convex-helpers rather than maintaining it myself.
ian
ian5mo ago
I suspect you'll find that you'll end up wanting to put more things in those functions - or making more versions of them, e.g. one that automatically checks that the logged in user is an admin. The trick with having a helpers version would be that they need to be typed to your schema, so you'd still need to define them somewhere that you can add type parameters. e.g. const { query, mutation, ...} = entFunctions(schema)
kstulgys
kstulgysOP5mo ago
make sense ,thank you

Did you find this page helpful?