简体   繁体   中英

NodeJs connecting to elasticsearch

I am trying to update my REST API to use elasticsearch, I found the mongoosastic plugin which seems to be the best solution but I cannot find out how to connect elasticsearch with the node server.

According to the mongoosastic doc ( https://github.com/mongoosastic/mongoosastic ), the code in user.model.ts should work, however when I create an user then hit http://localhost:9200/user/user/_search the body is the following 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
}

When creating a new user, I have the following error in the console :

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

So I believe the error is just some config options that I have misconfigured but I can't find out what I have done wrong.

Going to http://localhost:9200/_cluster/health via the browser give me :

{
    "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
}

I also use 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)

You should make this configurable, and use the Docker-internal DNS to communicate between hosts.

In your JavaScript code, the most straightforward thing to do is to use an environment variable

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

When you're developing your code (locally, outside of Docker) you can stand up a test Elasticsearch (in a container if you'd like) and set ELASTICSEARCH_HOST=localhost:9200 . When you go to deploy it, you can set the environment variable in your 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"

(You do not need links: any more; it's not harmful, but it's unnecessarily verbse. I'd use the Dockerfile to install your application code in the image and set the default command.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM