generated at
pVectorSearch2023-06-05

NextJS
$ npx create-next-app@latest
TypeScript使いますかとか聞いてくれる
vecsearch-service って名前にした
この名前のフォルダが作られてGit管理される
Githubに入れる
>push an existing repository from the command line
privateにした

Vercel
ダッシュボードにログインする
先ほどのリポジトリが表示されてるので選んでデプロイ
デプロイできた
あー、 https://vecsearch-service.vercel.app/ になるのか
設定からすぐ変えられる
便利な時代だなぁ

APIを作る
src がないときの置き場所は、 <project_root>/pages/api/foo.ts
ts
import { NextApiRequest, NextApiResponse } from "next"; const handle = async (_req: NextApiRequest, res: NextApiResponse) => { res.status(200).json({ data: "hello world" }); }; export default handle;
OK
うまく動かなくて混乱したがGPT-4に聞いたら「サーバを再起動してみろ」と言われたので再起動したら動いた、便利な時代

他のAPIを叩くAPI
とりあえずScrapboxAPIで試す
ts
import { NextApiRequest, NextApiResponse } from "next"; import axios from "axios"; const handle = async (_req: NextApiRequest, res: NextApiResponse) => { const otherApiUrl = "https://scrapbox.io/api/pages/nishio/pVectorSearch2023-06-05"; try { const response = await axios.get(otherApiUrl, {}); res.status(200).json(response.data); } catch (error) { res .status(500) .json({ error: "An error occurred while calling the other API" }); } }; export default handle;
OK

OpenAIを叩く
embed.ts
import axios from "axios"; export const embed = async (textToEmbed: string) => { const model = "text-embedding-ada-002"; const URL = "https://api.openai.com/v1/embeddings"; // Call OpenAI Embedding API const openAIResponse = await axios.post( URL, { input: textToEmbed, model: model }, { headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json", }, } ); const openAIEmbedding = openAIResponse.data.data[0].embedding; // assuming the embedding is in this path return openAIEmbedding; };
embed.ts
import { NextApiRequest, NextApiResponse } from "next"; import { embed } from "../../utils/embed"; const handle = async (req: NextApiRequest, res: NextApiResponse) => { const textToEmbed = req.body.text; const openAIEmbedding = await embed(textToEmbed); res.status(200).json(openAIEmbedding); }; export default handle;
OK

Qdrantを叩く
search.ts
import { NextApiRequest, NextApiResponse } from "next"; import { embed } from "../../utils/embed"; import { QdrantClient } from "@qdrant/js-client-rest"; const handle = async (req: NextApiRequest, res: NextApiResponse) => { const textToEmbed = req.body.text; const openAIEmbedding = await embed(textToEmbed); const COLLECTION_NAME = process.env.QDRANT_COLLECTION_NAME!; const client = new QdrantClient({ url: process.env.QDRANT_ENDPOINT, apiKey: process.env.QDRANT_API_KEY, }); const grdantResult = await client.search(COLLECTION_NAME, { vector: openAIEmbedding, limit: 3, }); res.status(200).json(grdantResult); }; export default handle;
OK

フォームから検索文字列を取る
:
Error: Event handlers cannot be passed to Client Component props. <button id="search" onClick={function} children=...> ^^^^^^^^^^ If you need interactivity, consider converting part of this to a Client Component.

レスポンスを整形する
ScrapboxのタイトルからURLを作る
見た目はさておき、検索はできた
タイトルをクリックするとScrapboxの該当ページにジャンプする
TailwindCSSが初めてなので見た目をどうするかわからない
ChatGPTに聞こうw
Make the result looks better.って言ったらいい感じにしてくれたw
全体的に直させた
こだわりがないウェブアプリの外見デザインに関してはChatGPTで十分だな…

次回のためのメモ
ページタイトル画像
/api/pages/:projectName/:pageTitle/icon

管理者用の画面
検索クエリは保管して管理者が見れるようにする
そういう旨の説明が書いてあるページを作る
検索した時点で結果をFirebaseに入れてパーマリンクを出すか
公開する
自分でスマホから使ってみたりする
スマホから見たら背景が黒くなかった
検索したときに、検索中なのかエラーで死んだのかわからない ✅fixed
スマホでエラーになる件 ✅fixed
環境変数の設定し忘れ!
.env.localはデプロイ先の環境変数に入らない(そもそも.gitignoreしててリポジトリに入ってない!)
これ厄介だな
例えばScrapbox記法が混じってるとブラウザのテキストには記法がないからヒットしない
真面目にやるならプレーンテキストにする必要がある