Skip to content

Commit 13cadf9

Browse files
committed
add script.js and make other changes
1 parent f5980c7 commit 13cadf9

File tree

6 files changed

+392
-97
lines changed

6 files changed

+392
-97
lines changed

paradedb/sample-movie-search/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Dependencies
22
node_modules/
3+
lambda/package-lock.json
34

45
# Build output
56
dist/
67
cdk.out/
7-
*.js
88
*.d.ts
99

1010
# Keep TypeScript source

paradedb/sample-movie-search/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ help:
1212
@echo " make deploy - Deploy CDK stack to LocalStack"
1313
@echo " make init - Initialize database schema and BM25 index"
1414
@echo " make seed - Load movie data from S3 into ParadeDB"
15+
@echo " make test-search - Test the search endpoint"
16+
@echo " make get-api-url - Get the API Gateway URL"
1517
@echo " make web-ui - Run the Web UI on localhost port 3000"
1618
@echo " make destroy - Tear down the stack"
1719
@echo " make clean - Remove build artifacts"

paradedb/sample-movie-search/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,19 +117,19 @@ make seed
117117

118118
```bash
119119
# Basic search
120-
curl "https://movie-search-api.execute-api.localhost.localstack.cloud:4566/dev/search?q=redemption"
120+
curl "http://movie-search-api.execute-api.localhost.localstack.cloud:4566/dev/search?q=redemption"
121121

122122
# With pagination
123-
curl "https://movie-search-api.execute-api.localhost.localstack.cloud:4566/dev/search?q=dark%20knight&limit=5&offset=0"
123+
curl "http://movie-search-api.execute-api.localhost.localstack.cloud:4566/dev/search?q=dark%20knight&limit=5&offset=0"
124124

125125
# Fuzzy search (handles typos)
126-
curl "https://movie-search-api.execute-api.localhost.localstack.cloud:4566/dev/search?q=godfater"
126+
curl "http://movie-search-api.execute-api.localhost.localstack.cloud:4566/dev/search?q=godfater"
127127
```
128128

129129
### Get Movie Details
130130

131131
```bash
132-
curl "https://movie-search-api.execute-api.localhost.localstack.cloud:4566/dev/movies/tt0111161"
132+
curl "http://movie-search-api.execute-api.localhost.localstack.cloud:4566/dev/movies/tt0111161"
133133
```
134134

135135
### Example Response

paradedb/sample-movie-search/lambda/index.ts

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@ interface Movie {
1919

2020
const pool = new Pool({
2121
host: process.env.PARADEDB_HOST || "paradedb.localhost.localstack.cloud",
22-
port: parseInt(process.env.PARADEDB_PORT || "5432"),
23-
database: process.env.PARADEDB_DATABASE || "postgres",
24-
user: process.env.PARADEDB_USER || "postgres",
25-
password: process.env.PARADEDB_PASSWORD || "postgres",
22+
port: parseInt(process.env.PARADEDB_PORT || "4566"),
23+
database: process.env.PARADEDB_DATABASE || "mydatabase",
24+
user: process.env.PARADEDB_USER || "myuser",
25+
password: process.env.PARADEDB_PASSWORD || "mypassword",
2626
});
2727

28+
const S3_ENDPOINT = process.env.S3_ENDPOINT || "http://s3.localhost.localstack.cloud:4566";
29+
const DATA_BUCKET = process.env.DATA_BUCKET || "movie-search-data";
30+
2831
const s3Client = new S3Client({
29-
endpoint: "http://s3.localhost.localstack.cloud:4566",
32+
endpoint: S3_ENDPOINT,
3033
region: "us-east-1",
3134
forcePathStyle: true,
3235
credentials: {
@@ -35,8 +38,6 @@ const s3Client = new S3Client({
3538
},
3639
});
3740

38-
const DATA_BUCKET = process.env.DATA_BUCKET || "movie-search-data";
39-
4041
function successResponse(data: unknown): APIGatewayProxyResult {
4142
return {
4243
statusCode: 200,
@@ -266,34 +267,26 @@ export async function seedHandler(): Promise<APIGatewayProxyResult> {
266267

267268
console.log(`Parsed ${movies.length} movies from bulk file`);
268269

269-
await client.query("DELETE FROM movies");
270+
await client.query("BEGIN");
270271

271-
let inserted = 0;
272-
const batchSize = 100;
272+
try {
273+
await client.query("DELETE FROM movies");
273274

274-
for (let i = 0; i < movies.length; i += batchSize) {
275-
const batch = movies.slice(i, i + batchSize);
275+
const batchSize = 100;
276+
let inserted = 0;
276277

277-
for (const movie of batch) {
278-
await client.query(
279-
`
280-
INSERT INTO movies (id, title, year, genres, rating, directors, actors, plot,
281-
image_url, release_date, rank, running_time_secs)
282-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
283-
ON CONFLICT (id) DO UPDATE SET
284-
title = EXCLUDED.title,
285-
year = EXCLUDED.year,
286-
genres = EXCLUDED.genres,
287-
rating = EXCLUDED.rating,
288-
directors = EXCLUDED.directors,
289-
actors = EXCLUDED.actors,
290-
plot = EXCLUDED.plot,
291-
image_url = EXCLUDED.image_url,
292-
release_date = EXCLUDED.release_date,
293-
rank = EXCLUDED.rank,
294-
running_time_secs = EXCLUDED.running_time_secs
295-
`,
296-
[
278+
for (let i = 0; i < movies.length; i += batchSize) {
279+
const batch = movies.slice(i, i + batchSize);
280+
281+
const values: unknown[] = [];
282+
const placeholders: string[] = [];
283+
284+
batch.forEach((movie, idx) => {
285+
const offset = idx * 12;
286+
placeholders.push(
287+
`($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}, $${offset + 6}, $${offset + 7}, $${offset + 8}, $${offset + 9}, $${offset + 10}, $${offset + 11}, $${offset + 12})`
288+
);
289+
values.push(
297290
movie.id,
298291
movie.title,
299292
movie.year || null,
@@ -305,21 +298,45 @@ export async function seedHandler(): Promise<APIGatewayProxyResult> {
305298
movie.image_url || null,
306299
movie.release_date || null,
307300
movie.rank || null,
308-
movie.running_time_secs || null,
309-
]
301+
movie.running_time_secs || null
302+
);
303+
});
304+
305+
await client.query(
306+
`INSERT INTO movies (id, title, year, genres, rating, directors, actors, plot,
307+
image_url, release_date, rank, running_time_secs)
308+
VALUES ${placeholders.join(", ")}
309+
ON CONFLICT (id) DO UPDATE SET
310+
title = EXCLUDED.title,
311+
year = EXCLUDED.year,
312+
genres = EXCLUDED.genres,
313+
rating = EXCLUDED.rating,
314+
directors = EXCLUDED.directors,
315+
actors = EXCLUDED.actors,
316+
plot = EXCLUDED.plot,
317+
image_url = EXCLUDED.image_url,
318+
release_date = EXCLUDED.release_date,
319+
rank = EXCLUDED.rank,
320+
running_time_secs = EXCLUDED.running_time_secs`,
321+
values
310322
);
311-
inserted++;
323+
324+
inserted += batch.length;
325+
console.log(`Inserted ${inserted}/${movies.length} movies...`);
312326
}
313327

314-
console.log(`Inserted ${inserted}/${movies.length} movies...`);
315-
}
328+
await client.query("COMMIT");
316329

317-
console.log(`Seeding complete: ${inserted} movies`);
330+
console.log(`Seeding complete: ${inserted} movies`);
318331

319-
return successResponse({
320-
message: "Data seeded successfully",
321-
count: inserted,
322-
});
332+
return successResponse({
333+
message: "Data seeded successfully",
334+
count: inserted,
335+
});
336+
} catch (error) {
337+
await client.query("ROLLBACK");
338+
throw error;
339+
}
323340
} catch (error) {
324341
console.error("Seed error:", error);
325342
return errorResponse(500, "Seeding failed");

paradedb/sample-movie-search/lib/movie-search-stack.ts

Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -21,92 +21,91 @@ export class MovieSearchStack extends cdk.Stack {
2121
destinationBucket: dataBucket,
2222
});
2323

24-
const paradeDbEnv = {
24+
const lambdaEnv = {
2525
PARADEDB_HOST: "paradedb.localhost.localstack.cloud",
2626
PARADEDB_PORT: "4566",
2727
PARADEDB_DATABASE: "mydatabase",
2828
PARADEDB_USER: "myuser",
2929
PARADEDB_PASSWORD: "mypassword",
3030
DATA_BUCKET: dataBucket.bucketName,
31+
S3_ENDPOINT: "http://s3.localhost.localstack.cloud:4566",
3132
};
3233

3334
const lambdaDir = path.join(__dirname, "../lambda");
34-
const getLambdaCode = () => {
35-
return lambda.Code.fromAsset(lambdaDir, {
36-
bundling: {
37-
image: lambda.Runtime.NODEJS_22_X.bundlingImage,
38-
command: [
39-
"bash",
40-
"-c",
41-
[
42-
"npm install --prefix /asset-input",
43-
"npx esbuild /asset-input/index.ts --bundle --platform=node --target=node22 --outfile=/asset-output/index.js",
44-
].join(" && "),
45-
],
46-
local: {
47-
tryBundle(outputDir: string) {
48-
const { spawnSync } = require("child_process");
49-
try {
50-
const npmResult = spawnSync("npm", ["install"], {
51-
cwd: lambdaDir,
52-
stdio: "inherit",
53-
});
54-
if (npmResult.status !== 0) return false;
55-
56-
const esbuildResult = spawnSync(
57-
"npx",
58-
[
59-
"esbuild",
60-
"index.ts",
61-
"--bundle",
62-
"--platform=node",
63-
"--target=node22",
64-
`--outfile=${outputDir}/index.js`,
65-
],
66-
{ cwd: lambdaDir, stdio: "inherit" }
67-
);
68-
return esbuildResult.status === 0;
69-
} catch {
70-
return false;
71-
}
72-
},
35+
const lambdaCode = lambda.Code.fromAsset(lambdaDir, {
36+
bundling: {
37+
image: lambda.Runtime.NODEJS_22_X.bundlingImage,
38+
command: [
39+
"bash",
40+
"-c",
41+
[
42+
"npm install --prefix /asset-input",
43+
"npx esbuild /asset-input/index.ts --bundle --platform=node --target=node22 --outfile=/asset-output/index.js",
44+
].join(" && "),
45+
],
46+
local: {
47+
tryBundle(outputDir: string) {
48+
const { spawnSync } = require("child_process");
49+
try {
50+
const npmResult = spawnSync("npm", ["install"], {
51+
cwd: lambdaDir,
52+
stdio: "inherit",
53+
});
54+
if (npmResult.status !== 0) return false;
55+
56+
const esbuildResult = spawnSync(
57+
"npx",
58+
[
59+
"esbuild",
60+
"index.ts",
61+
"--bundle",
62+
"--platform=node",
63+
"--target=node22",
64+
`--outfile=${outputDir}/index.js`,
65+
],
66+
{ cwd: lambdaDir, stdio: "inherit" }
67+
);
68+
return esbuildResult.status === 0;
69+
} catch {
70+
return false;
71+
}
7372
},
7473
},
75-
});
76-
};
74+
},
75+
});
7776

7877
const searchHandler = new lambda.Function(this, "SearchHandler", {
7978
runtime: lambda.Runtime.NODEJS_22_X,
8079
handler: "index.searchHandler",
81-
code: getLambdaCode(),
82-
environment: paradeDbEnv,
80+
code: lambdaCode,
81+
environment: lambdaEnv,
8382
timeout: cdk.Duration.seconds(30),
8483
memorySize: 256,
8584
});
8685

8786
const movieDetailHandler = new lambda.Function(this, "MovieDetailHandler", {
8887
runtime: lambda.Runtime.NODEJS_22_X,
8988
handler: "index.movieDetailHandler",
90-
code: getLambdaCode(),
91-
environment: paradeDbEnv,
89+
code: lambdaCode,
90+
environment: lambdaEnv,
9291
timeout: cdk.Duration.seconds(30),
9392
memorySize: 256,
9493
});
9594

9695
const initHandler = new lambda.Function(this, "InitHandler", {
9796
runtime: lambda.Runtime.NODEJS_22_X,
9897
handler: "index.initHandler",
99-
code: getLambdaCode(),
100-
environment: paradeDbEnv,
98+
code: lambdaCode,
99+
environment: lambdaEnv,
101100
timeout: cdk.Duration.seconds(60),
102101
memorySize: 256,
103102
});
104103

105104
const seedHandler = new lambda.Function(this, "SeedHandler", {
106105
runtime: lambda.Runtime.NODEJS_22_X,
107106
handler: "index.seedHandler",
108-
code: getLambdaCode(),
109-
environment: paradeDbEnv,
107+
code: lambdaCode,
108+
environment: lambdaEnv,
110109
timeout: cdk.Duration.minutes(10),
111110
memorySize: 1024,
112111
});

0 commit comments

Comments
 (0)