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.