diff --git a/docker-compose.yml b/docker-compose.yml index 57c9f37..87f5b29 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: db: # image: ankane/pgvector - build: + build: context: . dockerfile: Dockerfile.pg ports: @@ -35,7 +35,38 @@ services: volumes: - redis-data:/data + es: + image: elasticsearch:8.9.2 + environment: + - discovery.type=single-node + - ELASTIC_PASSWORD=changeme + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + ports: + - "9200:9200" + - "9300:9300" + volumes: + - elastic-data:/usr/share/elasticsearch/data + networks: + - elastic + + kibana: + image: kibana:8.9.2 + ports: + - "5601:5601" + depends_on: + - es + networks: + - elastic + volumes: + elastic-data: + driver: local redis-data: driver: local db-data: + driver: local + +networks: + elastic: + driver: bridge \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6fa2779..10ff44e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@nestjs/common": "^10.2.4", "@nestjs/config": "^3.0.1", "@nestjs/core": "^10.2.4", + "@nestjs/elasticsearch": "^10.0.1", "@nestjs/jwt": "^10.1.1", "@nestjs/passport": "^10.0.1", "@nestjs/platform-express": "^10.2.4", @@ -1939,6 +1940,42 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@elastic/elasticsearch": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-8.9.0.tgz", + "integrity": "sha512-UyolnzjOYTRL2966TYS3IoJP4tQbvak/pmYmbP3JdphD53RjkyVDdxMpTBv+2LcNBRrvYPTzxQbpRW/nGSXA9g==", + "peer": true, + "dependencies": { + "@elastic/transport": "^8.3.2", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@elastic/transport": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@elastic/transport/-/transport-8.3.3.tgz", + "integrity": "sha512-g5nc//dq/RQUTMkJUB8Ui8KJa/WflWmUa7yLl4SRZd67PPxIp3cn+OvGMNIhpiLRcfz1upanzgZHb/7Po2eEdQ==", + "peer": true, + "dependencies": { + "debug": "^4.3.4", + "hpagent": "^1.0.0", + "ms": "^2.1.3", + "secure-json-parse": "^2.4.0", + "tslib": "^2.4.0", + "undici": "^5.22.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@elastic/transport/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "peer": true + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2689,6 +2726,16 @@ } } }, + "node_modules/@nestjs/elasticsearch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/elasticsearch/-/elasticsearch-10.0.1.tgz", + "integrity": "sha512-rYkcuStF7oDbyt5X3h0BAYS3thZQm8vsHUkGot6dkB8dOTsSIsQJ8UYY9j66LhghzsBiewIwUrKkhY5WM2DVqA==", + "peerDependencies": { + "@elastic/elasticsearch": "^7.4.0 || ^8.0.0", + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "rxjs": "^7.2.0" + } + }, "node_modules/@nestjs/jwt": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.1.1.tgz", @@ -7307,6 +7354,15 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/hpagent": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", + "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", + "peer": true, + "engines": { + "node": ">=14" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -10448,6 +10504,12 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "peer": true + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -11521,6 +11583,39 @@ "node": ">=8" } }, + "node_modules/undici": { + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.24.0.tgz", + "integrity": "sha512-OKlckxBjFl0oXxcj9FU6oB8fDAaiRUq+D8jrFWGmOfI/gIyjk/IeS75LMzgYKUaeHzLUcYvf9bbJGSrUwTfwwQ==", + "peer": true, + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici/node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "peer": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/undici/node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/package.json b/package.json index e8e1a22..13f3c8d 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@nestjs/common": "^10.2.4", "@nestjs/config": "^3.0.1", "@nestjs/core": "^10.2.4", + "@nestjs/elasticsearch": "^10.0.1", "@nestjs/jwt": "^10.1.1", "@nestjs/passport": "^10.0.1", "@nestjs/platform-express": "^10.2.4", diff --git a/src/app.module.ts b/src/app.module.ts index bcd6150..d0c7762 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -9,6 +9,7 @@ import { PrismaService } from './prisma/prisma.service'; import { UserModule } from './user/user.module'; import { TagModule } from './tag/tag.module'; import { CollectionModule } from './collection/collection.module'; +import { SearchModule } from './search/search.module'; @Module({ imports: [ @@ -19,6 +20,7 @@ import { CollectionModule } from './collection/collection.module'; UserModule, TagModule, CollectionModule, + SearchModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/search/search.module.ts b/src/search/search.module.ts new file mode 100644 index 0000000..8e9b5e8 --- /dev/null +++ b/src/search/search.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { ElasticsearchModule } from '@nestjs/elasticsearch'; + +@Module({ + imports: [ + ConfigModule, + ElasticsearchModule.registerAsync({ + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + node: configService.get('ELASTICSEARCH_NODE'), + auth: { + username: configService.get('ELASTICSEARCH_USERNAME'), + password: configService.get('ELASTICSEARCH_PASSWORD'), + }, + }), + inject: [ConfigService], + }), + ], + exports: [ElasticsearchModule], +}) +export class SearchModule {} diff --git a/src/search/search.service.spec.ts b/src/search/search.service.spec.ts new file mode 100644 index 0000000..63fc48c --- /dev/null +++ b/src/search/search.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SearchService } from './search.service'; + +describe('SearchService', () => { + let service: SearchService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SearchService], + }).compile(); + + service = module.get(SearchService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/search/search.service.ts b/src/search/search.service.ts new file mode 100644 index 0000000..b8ed7eb --- /dev/null +++ b/src/search/search.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class SearchService {}