# PostgREST查询参考 ## 过滤运算符完整列表 ### 比较运算符 | 运算符 | 说明 | PostgREST语法 | JavaScript示例 | |--------|------|---------------|----------------| | eq | 等于 | `field=eq.value` | `{ field: 'eq.value' }` | | neq | 不等于 | `field=neq.value` | `{ field: 'neq.value' }` | | gt | 大于 | `field=gt.value` | `{ field: 'gt.100' }` | | gte | 大于等于 | `field=gte.value` | `{ field: 'gte.100' }` | | lt | 小于 | `field=lt.value` | `{ field: 'lt.100' }` | | lte | 小于等于 | `field=lte.value` | `{ field: 'lte.100' }` | ### 模式匹配运算符 | 运算符 | 说明 | PostgREST语法 | JavaScript示例 | |--------|------|---------------|----------------| | like | 模糊匹配(区分大小写) | `field=like.*pattern*` | `{ field: 'like.*测试*' }` | | ilike | 模糊匹配(不区分大小写) | `field=ilike.*pattern*` | `{ field: 'ilike.*TEST*' }` | **模式匹配通配符**: - `*`: 匹配任意字符(等同于SQL的`%`) - `_`: 匹配单个字符 **示例**: ```javascript // 查询标题以"合同"开头的文档 { title: 'like.合同*' } // 查询标题以"合同"结尾的文档 { title: 'like.*合同' } // 查询标题包含"合同"的文档 { title: 'like.*合同*' } // 不区分大小写查找 { title: 'ilike.*contract*' } ``` ### 列表运算符 | 运算符 | 说明 | PostgREST语法 | JavaScript示例 | |--------|------|---------------|----------------| | in | 在列表中 | `field=in.(value1,value2,...)` | `{ field: 'in.(active,pending)' }` | | cs | 包含(数组字段) | `field=cs.{value1,value2}` | `{ tags: 'cs.{重要,紧急}' }` | | cd | 被包含(数组字段) | `field=cd.{value1,value2}` | `{ tags: 'cd.{重要,紧急}' }` | | ov | 重叠(数组字段) | `field=ov.{value1,value2}` | `{ tags: 'ov.{重要,紧急}' }` | **示例**: ```javascript // 查询状态为active或pending的文档 { status: 'in.(active,pending)' } // 查询ID在[1, 2, 3]中的文档 { id: 'in.(1,2,3)' } // 查询标签包含"重要"和"紧急"的文档(数组字段) { tags: 'cs.{重要,紧急}' } ``` ### Null检查运算符 | 运算符 | 说明 | PostgREST语法 | JavaScript示例 | |--------|------|---------------|----------------| | is | 是null | `field=is.null` | `{ field: 'is.null' }` | | nt | 不是null | `field=nt.null` (或 `is.not.null`) | `{ field: 'nt.null' }` | **示例**: ```javascript // 查询deleted_at为null的文档(未删除) { deleted_at: 'is.null' } // 查询deleted_at不为null的文档(已删除) { deleted_at: 'nt.null' } ``` ## 排序 ### 单字段排序 ```javascript // 按创建时间升序 { order: 'created_at.asc' } // 按创建时间降序 { order: 'created_at.desc' } ``` ### 多字段排序 ```javascript // 先按状态升序,再按创建时间降序 { order: 'status.asc,created_at.desc' } ``` ## 分页 ### Limit和Offset ```javascript // 每页10条,第1页 { limit: 10, offset: 0 } // 每页10条,第2页 { limit: 10, offset: 10 } // 每页20条,第3页 { limit: 20, offset: 40 } ``` ### 获取总数 PostgREST会在响应头中返回`Content-Range`,包含总记录数: ``` Content-Range: 0-9/50 ``` 表示:返回第0-9条记录,总共50条记录。 **解析示例**: ```javascript const response = await apiClient.get('/postgrest/documents', { params: { limit: 10, offset: 0 } }); const contentRange = response.headers['content-range']; const total = parseInt(contentRange.split('/')[1]); // 50 ``` ## 字段选择 ### 选择特定字段 ```javascript // 只返回id, title, status字段 { select: 'id,title,status' } ``` ### 嵌套查询(外键关联) PostgREST支持嵌套查询,但DocAuditAI暂未启用此功能。 ## 高级查询 ### 逻辑运算符 **注意**: 前端应避免使用`or`、`and`、`not`参数,这些参数由后端自动生成(用于数据范围和交叉评查权限)。 ### 复杂查询示例 ```javascript // 查询状态为active且创建时间在2025年之后的文档,按创建时间降序,每页10条 const params = { status: 'eq.active', created_at: 'gte.2025-01-01', order: 'created_at.desc', limit: 10, offset: 0 }; const response = await apiClient.get('/postgrest/documents', { params }); ``` ## 完整示例 ### 示例1:文档列表查询 ```javascript // 需求:查询标题包含"合同"、状态为active的文档, // 按创建时间降序排列,每页20条,第2页 async function getDocumentList() { const response = await apiClient.get('/postgrest/documents', { params: { title: 'ilike.*合同*', status: 'eq.active', order: 'created_at.desc', limit: 20, offset: 20 // 第2页 } }); return { data: response.data, total: parseInt(response.headers['content-range'].split('/')[1]) }; } ``` ### 示例2:高级搜索 ```javascript // 需求:搜索2025年创建的、状态为active或pending的文档 async function advancedSearch() { const response = await apiClient.get('/postgrest/documents', { params: { status: 'in.(active,pending)', created_at: 'gte.2025-01-01', created_at: 'lt.2026-01-01', // 注意:PostgREST会组合多个同名参数 order: 'created_at.desc' } }); return response.data; } ``` ### 示例3:分页封装 ```javascript // 通用分页函数 async function fetchPaginated(table, page, pageSize, filters = {}) { const params = { limit: pageSize, offset: (page - 1) * pageSize, ...filters }; const response = await apiClient.get(`/postgrest/${table}`, { params }); const contentRange = response.headers['content-range']; const total = contentRange ? parseInt(contentRange.split('/')[1]) : 0; return { data: response.data, total: total, page: page, pageSize: pageSize, totalPages: Math.ceil(total / pageSize) }; } // 使用示例 const result = await fetchPaginated('documents', 1, 10, { status: 'eq.active', order: 'created_at.desc' }); console.log(`共${result.total}条记录,第${result.page}/${result.totalPages}页`); ``` ## 性能优化建议 ### 1. 始终使用分页 ```javascript // ✅ 好的做法 { limit: 10, offset: 0 } // ❌ 不好的做法(返回所有数据) {} ``` ### 2. 只查询需要的字段 ```javascript // ✅ 好的做法(只查询id和title) { select: 'id,title' } // ❌ 不好的做法(查询所有字段) {} ``` ### 3. 使用索引字段进行过滤 优先使用已建立索引的字段(如id, user_id, ou_id, status)进行过滤,提升查询性能。 ### 4. 避免过度模糊查询 ```javascript // ✅ 好的做法(明确的过滤条件) { title: 'like.合同*' } // ❌ 不好的做法(前后通配符,性能差) { title: 'like.*合同*' } ``` ## 常见错误 ### 错误1:运算符拼写错误 ```javascript // ❌ 错误 { status: 'equal.active' } // 应该是 eq // ✅ 正确 { status: 'eq.active' } ``` ### 错误2:日期格式错误 ```javascript // ❌ 错误 { created_at: 'gte.2025/01/01' } // 格式错误 // ✅ 正确 { created_at: 'gte.2025-01-01' } // ISO 8601格式 ``` ### 错误3:in运算符语法错误 ```javascript // ❌ 错误 { status: 'in.active,pending' } // 缺少括号 // ✅ 正确 { status: 'in.(active,pending)' } ``` ## 参考资料 - [PostgREST官方文档](https://postgrest.org/en/stable/) - [PostgREST API查询语法](https://postgrest.org/en/stable/api.html#horizontal-filtering-rows)