连接到Elasticsearch的NodeJ

时间:2018-09-16 13:05:52

标签: node.js docker elasticsearch mongoose

我试图更新我的REST API以使用elasticsearch,我发现mongoosastic插件似乎是最好的解决方案,但我找不到如何将Elasticsearch与节点服务器连接的方法。

根据mongoosastic doc(https://github.com/mongoosastic/mongoosastic),user.model.ts中的代码应该可以工作,但是当我创建用户然后点击http://localhost:9200/user/user/_search时,主体为以下json:

{
    "error": {
        "root_cause": [{
            "type": "index_not_found_exception",
            "reason": "no such index",
            "resource.type": "index_or_alias",
            "resource.id": "user",
            "index_uuid": "_na_",
            "index": "user"
        }],
        "type": "index_not_found_exception",
        "reason": "no such index",
        "resource.type": "index_or_alias",
        "resource.id": "user",
        "index_uuid": "_na_",
        "index": "user"
    },
    "status": 404
}

创建新用户时,控制台中出现以下错误:

POST http://0.0.0.0:9200/users/user/5b9e535a6a2a4100cb86e9a3?refresh=true => connect ECONNREFUSED 0.0.0.0:9200
    at Log.error (/app/node_modules/elasticsearch/src/lib/log.js:226:56)
    at checkRespForFailure (/app/node_modules/elasticsearch/src/lib/transport.js:259:18)
    at HttpConnector.<anonymous> (/app/node_modules/elasticsearch/src/lib/connectors/http.js:163:7)
    at ClientRequest.wrapper (/app/node_modules/elasticsearch/node_modules/lodash/lodash.js:4949:19)
    at ClientRequest.emit (events.js:182:13)
    at Socket.socketErrorListener (_http_client.js:391:9)
    at Socket.emit (events.js:182:13)
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)
::ffff:172.18.0.1 - - [16/Sep/2018:12:58:02 +0000] "POST /api/v0/user/ HTTP/1.1" 201 230 "http://localhost:9000/doc/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"
Elasticsearch WARNING: 2018-09-16T12:58:02Z
  Unable to revive connection: http://0.0.0.0:9200/
Elasticsearch WARNING: 2018-09-16T12:58:02Z
  No living connections

所以我相信错误只是我配置错误的一些配置选项,但我无法找出做错了什么。

通过浏览器转到http://localhost:9200/_cluster/health,给我:

{
    "cluster_name": "docker-cluster",
    "status": "yellow",
    "timed_out": false,
    "number_of_nodes": 1,
    "number_of_data_nodes": 1,
    "active_primary_shards": 1,
    "active_shards": 1,
    "relocating_shards": 0,
    "initializing_shards": 0,
    "unassigned_shards": 1,
    "delayed_unassigned_shards": 0,
    "number_of_pending_tasks": 0,
    "number_of_in_flight_fetch": 0,
    "task_max_waiting_in_queue_millis": 0,
    "active_shards_percent_as_number": 50.0
}

我也使用docker。

Dockerfile:

FROM mhart/alpine-node:10
ADD . /app
WORKDIR /app
RUN apk --no-cache add --virtual builds-deps build-base python &&\
    yarn global add nodemon &&\
    yarn &&\
    apk del builds-deps build-base python

docker-compose.yml:

version: "3.3"
services:
  api:

    build: .
    links:
      - 'mongo:mongo'
      - 'elasticsearch:elasticsearch'
    env_file:
      - .env
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "0.0.0.0:9000:9000"
    command: sh -c "mkdir -p dist && touch ./dist/app.js && yarn run start"

  transpiler:
    build: .
    volumes:
      - .:/app
      - /app/node_modules
    command: yarn run transpile -- -w

  linter:
    build: .
    volumes:
      - .:/app
      - /app/node_modules
    command: nodemon --delay 500ms --exec yarn run lint

  mongo:
    image: mongo:4.0
    ports:
      - "27017"
    command: mongod
    volumes:
      - ./data:/data/db

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.1.1
    volumes:
      - ./esdata:/esdata
    environment:
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - discovery.type=single-node
    ports:
      - "9300:9300"
      - "9200:9200"

  mocha:
    build: .
    volumes:
      - .:/app
      - /app/node_modules
    links:
      - 'mongo:mongo'
    command: nodemon --delay 500ms --exec yarn run test
    env_file:
      - .env
    environment:
      NODE_ENV: 'test'

user.model.ts

import * as mongoose from 'mongoose'
import * as mongoosastic from 'mongoosastic'
import * as elasticsearch from 'elasticsearch'
import * as bcrypt from 'bcrypt'
import * as emailValidator from 'email-validator'
import { IImage, Image } from '../image/image.model'
import config from '../../config/environment'
import { NextFunction } from 'express'
import * as beautifyUnique from 'mongoose-beautiful-unique-validation'

const SALT_WORK_FACTOR: number = config.bcryptWorkFactor

export interface IUser extends mongoose.Document {
    mail: string
    password: string
    login: string
    profilPic: mongoose.Types.ObjectId
    public: {
        mail: string
        profilPic: mongoose.Types.ObjectId
        login: string
    }
}

const UserSchema: mongoose.Schema = new mongoose.Schema({
    login: {
        required: true,
        type: String,
        unique: 'Le nom d\'utilisateur `{VALUE}` est déjà utilisé'
    },
    mail: {
        required: true,
        type: String,
        unique: 'Le mail `{VALUE}` est déjà utilisé'
    },
    password: { required: true, type: String, select: false },
    profilPic: { type: mongoose.Schema.Types.ObjectId, ref: 'Image' },
}, { timestamps: true })

UserSchema.virtual('public').get(function (): object {
    return {
        id: this.id,
        mail: this.mail,
        profilPic: this.profilPic,
        login: this.login,
    }
})

UserSchema.pre<IUser>('save', function (next: NextFunction): void {
    // If user already have a picture no need to set the default one
    if (this.profilPic) {
        return next()
    }
    const imgProfil: IImage = new Image({
        name: 'default.png',
        link: config.assetsURI + 'user/profil/default.png',
        type: 'png',
        width: 502,
        height: 502,
    })
    Image.create(imgProfil).then((img: IImage) => {
        this.profilPic = img.id
        return next()
    }).catch((error: Error) => {
        return next(error)
    })
})

UserSchema.method('comparePassword',
    (password: string, hash: string): Promise<boolean> => {
        return bcrypt.compare(password, hash)
            .then((value: boolean) => {
                return value
            })
    })

UserSchema.method('hashPassword',
    (password: string): Promise<string> => {
        return bcrypt.hash(password, SALT_WORK_FACTOR)
            .then((hashedPassword: string) => {
                return hashedPassword
            })
    })

UserSchema.static('validEmail', (email: string): boolean => {
    return emailValidator.validate(email)
})

UserSchema.set('toJSON', {
    transform: (doc: IUser, ret: IUser): void => {
        ret.id = ret._id
        delete ret._id
        delete ret.__v
    }
})

UserSchema.plugin(beautifyUnique)
const esClient: elasticsearch.Client = new elasticsearch.Client({
    host: '0.0.0.0:9200'
})
UserSchema.plugin(mongoosastic, {
    esClient: esClient
})
export const User: mongoose.Model<IUser> =
    mongoose.model<IUser>('User', UserSchema)

1 个答案:

答案 0 :(得分:1)

您应该将此配置为可配置,并使用Docker内部DNS在主机之间进行通信。

在您的JavaScript代码中,最直接的操作是使用环境变量

const esClient: elasticsearch.Client = new elasticsearch.Client({
    host: process.env.ELASTICSEARCH_HOST
})

在开发代码时(本地,在Docker外部),您可以测试一个Elasticsearch(如果需要,可以在容器中)并设置ELASTICSEARCH_HOST=localhost:9200。部署它时,可以在docker-compose.yml中设置环境变量:

services:
  elasticsearch: 
    image: docker.elastic.co/elasticsearch/elasticsearch:6.1.1
    et: cetera
  api:
    build: .
    environment:
      ELASTICSEARCH_HOST: 'elasticsearch:9200'
    env_file:
      - .env
    ports:
      - "0.0.0.0:9000:9000"

(您不再需要links:;它没有害处,但是它不必要地冗长。我会使用Dockerfile在图像中安装应用程序代码并设置默认命令。)