ai agent --- postgreSQL 关系型数据库

一.postgreSQL 是什么?

1.概念

PostgreSQL 是一个免费、开源、功能完善的对象关系型数据库。他和mysql 一样,都是关系型数据库。

2.postgreSQL 和 mysql 对比

在安装方面

我们使用mysql的时候需要安装 mysql服务端用来存取数据;然后再安装 MYSQL Workbench来图形化查看服务端的数据;如果你在nodejs代码里面操作数据库,你需要安装mysql2包,用代码的方式操作数据库,操作完以后可以在 MYSQL Workbench里面清晰看到数据变化。

和mysql一样,postgreSQL也需要安装三个包,安装 psql 服务端用来存取数据;然后安装 pgAdmin4 来图形化查询服务端的数据;你在nodejs项目里面要用代码操作psql数据库里面的数据,就要安装pg包进行增删改查,然后在pgAdmin4软件里面查看最终数据。

在项目架构方面

在安装方面他们是极其相似的,但是对于aiAgent项目而言,mysql 和 postgresql 之间最大的区别是: postgresql可以存储向量,它还有一个插件pgvector,可以对问题向量和文档向量做余弦相似度检索,找到相关文档,给大模型使用。

mysql虽然也能存储向量,可是他却没有做余弦相似度检索的工具,所以一般在企业里面要是有mysql存数文档,那么就会选择用milvus做向量存储。

当然最新版的milvus也能存储一部分文档块,但是他的能力有限,如果你存的太多,就会将整个milvus的检索向量的能力拉低,所以在企业里面,milvus 里面一般不会放文档块。milvus的优势就是向量相似度对比,那么文档存储就会交给mysql做。所以之前的样例都是mysql + milvus的实现方案。

如果你的项目是一个新项目,没有使用mysql,那么你就可以直接用postgreSQL,他内置的插件pgvector可以快速解决向量相似度检索,从而直接拿到这条数据对应的文本块。而不像 mysql + milvus 一样,先从milvus里面拿到id 然后用这个id 去mysql 里面拿文本块。

在sql语句方面

PostgreSQL 和 MySQL 共享标准 SQL 基础,基本增删改查差异不大;真正明显的差异集中在自增主键、分页简写、日期函数、布尔值、冲突处理、JSON、存储过程、DDL 事务、引号和特有系统函数。

3.运行原理

首先将一个长文本按照某种规则,剪切成文本块,然后由嵌入模型把这个文本块转化成向量,利用sql语句将 文本块 和 向量 插入到 postgreSQL 数据库里面。
当用户问问题的时候,嵌入模型会将用户问题转化成向量,然后通过sql 语句发送给postgreSQL 数据库,再由 PostgreSQL 配合 pgvector 计算向量距离并返回最相似的文本块。
pgvector 支持多种距离算法,而不仅是余弦相似度,并且检索到的文本还需要拼接到prompt里面交给大模型生成最终回答。

注意:问题向量和文档向量必须使用 同一个嵌入模型生成,否则维度不同、语义空间不同,无法直接比较。

4.安装

4.1 安装postgreSQL

官网 www.postgresql.org/

4.2 安装 pgAdmin 4

官网: www.pgadmin.org/download/pg...

pgAdmin 4图形界面的使用和 mysql workbetch 很相似,连接数据库后就能使用sql 语句进行增删改查了。

4.3 在nodejs 里面使用

4.3.1 初始化项目

js 复制代码
mkdir node-pg-demo
cd node-pg-demo
npm init -y
npm install pg dotenv

4.3.2配置.env

js 复制代码
PGHOST=localhost
PGPORT=5432
PGUSER=postgres
PGPASSWORD=你的密码
PGDATABASE=node_pg_demo

4.3.3连接数据库

js 复制代码
import pg from "pg";
import dotenv from "dotenv";

dotenv.config();

const { Pool } = pg;

const pool = new Pool({
  host: process.env.PGHOST,
  port: process.env.PGPORT,
  user: process.env.PGUSER,
  password: process.env.PGPASSWORD,
  database: process.env.PGDATABASE,
});

// 连接测试
pool.query("SELECT NOW()", (err, res) => {
  if (err) {
    console.error("连接失败:", err.message);
  } else {
    console.log("数据库连接成功,服务器时间:", res.rows[0].now);
  }
});

export default pool;

4.3.4新建数据表

js 复制代码
import pool from "./db.js";

const createTable = `
  CREATE TABLE IF NOT EXISTS users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    age INT,
    created_at TIMESTAMPTZ DEFAULT NOW()
  )
`;

const run = async () => {
  try {
    await pool.query(createTable);
    console.log("users 表创建成功");
  } catch (err) {
    console.error("建表失败:", err.message);
  } finally {
    await pool.end();
  }
};

run();

4.3.5增删改查

js 复制代码
import pool from "./db.js";

// 1. 新增
const createUser = async (name, email, age) => {
  const sql = `
    INSERT INTO users (name, email, age)
    VALUES ($1, $2, $3)
    RETURNING *
  `;
  const res = await pool.query(sql, [name, email, age]);
  return res.rows[0];
};

// 2. 查询全部
const getAllUsers = async () => {
  const res = await pool.query(
    "SELECT * FROM users ORDER BY id DESC"
  );
  return res.rows;
};

// 3. 按 ID 查询
const getUserById = async (id) => {
  const res = await pool.query(
    "SELECT * FROM users WHERE id = $1",
    [id]
  );
  return res.rows[0];
};

// 4. 修改
const updateUser = async (id, name, age) => {
  const sql = `
    UPDATE users
    SET name = $1, age = $2
    WHERE id = $3
    RETURNING *
  `;
  const res = await pool.query(sql, [name, age, id]);
  return res.rows[0];
};

// 5. 删除
const deleteUser = async (id) => {
  const res = await pool.query(
    "DELETE FROM users WHERE id = $1 RETURNING *",
    [id]
  );
  return res.rows[0];
};

// 演示完整流程
const run = async () => {
  try {
    // 新增
    const user = await createUser("张三", "zhangsan@example.com", 28);
    console.log("新增:", user);

    // 查询全部
    const list = await getAllUsers();
    console.log("全部用户:", list);

    // 按 ID 查询
    const one = await getUserById(user.id);
    console.log("单条查询:", one);

    // 修改
    const updated = await updateUser(user.id, "李四", 30);
    console.log("修改后:", updated);

    // 删除
    const deleted = await deleteUser(user.id);
    console.log("已删除:", deleted);
  } catch (err) {
    console.error("操作失败:", err.message);
  } finally {
    await pool.end();
  }
};

run();

4.4 在nestjs里面使用

4.4.1 初始化项目

js 复制代码
# 使用 Nest CLI 创建项目
npm i -g @nestjs/cli
nest new nest-pg-demo
cd nest-pg-demo

# 安装 PostgreSQL 驱动和 TypeORM
npm install @nestjs/typeorm typeorm pg
npm install @nestjs/mapped-types

4.4.2 连接数据库

js 复制代码
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: '你的密码',
      database: 'nest_pg_demo',
      autoLoadEntities: true,
      synchronize: true, // 开发环境自动建表,生产环境建议关闭
    }),
    UsersModule,
  ],
})
export class AppModule {}

4.4.3创建 User 实体(Entity)

新建 src/users/entities/user.entity.ts

js 复制代码
import {
  Column,
  CreateDateColumn,
  Entity,
  PrimaryGeneratedColumn,
} from 'typeorm';

@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 100 })
  name: string;

  @Column({ unique: true, length: 255 })
  email: string;

  @Column({ nullable: true })
  age: number;

  @CreateDateColumn()
  createdAt: Date;
}

4.4.4创建 DTO(数据传输对象)

新建 src/users/dto/create-user.dto.ts:

typescript 复制代码
export class CreateUserDto {
  name: string;
  email: string;
  age?: number;
}

新建 src/users/dto/update-user.dto.ts:

scala 复制代码
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';

export class UpdateUserDto extends PartialType(CreateUserDto) {}

4.4.5创建 Service(业务逻辑 + CRUD)

新建 src/users/users.service.ts:

typescript 复制代码
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  // 新增
  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = this.userRepository.create(createUserDto);
    return await this.userRepository.save(user);
  }

  // 查询全部
  async findAll(): Promise<User[]> {
    return await this.userRepository.find({
      order: { id: 'DESC' },
    });
  }

  // 按 ID 查询
  async findOne(id: number): Promise<User> {
    const user = await this.userRepository.findOne({ where: { id } });
    if (!user) {
      throw new NotFoundException(`ID 为 ${id} 的用户不存在`);
    }
    return user;
  }

  // 修改
  async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.findOne(id);
    Object.assign(user, updateUserDto);
    return await this.userRepository.save(user);
  }

  // 删除
  async remove(id: number): Promise<void> {
    const user = await this.findOne(id);
    await this.userRepository.remove(user);
  }
}

4.4.6创建 Controller(路由)

新建 src/users/users.controller.ts:

less 复制代码
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Patch,
  Post,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  // POST /users
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  // GET /users
  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  // GET /users/:id
  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(Number(id));
  }

  // PATCH /users/:id
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(Number(id), updateUserDto);
  }

  // DELETE /users/:id
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(Number(id));
  }
}

4.4.7 创建 UsersModule

新建 src/users/users.module.ts:

python 复制代码
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { User } from './entities/user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}
相关推荐
卷无止境2 小时前
ECharts:把数据变成故事的可视化利器
前端
Eric_见嘉2 小时前
在职前端 Skill 和 MCP 分享
前端·后端·agent
liangshanbo12152 小时前
面试题:如何优化 Webpack 的打包速度?
前端·webpack·node.js
wengad3 小时前
从 wangEditor 迁到 wangEditor-next
前端·javascript·vue.js
行者-全栈开发4 小时前
【前端/JS框架】CVE-2026-3854:GitHub Enterprise Server X-Stat注入XSS漏洞深度剖析与紧急修复指南
前端·javascript·github
计算机魔术师5 小时前
AMD砸82亿买下李飞飞,这次不卖芯片卖什么?
前端
hzxpaipai5 小时前
杭州企业官网怎么建设?从策划到上线的5步建站方法
运维·服务器·前端·网络
怕浪猫5 小时前
2026年金九银十,我面了22个前端
前端·javascript·面试
IT_陈寒5 小时前
Vue 这个响应式陷阱,我的头发都掉没了
前端·人工智能·后端