{"id":23790041,"url":"https://github.com/blackglory/fts","last_synced_at":"2026-05-08T04:02:22.484Z","repository":{"id":44964999,"uuid":"363674215","full_name":"BlackGlory/fts","owner":"BlackGlory","description":"🌳","archived":false,"fork":false,"pushed_at":"2025-10-15T10:15:10.000Z","size":2196,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-16T02:58:51.289Z","etag":null,"topics":["docker-image","esm","microservice","nodejs","typescript"],"latest_commit_sha":null,"homepage":"https://hub.docker.com/r/blackglory/fts","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BlackGlory.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-05-02T14:44:52.000Z","updated_at":"2025-10-15T10:15:14.000Z","dependencies_parsed_at":"2025-02-21T12:43:27.521Z","dependency_job_id":"f59c6d59-cda5-4a31-8889-90747ef80699","html_url":"https://github.com/BlackGlory/fts","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/BlackGlory/fts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Ffts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Ffts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Ffts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Ffts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BlackGlory","download_url":"https://codeload.github.com/BlackGlory/fts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Ffts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32766122,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T02:36:36.067Z","status":"ssl_error","status_checked_at":"2026-05-08T02:36:07.210Z","response_time":54,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker-image","esm","microservice","nodejs","typescript"],"created_at":"2025-01-01T17:18:12.177Z","updated_at":"2026-05-08T04:02:22.447Z","avatar_url":"https://github.com/BlackGlory.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FTS\n提供以WebSocket为通讯协议的全文搜索服务, 受到[Sonic]启发.\n\n[Sonic]: https://github.com/valeriansaliou/sonic\n\n## 特点\n通常语境下, 全文搜索引擎是由数据库, 分词器, 倒排索引, 查询语言等元素组合而成的集成软件.\nFTS与一般全文搜索引擎的区别在于, 它解耦了全文搜索的分词器, 没有内置分词能力, 分词被完全委托给客户端来实现.\n\n## 缺点\n- FTS在更新词典时必须由用户重写整个索引, 通信成本导致其效率比内置分词的全文搜索引擎慢得多.\n- FTS使用的后端PostgreSQL所支持的全文搜索性能仅适用于小型数据集.\n- FTS的查询结果不支持文本高亮.\n\n## 术语\n### 命名空间 namespace\n桶的集合.\n\n### 桶 bucket\n文档的集合.\n\n### 文档 document\n词素的集合.\n\n### 词素 lexeme\n分词后得到的结果.\n\n词素是否大小写敏感取决于其后端实现, 为了确保查询不受后端实现差异的影响, 应该总是将其转换为统一的大写或小写形式.\n\n## Quickstart\n```sh\ndocker run \\\n  --detach \\\n  --publish 8080:8080 \\\n  blackglory/fts\n```\n\n## Install\n### 从源代码运行\n```sh\ngit clone https://github.com/BlackGlory/fts\ncd log\nyarn install\nyarn build\nyarn bundle\nyarn --silent start\n```\n\n### 从源代码构建\n```sh\ngit clone https://github.com/BlackGlory/fts\ncd fts\nyarn install\nyarn docker:build\n```\n\n### Recipes\n#### docker-compose.yml\n```yaml\nversion: '3.8'\n\nservices:\n  fts:\n    image: 'blackglory/fts'\n    restart: always\n    depends_on:\n      - postgres\n    environment:\n      - 'FTS_POSTGRES_HOST=postgres'\n      - 'FTS_POSTGRES_PORT=5432'\n      - 'FTS_POSTGRES_USERNAME=postgres'\n      - 'FTS_POSTGRES_PASSWORD=password'\n      - 'FTS_POSTGRES_NAME=fts'\n    volumes:\n      - 'fts-data:/data'\n    ports:\n      - '8080:8080'\n\n  postgres:\n    image: 'postgres:15'\n    environment:\n      - 'POSTGRES_PASSWORD=password'\n    volumes:\n      - 'postgres-data:/var/lib/postgresql/data'\n\nvolumes:\n  fts-data:\n  postgres:\n```\n\n## API\n```ts\ninterface INamespaceStats {\n  buckets: number\n  documents: number\n}\n\ninterface IBucketStats {\n  documents: number\n}\n\ninterface IDocumentQueryResult {\n  bucket: string\n  documentId: string\n}\n\ninterface IAPI {\n  getNamespaceStats(namespace: string): INamespaceStats\n  getBucketStats(namespace: string, bucket: string): IBucketStats\n\n  getAllNamespaces(): string[]\n  getAllBuckets(namespace: string): string[]\n\n  setDocument(\n    namespace: string\n  , bucket: string\n  , documentId: string\n  , lexemes: string[]\n  ): null\n\n  removeDocument(\n    namespace: string\n  , bucket: string\n  , documentId: string\n  ): null\n\n  clearBucketsByNamespace(namespace: string): null\n  clearDocumentsByBucket(namespace: string, bucket: string): null\n\n  queryDocuments(\n    namespace: string\n  , expression: IQueryExpression\n  , options?: {\n      buckets?: string[]\n      limit?: number\n      offset?: number\n    }\n  ): IDocumentQueryResult[]\n}\n```\n\n## 查询语言\nFTS的查询语言是一种以JSON数组表示的AST.\n\n```ts\nenum QueryKeyword {\n  And = 0\n, Or = 1\n, Not = 2\n, Phrase = 3\n, Prefix = 4\n}\n\ntype IQueryExpression =\n| ITermExpression\n| IPhraseExpression\n| IPrefixExpression\n| IAndExpression\n| IOrExpression\n| INotExpression\n\ntype ITermExpression = string\n\ntype IPhraseExpression = [\n  QueryKeyword.Phrase\n, ...IQueryExpression[]\n]\n\ntype IPrefixExpression = [\n  QueryKeyword.Prefix\n, string\n]\n\ntype IAndExpression = [\n  IQueryExpression\n, QueryKeyword.And\n, IQueryExpression\n]\n\ntype IOrExpression = [\n  IQueryExpression\n, QueryKeyword.Or\n, IQueryExpression\n]\n\ntype INotExpression = [\n  QueryKeyword.Not\n, IQueryExpression\n]\n```\n\n```js\n['a', AND, ['b', OR, [NOT, 'c']]]\n// 相当于 'a' AND ('b' OR (NOT 'c'))\n```\n\n### `AND = 0`\n逻辑与, 左值和右值可以嵌套其他表达式.\n\n```js\n['left', AND, 'right']\n```\n\n### `OR = 1`\n逻辑或, 左值和右值可以嵌套其他表达式.\n\n```js\n['left', OR, 'right']\n```\n\n### `NOT = 2`\n逻辑非, 右值可以嵌套其他表达式.\n\n```js\n[NOT, 'right']\n```\n\n### `PHRASE = 3`\n由连续单词组成的短语.\n\n```js\n[PHRASE, 'a', 'b', 'c']\n```\n\n### `PREFIX = 4`\n由单个词素构成的前缀搜索.\n\n```js\n[PREFIX, 'a']\n```\n\n## 环境变量\n### `FTS_HOST`, `FTS_PORT`\n通过环境变量`FTS_HOST`和`FTS_PORT`决定服务器监听的地址和端口,\n默认值为`localhost`和`8080`.\n\n### `FTS_WS_HEARTBEAT_INTERVAL`\n通过环境变量`FTS_WS_HEARTBEAT_INTERVAL`可以设置WS心跳包(ping帧)的发送间隔, 单位为毫秒.\n在默认情况下, 服务不会发送心跳包,\n半开连接的检测依赖于服务端和客户端的运行平台的TCP Keepalive配置.\n\n当`FTS_WS_HEARTBEAT_INTERVAL`大于零时,\n服务会通过WS的ping帧按间隔发送心跳包.\n\n### PostgreSQL连接信息\n- `FTS_POSTGRES_HOST`: 主机名\n- `FTS_POSTGRES_PORT`: 端口号, 默认为`5432`\n- `FTS_POSTGRES_USERNAME`: 用户名\n- `FTS_POSTGRES_PASSWORD`: 密码\n- `FTS_POSTGRES_DATABASE`: 数据库\n\n## 客户端\n- JavaScript/TypeScript(Node.js, Browser): \u003chttps://github.com/BlackGlory/fts-js\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackglory%2Ffts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblackglory%2Ffts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackglory%2Ffts/lists"}