将实时全文搜索添加到tigris的next.js应用
#开源 #typescript #database #搜索

实时全文搜索是一项功能,可增强Web应用程序的用户体验,尤其是在在线商店,社交媒体平台,文档和博客中。
它使用户能够搜索并立即获取最新信息。将其与搜索结果更新为用户类型(无直接用户查询提交或页面重新加载)的体验相结合,提供了一个更好的UX,可以帮助用户获得更有效的信息。

在本教程中,我们将带您通过使用Tigris的所有产品进行实时全文搜索。

在下一节中,我们将提供有关实时,全文搜索的背景信息,以及Tigris如何成为两者的推动者。但是,请随时跳到Tutorial遵循分步指南。
或者,如果要潜入代码,请前往real-time full-text search GitHub repo

背景

什么是实时搜索?

实时搜索返回搜索结果基于基础数据库索引,该索引在任何数据库操作后立即更新。
这使用户可以尽快找到相关和最新信息。

越来越多的real-time search use cases
例如,新闻集合器网站出版后立即显示最新标题的新闻网站,并在发布后立即显示出从用户网络中显示新帖子的社交媒体平台。

用例的增加是由用户偏好驱动的;随着越来越多的信息在线生成,实时搜索为用户提供了比以前更快的信息。
结果,许多公司正在合并实时搜索功能,以在其在线应用程序上提供更好的用户体验。

什么是全文搜索?

全文搜索是一种允许用户通过输入查询来搜索数据库或文档集合中的信息的技术:通常是关键字的组合。
全文搜索使用自然语言处理和算法来理解查询的上下文和含义,即使它们与确切的术语不匹配,它们也相关的结果。
此方法通常用于各种应用程序,例如在线数据库,搜索引擎,电子商务网站和企业搜索,以帮助员工找到所需的信息。
全文搜索引擎可以提供诸如文本索引,解析,令牌化,茎,创建倒置索引,排名结果以及提供用于执行搜索的API或Web界面等功能。

Tigris如何帮助实时全文搜索?

Tigris是一个代码优先的开发人员数据平台,将数据库,搜索引擎和同步机制集成到统一和完全管理的平台中,从而易于将实时搜索功能构建到应用程序中。

数据库功能建立在FoundationDB(一个开源的,分布式的,交易的键值商店。
) Tigris旨在与实时Web应用程序和无服务器功能集成。

实时全文搜索可能会给开发人员带来一些挑战,例如并行运行两个系统(数据库和搜索),
站起来并管理同步机制,以将数据库和搜索索引实时保持同步,并随着数据库的发展重新搜索索引。

Tigris通过在多合一开发人员数据平台中提供集成数据库,自动索引和搜索功能来解决这些挑战。
这种集成消除了运行单独的数据库和搜索系统的复杂性,以及需要同步数据库和手动搜索的需要;
Tigris会自动完成所有操作。使用Tigris,开发人员可以使用单个开发人员数据平台在其应用程序中实现实时搜索功能。

已经说服了吗?然后,注册Tigris Cloud。不过,您仍然应该阅读本教程的其余部分!

构建Next.js电子商务产品列表

要与本教程一起,您需要执行以下操作:

获取静态电子商务网站

首先,克隆next.js电子商务应用程序为本教程中的演示创建:

git clone https://github.com/tigrisdata-community/real-time-full-text-search-nextjs

然后将目录更改为项目文件夹,切换到启动器分支,然后安装项目依赖项:

cd real-time-full-text-search-nextjs
git checkout starter && npm install

您现在可以运行应用程序并查看产品的静态清单:

npm run dev

Next.js e-commerce product listing app preview

代码和文件结构看起来很熟悉,因为该应用程序是使用create-next-app命令创建的。
产品信息是在db/products.json中定义的,然后在pages/index.tsx中使用该信息显示产品列表。

现在,让我们更改应用程序以通过Tigris数据库存储和检索产品。

创建一个底格里斯项目

Tigris Cloud dashboard中为您的电子商务商店创建一个Tigris项目。单击创建一个新项目按钮,输入商店作为您的项目名称,然后单击创建按钮继续。

The create a project dialog within the Tigris console web application

选择创建按钮后,将出现一个窗口,并带有一个命令,以踩踏您的新Tigris Project应用程序。但是我们可以忽略这一点。

The create-tigris-app terminal command displayed within a dialog within the Tigris web console

如上所述,我们不需要执行npx create-tigris-app命令。单击转到项目

接下来,从新的tigris项目的应用程序键部分中复制Project NameClient IDClient Secret
然后,在您的首选代码编辑器中打开项目,在项目的根目录中创建一个.env.local文件,然后粘贴其中的凭据:

TIGRIS_URI=api.preview.tigrisdata.cloud
TIGRIS_PROJECT=<REPLACE WITH PROJECT_NAME>
TIGRIS_CLIENT_ID=<REPLACE WITH CLIENT_ID>
TIGRIS_CLIENT_SECRET=<REPLACE WITH CLIENT_SECRET>
TIGRIS_DB_BRANCH="develop"

安装Tigris TypeScript SDK

要将Tigris集成到您的应用程序中,请安装Tigris TypeScript SDK:

npm install @tigrisdata/core

接下来,使用以下配置更新tsconfig.json文件,以指定Typescript编译器在更换项目代码时应使用的选项:

{
  "compilerOptions": {
    ...

    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
  },
  "ts-node": {
    "compilerOptions": {
      "module": "commonjs"
    }
  },
  ...
}

在上面的配置中,我们更新compilerOptions对象,该对象包含几个控制打字稿编译器行为的属性,例如:

  • experimentalDecorators:对TypeScript decorators的实验支持,这是将元数据添加到班级或其成员的一种方式。
  • emitDecoratorMetadata:启用发射设计型元数据,以供源代表装饰的声明。
  • ts-node:这是ts-node package的配置,可让您直接从命令行运行打字稿文件。 module属性设置为commonjs,这意味着模块分辨率设置为使用COMPORJS策略。

创建Tigris数据模型

每个Tigris项目都已经设置了一个数据库,并且以documents的形式存储数据记录。
这些文档类似于JSON对象,并将其组织为称为集合的组。

让我们创建一个tigris模型来为电子商务应用程序定义集合。
在您的电子商务项目的根目录中创建一个db/models文件夹。在该文件夹中,创建一个store.ts文件,然后添加代码:

import {
  Field,
  PrimaryKey,
  TigrisCollection,
  TigrisDataTypes,
} from "@tigrisdata/core";

@TigrisCollection("products")
export class Product {
  @PrimaryKey(TigrisDataTypes.INT32, { order: 1, autoGenerate: true })
  id!: number;

  @Field()
  name: string;

  @Field()
  price: number;

  @Field()
  star: number;

  @Field()
  tag: string;

  @Field()
  image: string;
}

上面的代码在您的电子商务应用程序中定义了products集合的Tigris模型。
它导入了@TigrisCollection Decorator,该装饰器定义了应该通过以该集合作为参数的名称将Product的集合来称为“产品”。
TigrisDataTypes类型可访问Tigris中可用的各种数据类型。
@Field装饰器定义了集合中的字段,@PrimaryKey Decorator指定了集合中每个记录的主要键。

实例化Tigris客户端并获取数据库

让我们初始化tigris客户端并将其导出到应用程序的其他部分,以便使用它。在项目root文件夹中创建lib/tigris.ts文件,并在下面添加代码段:

import { DB, Tigris } from "@tigrisdata/core";

const tigrisClient = new Tigris();
const tigrisDB: DB = tigrisClient.getDatabase();

// export to share client and DB across modules
export { tigrisClient, tigrisDB };

代码从@tigrisdata/core软件包导入DBTigris类。
它创建了Tigris类的实例,该类别使用存储在.env.local中定义的环境变量中的凭据初始化Tigris客户端(注意:必须加载这些)。
然后创建一个名为tigrisDB的新变量,并通过调用getDatabase方法将其分配给返回的值,该方法将从Tigris Console获取项目的预配置数据库。
客户端和数据库实例均已导出以在应用程序中共享。

创建设置脚本

接下来,让我们创建一个设置脚本,该脚本将为上述定义的数据模型创建集合。继续创建一个scripts/setup.ts文件并添加以下代码:

import { loadEnvConfig } from "@next/env";
loadEnvConfig(process.cwd());

import { Product } from "../db/models/store";
import { tigrisClient } from "../lib/tigris";

async function main() {
  // ensure branch exists, create it if it needs to be created dynamically
  await tigrisClient.getDatabase().initializeBranch();
  // create collections
  await tigrisClient.registerSchemas([Product]);
}

main()
  .then(async () => {
    console.log("Setup complete ...");
    process.exit(0);
  })
  .catch(async (e) => {
    console.error(e);
    process.exit(1);
  });

在这里,我们从@next/env软件包导入loadEnvConfig函数,该软件包用于从.env.local加载环境变量。
tigrisClient是从我们刚刚创建的共享实用程序库导入的,我们还导入Product模型。
在导入tigrisClient之前,必须调用loadEnvConfig,因为它依赖于已加载的环境变量。

main函数中,我们使用提供的模型架构来初始化Tigris Client,并使用registerSchemas方法创建一个集合。
我们从getDatabase()方法调用initializeBranch()来创建一个database branch
.env.local文件中为TIGRIS_DB_BRANCH变量提供的值。

然后更新您的package.json文件以添加脚本setuppredevpostbuild,它们最终运行setup.ts

  "scripts": {
    "predev": "npm run setup",
    "dev": "next",
    "build": "next build",
    "postbuild": "npm run setup",
    "setup": "npx ts-node ./scripts/setup.ts",
    "start": "next start",
    "typecheck": "tsc"
  }

现在,运行scripts/setup.ts文件以使用命令:
创建 product 模型的集合

npm run setup

您会看到与以下信息相似的输出,告诉您模式注册成功:

npm run setup      

> my-app@0.1.0 setup
> npx ts-node ./scripts/setup.ts

Loaded env from /store/.env.local
info - Using reflection to infer type of Product#name
info - Using reflection to infer type of Product#price
info - Using reflection to infer type of Product#star
info - Using reflection to infer type of Product#tag
info - Using reflection to infer type of Product#image
info - Using Tigris at: api.preview.tigrisdata.cloud:443
event - Creating collection: 'products' in project: 'store'
Setup complete ...

将数据加载到底格利

通过配置底格利式和为应用程序创建的集合,是时候将一些数据加载到产品数据库集合中了。
为此,创建一个带有以下代码的scripts/loadData.ts文件:

import { loadEnvConfig } from "@next/env";
loadEnvConfig(process.cwd());

import { Product } from "../db/models/store";
import { tigrisDB } from "../lib/tigris";
import productsJson from "../db/products.json";

async function main() {
  const products: Array<Product> = productsJson as Array<Product>;
  const productsCollection = tigrisDB.getCollection<Product>(Product);
  const inserted = await productsCollection.insertMany(products);
  console.log(inserted);
}

main()
  .then(async () => {
    console.log("Data loading complete ...");
    process.exit(0);
  })
  .catch(async (e) => {
    console.error(e);
    process.exit(1);
  });

与设置脚本一样,loadEnvConfig被导入并用于加载环境变量。
然后,它从../db/models/store导入Product模型,该模型定义了将保存在Tigris数据库中的产品的结构。
它还从../lib/tigris导入tigrisDB对象,该对象负责处理数据库操作。

加载数据脚本从../db/products.json文件中读取产品数据并将其分配给productsJson变量。
然后,它定义了一个异步函数main,该函数在productsJson上执行Array<Product> type assertion
然后,它使用tigrisDB对象访问产品集合并使用
插入数据 koude63方法。
该函数返回插入的结果,并将其分配给inserted变量,该变量已记录到控制台。

ðâtigris也有一种insertOne方法将一个或多个文档插入该集合中。

现在,更新package.json文件以添加一个允许您从命令行加载数据的脚本:

...
"scripts": {
   ...
   "load-data": "npx ts-node ./scripts/loadData.ts"
},
...

最后,使用下面的命令运行将产品数据加载到products.json文件中的脚本中:

npm run load-data

执行load-data命令后,将产品加载到数据库中,并在Tigris Console的Data Explorer页面上可见。

The created products visually displayed on the Tigris web console data explorer page

在Next.js应用中获取所有产品

现在,让我们将一个名为fetchAll的函数添加到商店API中,将其添加到api/store/index.ts文件中,以获取数据库中的所有产品:

import { NextApiRequest, NextApiResponse } from "next";
import { Product } from "../../../db/models/store";
import { tigrisDB } from "../../../lib/tigris";

type FetchAllResponse = {
  result?: Array<Product>;
  error?: string;
};

async function fetchAll(res: NextApiResponse<FetchAllResponse>) {
  try {
    const productsCollection = tigrisDB.getCollection<Product>(Product);
    const cursor = productsCollection.findMany();
    const products = await cursor.toArray();
    res.status(200).json({ result: products });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
}

这里的代码将使用findMany()方法获取产品集合中的所有数据(有关更多信息,请参见Query Documents)。
它将使用toArray()Method将光标转换为数组,并将返回的数据存储在products变量中。

定义下一个。JSAPI处理程序

使用脚本加载和功能以使产品脱离方向,将处理程序函数添加到API端点,以调用您刚刚在 get get 请求上创建的功能:

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<FetchAllResponse>
) {
  switch (req.method) {
    case "GET":
      await fetchAll(res);
      break;
    default:
      res.setHeader("Allow", ["GET"]);
      res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

在这里,我们使用switch语句来确定基于请求的HTTP方法执行的操作,该操作使用req.method属性访问。
如果请求方法为 get ,则称为fetchAll函数。

更新UI以获取Tigris的产品

让我们更新电子商务应用程序的用户界面以显示底格里斯数据库中的产品并在应用程序中启用实时功能。
首先,在pages/index.tsx中更新 home组件如下:

//...

import { useEffect, useState } from "react";
import { Product } from "../db/models/store";

// import products from "../db/products.json";

export default function Home() {
  const [products, setProducts] = useState<Product[]>([]);

  const fetchProducts = async () => {
    try {
      const response = await fetch("/api/store");
      const { result } = await response.json();
      if (result) {
        setProducts(result);
      }
    } catch (e) {
      console.log(`Error: ${e}`);
    }
  };

  useEffect(() => {
    fetchProducts();
  }, []);

  //...

在这里,我们从react并从JSON文件中注释产品的加载。
然后,我们创建一个名为products的状态变量,带有函数setProducts来更新products的值。
我们还定义了使用fetch API请求/api/store端点的函数fetchProducts

当在fetchProducts中收到响应时,它将被解析为JSON,并且JSON对象的结果属性将传递给setProducts函数以更新products状态变量。
然后,当组件首次渲染时,我们使用useEffect钩调用fetchProduts函数。

现在,我们已经将静态产品列表转换为数据库驱动的产品列表。

实现实时搜索

在这一点上,您可以通过向商店API提出API请求(/api/store)从收集中获取数据。但是,我们需要添加一个API端点,以启用您的用户搜索体验。
因此,让我们在api/store目录中创建search.ts文件以将实时搜索添加到您的应用程序:

import { NextApiRequest, NextApiResponse } from "next";
import { Product } from "../../../db/models/store";
import { tigrisDB } from "../../../lib/tigris";
import { SearchQuery } from "@tigrisdata/core";

type Data = {
  result?: Array<Product>;
  error?: string;
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  const { query, page } = req.query;
  try {
    const productCollection = tigrisDB.getCollection<Product>(Product);
    const searchRequest: SearchQuery<Product> = { q: query as string };
    const results = await productCollection.search(
      searchRequest,
      Number(page) || 1
    );
    const products = new Array<Product>();
    for (const hit of results.hits) {
      products.push(hit.document);
    }
    res.status(200).json({ result: products });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
}

除了下一个软件包导入外,我们还从tigrisdata/core库和tigrisDB对象导入Products型号,SearchQuery
我们声明了一种Data类型,该类型分别定义了属性的搜索响应有效负载,分别是Array<Products>string,的属性。

然后,我们从传入请求的查询对象中破坏querypage参数,并使用Tigris search方法在集合中搜索产品。
搜索查询定义为SearchQuery对象,将q属性设置为传入的query参数。 page参数传递给search方法,默认值为1

将搜索功能添加到UI

首先将搜索查询的状态钩添加到 home组件 pages/index.tsx

//...

export default function Home() {
  const [searchInput, setSearchInput] = useState<string>();

        //...

接下来,创建一个函数以使 get 请求新搜索端点:

//...

const searchQuery = async () => {
    const response = await fetch(`/api/store/search?query=${encodeURI(searchInput)}`);
    const { result } = await response.json();
    if (result) {
      setProducts(result);
    }
  };
  useEffect(() => {
        //...

searchQuery函数使用fetch/api/store/search端点发送 get ,带有query的参数名称,并将值设置为用户文本条目的编码版本。

结果用于更新products状态变量。

接下来,更新家庭组件中的<form>标签以添加事件侦听器以调用SearchQuerySetSearchInput函数:

<form className="d-flex" role="search">
  <input
    className="form-control me-2"
    type="search"
    placeholder="Search"
    aria-label="Search"
    onKeyUp={searchQuery}
    onChange={(e) => setSearchInput(e.target.value)}
  />
</form>

我们将searchQuery函数绑定到onKeyUp事件,以便每次用户发布键时都调用该函数执行搜索。

就是这样!让我们尝试一下应用程序。

测试应用程序

现在,让我们测试应用程序,以查看实时搜索功能如何在您的电子商务应用程序上工作:

npm run dev

您的电子商务应用程序现在使用闪亮的实时全文搜索功能列出了底格里斯数据库中的产品。

The Tigris real-time full-text search demonstrations preview

结论

在本教程中,我们探索了实时全文搜索的概念,以及如何在tigris的next.js应用程序中实现它。

作为演示,我们开发了一个Next.js电子商务产品列表应用程序,并使用Tigris集成了实时搜索功能。


我们首先设置Tigris TypeScript SDK并创建脚本以创建产品集合的模型,并将该产品数据导入到数据库中。
然后,我们更新了该应用程序,以通过Next.js API端点从Tigris数据库中检索产品详细信息。
最后,我们通过新的API端点添加了产品的实时全文本搜索功能,并通过更新UI来聆听搜索表格中的用户击键事件。

本教程的完整代码可在 main 分支上获得here

next.js是一个用于构建Web应用程序的高产框架。
本教程仅显示了将Next.js与Tigris相结合的一些好处,Tigris是一个新的多合一开发人员数据平台,可提供数据库和自动搜索索引进行实时搜索。
查看Tigris documentation以了解更多信息。愉快的编码!