hasanaktasTR
hasanaktasTR
CCConvex Community
Created by ampp on 11/14/2024 in #support-community
Multiple monorepo app deployments using the same convex backend?
@FleetAdmiralJakob πŸ—• πŸ—— πŸ—™ 1) You can keep the current app version in your database and serve it with an API end. (You can keep it separately for iOS, Android and Web.) 2) If a change is made that will break the app, you should publish the app (web or mobile) first. 3) Then, you should publish the backend. 4) You should upgrade the app version in your database to the version you published. 5) If the version installed on the user's phone does not match the version coming from the API when the mobile app is opened or according to your scenario, you can show the forced update screen. If your backend does not contain any breaking changes, you do not need to force the user to update. You can publish the backend directly. These scenarios are valid for any backend in general, not just for convex, In short, Have an API that will not change and query whether it can be forcefully updated through this API.
9 replies
CCConvex Community
Created by ampp on 11/14/2024 in #support-community
Multiple monorepo app deployments using the same convex backend?
@wild-silent In mobile applications, in any breakdown change on the backend side, you must first publish the application and then publish the backend. (Convex or any backend) In such scenarios, you should add the forced update logic to your application. When the user application is first opened, you can check the application version from the backend and show the forced update screen accordingly. We use this scenario in the company we work with.
9 replies
CCConvex Community
Created by Matt Luo on 11/17/2024 in #support-community
Does Vercel charge bandwidth for Convex images?
In Nextjs, images are optimized by default and served by itself. Therefore, you will be charged additionally in Vercel. You should use unoptimized prop for image component or set unoptimized to true globally in the next config file. The most logical scenario is to serve images via convex from the http api and cache them via cloudflare. This way, you can significantly reduce costs and quickly access images from all over the world. You should keep in mind that in Convex, every storage request costs money. So if you are going to have a lot of image access, you should use an intermediate solution like cloudflare. Convex team was working on a cdn but I don't know if anything has been developed yet.
4 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
As the user reacts, I manually delete it from the screen and write it to the reaction table. If I used websocket, it would send a new request after each deletion. That's why I send an http request. When the list is finished, the reaction table is up to date, so the new list comes correctly.
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
I tried to show the scenario in the video. New profile requests are not made until 20 profile reactions are given. After 20 profile reactions are given, new users are pulled, these new profiles are selected from the group that has not been reacted
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
I send a request to the client and pull 20 profiles (not websocket, useConvex().query) Then, after the user gives a reaction to all 20, I send a request to the same service again and bring 20 new profiles.
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
@Hmza Are you sure about pagination? If you have examined the sample code, I am trying to bring a maximum of 20 profiles. If you are saying to send 37 requests on the client side, it is very difficult to stay under the cost of this. Can you explain in more detail?
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
reactions and profiles are kept in 2 separate tables. In each API request I need to fetch other 20 profiles for which the user has not received reactions. The flow is as follows: Get reactions of the user making the request from the database. If the user making the request has an h3Index - Create a certain number of other h3indexes around it. Make a separate database query request for each h3. I will query up to 37 adjacent h3Indexes. 1 hexagon 7 hexagon (1+6) 19 hexagon (1+6+12) 37 hexagon (1+6+12+18) Stop the request when the request results are 20. --------------- if the number of users found does not reach 20 - ​​Fetch 20 internal users. As I said, my problem is filtering out users who have not received reactions when sending requests to these tables. It's fine for small numbers of users, but I started getting errors for high-profile numbers like the ones I simulated above. Above are simple table definitions and my query. I tried to make the code as clean as possible for clarity. ------- Brief summary If it finds 20 people in the first queries, there is no problem. If it cannot find them and tries to make 37 queries in order, it gives an error. If I remove the notIn filter, the problem is solved. Function execution timed out (maximum duration: 1s)
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
Query
export const getExploreAccountsQuery = protectedProfileQuery({
args: {
gender: gender,
ageRange: ageRange,
},
handler: async (ctx, args) => {
const currentYear = new Date().getFullYear();
const minAge = args.ageRange[0];
const maxAge = args.ageRange[1];

const reactions = await ctx.db
.query("reactions")
.withIndex("by_userId", (q) => q.eq("userId", ctx.profile.userId))
.collect();

const notInUserIds = [
ctx.profile.userId,
...reactions.map((reaction) => reaction.targetUserId),
];

let profiles: Doc<"profiles">[] = [];



if (ctx.profile.h3Index) {
const h3Indexes = gridDiskDistances(
ctx.profile.h3Index,
3,
).flat();

for (const h3Index of h3Indexes) {
const result = await getProfilesByH3Index(h3Index);
profiles = [...profiles, ...result];
if (profiles.length >= 20) {
break;
}
}
}

if (profiles.length < 20) {
const result = await getInternalProfiles();

profiles = [...profiles, ...result];
}

const accounts = profiles.map((profile) => {
return {
profile,
};
});

return accounts;
},
});
export const getExploreAccountsQuery = protectedProfileQuery({
args: {
gender: gender,
ageRange: ageRange,
},
handler: async (ctx, args) => {
const currentYear = new Date().getFullYear();
const minAge = args.ageRange[0];
const maxAge = args.ageRange[1];

const reactions = await ctx.db
.query("reactions")
.withIndex("by_userId", (q) => q.eq("userId", ctx.profile.userId))
.collect();

const notInUserIds = [
ctx.profile.userId,
...reactions.map((reaction) => reaction.targetUserId),
];

let profiles: Doc<"profiles">[] = [];



if (ctx.profile.h3Index) {
const h3Indexes = gridDiskDistances(
ctx.profile.h3Index,
3,
).flat();

for (const h3Index of h3Indexes) {
const result = await getProfilesByH3Index(h3Index);
profiles = [...profiles, ...result];
if (profiles.length >= 20) {
break;
}
}
}

if (profiles.length < 20) {
const result = await getInternalProfiles();

profiles = [...profiles, ...result];
}

const accounts = profiles.map((profile) => {
return {
profile,
};
});

return accounts;
},
});
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
helpers
const getProfilesByH3Index = async (h3Index: string) => {
const profiles = await ctx.db
.query("profiles")
.withIndex("by_isInternal_h3Index_gender_birthDate", (q) => {
let filter;

filter = q
.eq("isInternal", false)
.eq("h3Index", h3Index)
.eq("gender", args.gender);
if (maxAge !== 60) {
filter = filter.gte(
"birthDate",
formatISO(new Date(currentYear - maxAge, 0, 1)),
);
}

if (minAge !== 18) {
filter = filter.lte(
"birthDate",
formatISO(new Date(currentYear - minAge, 0, 1)),
);
}

return filter;
})
.filter((q) =>
q.and(...notInUserIds.map((id) => q.neq(q.field("userId"), id))),
)

.take(20);

return profiles;
};

const getInternalProfiles = async () => {
const internalProfiles = await ctx.db
.query("profiles")
.withIndex("by_isInternal_gender_birthDate", (q) => {
let filter;

filter = q.eq("isInternal", true).eq("gender", args.gender);

if (maxAge !== 60) {
filter = filter.gte(
"birthDate",
formatISO(new Date(currentYear - maxAge, 0, 1)),
);
}

if (minAge !== 18) {
filter = filter.lte(
"birthDate",
formatISO(new Date(currentYear - minAge, 0, 1)),
);
}

return filter;
})
.filter((q) =>
q.and(...notInUserIds.map((id) => q.neq(q.field("userId"), id))),
)

.take(20);

return internalProfiles;
};
const getProfilesByH3Index = async (h3Index: string) => {
const profiles = await ctx.db
.query("profiles")
.withIndex("by_isInternal_h3Index_gender_birthDate", (q) => {
let filter;

filter = q
.eq("isInternal", false)
.eq("h3Index", h3Index)
.eq("gender", args.gender);
if (maxAge !== 60) {
filter = filter.gte(
"birthDate",
formatISO(new Date(currentYear - maxAge, 0, 1)),
);
}

if (minAge !== 18) {
filter = filter.lte(
"birthDate",
formatISO(new Date(currentYear - minAge, 0, 1)),
);
}

return filter;
})
.filter((q) =>
q.and(...notInUserIds.map((id) => q.neq(q.field("userId"), id))),
)

.take(20);

return profiles;
};

const getInternalProfiles = async () => {
const internalProfiles = await ctx.db
.query("profiles")
.withIndex("by_isInternal_gender_birthDate", (q) => {
let filter;

filter = q.eq("isInternal", true).eq("gender", args.gender);

if (maxAge !== 60) {
filter = filter.gte(
"birthDate",
formatISO(new Date(currentYear - maxAge, 0, 1)),
);
}

if (minAge !== 18) {
filter = filter.lte(
"birthDate",
formatISO(new Date(currentYear - minAge, 0, 1)),
);
}

return filter;
})
.filter((q) =>
q.and(...notInUserIds.map((id) => q.neq(q.field("userId"), id))),
)

.take(20);

return internalProfiles;
};
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
Schema
const reactions = defineTable({
userId: v.id("users"),
targetUserId: v.id("users"),

type: v.union(
v.literal("like"),
v.literal("dislike"),
),
})
.index("by_userId", ["userId"])
.index("by_targetUserId_userId", ["targetUserId", "userId"]);


const profiles = defineTable({
userId: v.id("users"),
gender: v.union(v.literal("male"), v.literal("female")),
birthDate: v.string(),
name: v.string(),
countryCode: v.optional(v.string()),
isInternal: v.boolean(),
h3Index: v.optional(v.string()),
location: v.optional(
v.object({
latitude: v.number(),
longitude: v.number(),
}),
),
})
.index("by_userId", ["userId"])
.index("by_isInternal_h3Index_gender_birthDate", [
"isInternal",
"h3Index",
"gender",
"birthDate",
])
.index("by_isInternal_gender_birthDate", [
"isInternal",
"gender",
"birthDate",
]);
const reactions = defineTable({
userId: v.id("users"),
targetUserId: v.id("users"),

type: v.union(
v.literal("like"),
v.literal("dislike"),
),
})
.index("by_userId", ["userId"])
.index("by_targetUserId_userId", ["targetUserId", "userId"]);


const profiles = defineTable({
userId: v.id("users"),
gender: v.union(v.literal("male"), v.literal("female")),
birthDate: v.string(),
name: v.string(),
countryCode: v.optional(v.string()),
isInternal: v.boolean(),
h3Index: v.optional(v.string()),
location: v.optional(
v.object({
latitude: v.number(),
longitude: v.number(),
}),
),
})
.index("by_userId", ["userId"])
.index("by_isInternal_h3Index_gender_birthDate", [
"isInternal",
"h3Index",
"gender",
"birthDate",
])
.index("by_isInternal_gender_birthDate", [
"isInternal",
"gender",
"birthDate",
]);
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
No description
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
No description
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
I haven't tested it, but when I use a filter, it will perform a full table search, so the query performance will decrease as the number of profiles increases. But I can prepare for such a scenario. I will test it during the week.
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
The flow works as I want it to. The problem is that as the number of reactions and profiles increases, query performance will decrease significantly with the current structure. I couldn't find a solution. So I wanted to get some ideas.
27 replies
CCConvex Community
Created by hasanaktasTR on 9/10/2024 in #support-community
Big dataset not in filter
export const getExploreAccounts = protectedProfileQuery({
args: {gender: gender, ageRange: ageRange},
handler: async (ctx, args) => {
const currentYear = new Date().getFullYear();

const minAge = args.ageRange[0];

const maxAge = args.ageRange[1];

const reactions = await ctx.db.query("reactions").withIndex("by_userId", (q) => q.eq("userId", ctx.profile.userId)).collect();

const notInUserIds = [ctx.profile.userId,...reactions.map((reaction) => reaction.targetUserId)];

let profiles: Doc<"profiles">[] = [];

const getProfilesByH3Index = async (h3Index: string) => {
return await ctx.db.query("profiles").withIndex("by_h3Index_gender_birthDate", (q) => {
let filter;

filter = q.eq("location.h3Index", h3Index).eq("gender", args.gender);

if (maxAge !== 60) {
filter = filter.gte("birthDate",formatISO(new Date(currentYear - maxAge, 0, 1)))
}

if (minAge !== 18) {
filter = filter.lte("birthDate",formatISO(new Date(currentYear - minAge, 0, 1)))
}

return filter;
})
.filter((q) =>q.and(...notInUserIds.map((id) => q.neq(q.field("userId"), id))))
.take(config.exploreProfileCount);
};
const h3Indexes = gridDiskDistances(ctx.profile.location.h3Index,config.exploreGridDiskSize,).flat();

for (const h3Index of h3Indexes) {
const result = await getProfilesByH3Index(h3Index);
profiles = [...profiles, ...result];
if (profiles.length >= config.exploreProfileCount) {
break;
}
}
return profiles
},
});
export const getExploreAccounts = protectedProfileQuery({
args: {gender: gender, ageRange: ageRange},
handler: async (ctx, args) => {
const currentYear = new Date().getFullYear();

const minAge = args.ageRange[0];

const maxAge = args.ageRange[1];

const reactions = await ctx.db.query("reactions").withIndex("by_userId", (q) => q.eq("userId", ctx.profile.userId)).collect();

const notInUserIds = [ctx.profile.userId,...reactions.map((reaction) => reaction.targetUserId)];

let profiles: Doc<"profiles">[] = [];

const getProfilesByH3Index = async (h3Index: string) => {
return await ctx.db.query("profiles").withIndex("by_h3Index_gender_birthDate", (q) => {
let filter;

filter = q.eq("location.h3Index", h3Index).eq("gender", args.gender);

if (maxAge !== 60) {
filter = filter.gte("birthDate",formatISO(new Date(currentYear - maxAge, 0, 1)))
}

if (minAge !== 18) {
filter = filter.lte("birthDate",formatISO(new Date(currentYear - minAge, 0, 1)))
}

return filter;
})
.filter((q) =>q.and(...notInUserIds.map((id) => q.neq(q.field("userId"), id))))
.take(config.exploreProfileCount);
};
const h3Indexes = gridDiskDistances(ctx.profile.location.h3Index,config.exploreGridDiskSize,).flat();

for (const h3Index of h3Indexes) {
const result = await getProfilesByH3Index(h3Index);
profiles = [...profiles, ...result];
if (profiles.length >= config.exploreProfileCount) {
break;
}
}
return profiles
},
});
27 replies
CCConvex Community
Created by hasanaktasTR on 8/23/2024 in #support-community
Typescript error for @tanstack/react-query useMutation
4 replies
CCConvex Community
Created by hasanaktasTR on 8/1/2024 in #support-community
Typescript error for @tanstack/react-query when extra parameter is added
error fixed @ballingt thanks
26 replies
CCConvex Community
Created by hasanaktasTR on 8/1/2024 in #support-community
Typescript error for @tanstack/react-query when extra parameter is added
You're right Tom. I'm not going to write a custom hook and mess with type safety πŸ™‚
26 replies
CCConvex Community
Created by hasanaktasTR on 8/1/2024 in #support-community
Typescript error for @tanstack/react-query when extra parameter is added
For the second scenario, I can use it as follows, so do I understand correctly?
import { ConvexAuthProvider } from "@convex-dev/auth/react";
import { ConvexQueryClient } from "@convex-dev/react-query";
import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
import { api } from "@uniobb/backend/convex/_generated/api";
import { ConvexReactClient } from "convex/react";
import { FunctionArgs, FunctionReference } from "convex/server";

export const convex = new ConvexReactClient("");
export const convexQueryClient = new ConvexQueryClient(convex);

const queryClient = new QueryClient({});

convexQueryClient.connect(queryClient);

export const Provider = ({ children }: { children: React.ReactNode }) => {
return (
<ConvexAuthProvider client={convex}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</ConvexAuthProvider>
);
};




export const useConvexTanstackQuery=<ConvexQueryReference extends FunctionReference<"query">>(
funcRef: ConvexQueryReference,
queryArgs: FunctionArgs<ConvexQueryReference>,
)=>{
return useQuery(convexQueryClient.queryOptions(funcRef,queryArgs))
}



// Example

useConvexTanstackQuery(api.account.getProfile,{})
import { ConvexAuthProvider } from "@convex-dev/auth/react";
import { ConvexQueryClient } from "@convex-dev/react-query";
import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
import { api } from "@uniobb/backend/convex/_generated/api";
import { ConvexReactClient } from "convex/react";
import { FunctionArgs, FunctionReference } from "convex/server";

export const convex = new ConvexReactClient("");
export const convexQueryClient = new ConvexQueryClient(convex);

const queryClient = new QueryClient({});

convexQueryClient.connect(queryClient);

export const Provider = ({ children }: { children: React.ReactNode }) => {
return (
<ConvexAuthProvider client={convex}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</ConvexAuthProvider>
);
};




export const useConvexTanstackQuery=<ConvexQueryReference extends FunctionReference<"query">>(
funcRef: ConvexQueryReference,
queryArgs: FunctionArgs<ConvexQueryReference>,
)=>{
return useQuery(convexQueryClient.queryOptions(funcRef,queryArgs))
}



// Example

useConvexTanstackQuery(api.account.getProfile,{})
26 replies