How to create or replace a document in Elasticsearch? - javascript

I'm just trying to create or replace a document, but the API docs aren't clear on how to do this.
The upsert example shows a document being created with a counter value of 1:
client.update({
index: 'myindex',
type: 'mytype',
id: '777',
body: {
script: 'ctx._source.counter += 1',
upsert: {
counter: 1
}
}
}, function(error, response) {
// ...
})
I don't need the increment script, so I try to just send an upsert by itself, but I get a 400 Validation Failed: 1: script or doc is missing:
client.update({
index: "testindex",
type: "testType",
id: 1234,
body: {
upsert: {
itworks: true,
ok: "oh yeah"
}
}
})
Well, let's try something silly and include the doc twice; once under doc, which would presumably replace an existing document if there was one, and once under upsert, which would be used to create a new document otherwise:
client.update({
index: "testindex",
type: "testType",
id: 1234,
body: {
upsert: {
itworks: true,
ok: "oh yeah"
},
doc: {
itworks: true,
ok: "oh yeah"
}
}
})
That actually worked, 201 Created. 200 OK when I repeat it to replace the doc with a different one:
client.update({
index: "testindex",
type: "testType",
id: 1234,
body: {
upsert: {
whatever: "man"
},
doc: {
whatever: "man"
}
}
})
But when I check the doc (client.get({index: "testindex", type: "testType", id: 1234})), I see that instead of replacing the existing doc, the new doc was merged with it.
How do you just replace a doc in Elasticsearch, with the standard Node client? The HTTP API makes it look so simple, but I've tried a bunch of permutations of that in the Node client without success. Is there no replace command? Do I have to delete and then create?

In Elasticsearch, to replace a document you simply have to index a document with the same ID and it will be replaced automatically.
If you would like to update a document you can either do a scripted update, a partial update or both.
To do a partial doc update you simply
Partial document update:
client.update({
index: 'myindex',
type: 'mytype',
id: '1',
body: {
// put the partial document under the `doc` key
doc: {
title: 'Updated'
}
}
}, function (error, response) {
// ...
})
Scripted update:
client.update({
index: 'myindex',
type: 'mytype',
id: '1',
body: {
script: 'ctx._source.tags += tag',
params: { tag: 'some new tag' }
}
}, function (error, response) {
// ...
});
More info in Elasticsearch JS documentation

This is one of the reasons I always use index instead of create:
client.index({
"index": 'index-name',
"type": 'mapping type',
"id": 'id you want to create/replace',
'body': JSON.stringify(obj)
}, function (err, resp) {
if (err) {
console.error(err['message'])
}
else {
console.log(resp);
return resp
}
});

Related

Jasmine javascript : how to check response for creating a post some properties as it been created?

I am having the following response; how can I verify its been created by having a response contains userid ?
({
id: '612bd3f42ca01806398da144',
data: Object({
createdOn: '2021-08-29T18:37:39.693Z',
lastUpdatedBy: null,
userId: '60f469cf784379051298e96d',
displayName: 'Nadia',
postText: null,
postImages: [],
pluginInstance: Object({
pluginInstanceId: '1627334094776-047554258642281355',
pluginInstanceTitle: 'communityFeedPlugin'
}),
isPublic: false,
_buildfire: Object({
index: Object({
array1: [Object({
string1: 'userId_60f469cf784379051298e96d'
}), Object({
string1: 'displayName_nadia'
}), Object({
string1: 'pluginTitle_communityfeedplugin'
}), Object({
string1: 'isPublic_0'
})]
})
})
}),
tag: 'posts'
})
If you use express rest api you can use like this.
const exists = await posts.findOne({ "data.userid": userid });
if(exists) {
return res.json({ success: false, message: "User already exists"})
}
If I get it right, you need to know if the userId property is present/truthy.
Given that the response is stored in a jsonBody variable, use an if statement like so:
if (jsonBody.data.userId) {
...
}
Also, make sure the response is parsed in JSON properly, so that you can navigate it.
If you need to know whether the response exists in the first place or not, you can add another if check:
if (jsonBody) {
...
if (jsonBody.data.userId) {...}
...
}
Or join both checks in one if statement:
if (jsonBody && jsonBody.data.userId) {...}
Anyways, it's good practice to consume backends that comunicate correctly the request status, so that you can write cleaner code avoiding this types of checks and relying on status codes for the most part.
worked in this form :
it('Add public post with post image and text', function(done) {
Posts.addPost({isPublic : true , postImages :'image test',postText : 'post text sample'},(err,resp) => {
expect(resp.data).toEqual(jasmine.objectContaining({
postImages: 'image test',
postText:'post text sample',
userId: authManager.currentUser._id,
isPublic: true
}));
done();
});
});

Dynamically return config value (node.js)

I'm building a content middleware which gather contents from our external publishers. The publishers will share their contents either in rss or json and the key/value field would be different from each other. To make thing easier, I created a config file where I can pre-defined the key/value and the feed type. The problem is, how can I dynamically return this config value based on publishers name.
Example: To get Publisher #1 feed type, I just can use config.articles.rojak_daily.url_feed
my config file /config/index.js
module.exports = {
batch:100,
mysql: {
database: process.env.database,
host: process.env.host,
username: process.env.username,
password: process.env.password
},
articles:{
rojak_daily:{ // Publisher 1
url: 'xxx',
url_feed: 'rss',
id: null,
Name: 'title',
Description: 'description',
Link: 'link',
DatePublishFrom: 'pubDate',
LandscapeImage: 's3image',
SiteName: 'Rojak Daily',
SiteLogo: null
},
rojak_weekly:{ // publisher 2
url: 'xxx',
url_feed: 'json',
id: null,
Name: 'Name',
Description: 'Desc',
Link: 'link',
DatePublishFrom: 'pubDate',
LandscapeImage: 's3image',
SiteName: 'Rojak Weekly',
SiteLogo: null
}
}
}
my main application script
const config = require('#config'); // export from config file
class Main {
constructor(){
this.publishers = ['rojak_daily','rojak_weekly'];
}
// Main process
async startExport(){
try{
for(let publisher of this.publishers){
const feedType = await this.getFeedType(publisher)
const result = (feedType == 'rss')? await this.rss.start(publisher): await this.json.start(publisher)
return result
}
}catch(err){
console.log("Error occured: ", err)
}
}
// Get feed type from config
async getFeedType(publisher){
return await config.articles.rojak_daily.url_feed;
// this only return publisher 1 url feed.
// my concern is to dynamically passing variable
// into this config file (example: config.articles.<publisher>.url_feed)
}
}
module.exports = Main
async getFeedType(publisher){
return await config.articles[publisher].url_feed;
}
You can access properties of objects by variable
You could either loop over the articles by using Object.entries(articles) or Object.values(articles) in conjunction with Array.prototype.forEach(), or since you already have the name of the publisher you could access that entry with config.articles[publisher].url_feed, like so:
const config = {
articles: {
rojak_daily: { // Publisher 1
url: 'xxx',
url_feed: 'rss',
id: null,
Name: 'title',
Description: 'description',
Link: 'link',
DatePublishFrom: 'pubDate',
LandscapeImage: 's3image',
SiteName: 'Rojak Daily',
SiteLogo: null
},
rojak_weekly: { // publisher 2
url: 'xxx',
url_feed: 'json',
id: null,
Name: 'Name',
Description: 'Desc',
Link: 'link',
DatePublishFrom: 'pubDate',
LandscapeImage: 's3image',
SiteName: 'Rojak Weekly',
SiteLogo: null
}
}
}
const publishers = ['rojak_daily', 'rojak_weekly']
function getFeedType(publisher) {
return config.articles[publisher].url_feed;
}
publishers.forEach(publisher => console.log(getFeedType(publisher)));

Mongoose .findOne Error returns the model found?

So I'm trying to access this account by using the findOne function in mongoose, and I'm trying to console.log the error, but the error is just the correct model found.. once I find the correct model I want to access one of the nested objects in the schema so I can edit the value.
I'm not sure why this is happening, below I put the code as well as the error that was logged into the console, I can provide more if needed.
let accountSchema = mongoose.Schema({
username:{
type: String,
required: true,
index: true,
unique: true,
},
password:{
type: String,
required: true,
},
money:{
type: Number,
},
inventory: { type: [{
weed: { type: Number },
coke: { type: Number },
}]},
});
mp.events.addCommand('coke', (player) => {
console.log(player.name);
Account.findOne({username: 'a'}, function(acc, err) {
if(err) return console.log(err);
console.log(acc.username);
acc.inventory[1] = acc.inventory[1] + 1;
acc.save(function(err){
if(err) return player.outputChatBox('Not logged in');
player.outputChatBox('Added 1 coke');
});
});
});
(Console) {"_id":"5b6acbbbc285477e39514cb9","username":"a","password":"$2a$10$XABqooqFRINYVdJ79.i2E.5xdpitRrfZxUBmIPAZjjaXKvvLDc2y2","money":5000,"inventory":[{"_id":"5b6acbbbc285477e39514cbb","weed":0},{"_id":"5b6acbbbc285477e39514cba","coke":0}],"__v":0}
The callback function for the .findOne method has the following signature:
function (err, obj) {
}
You are using the arguments in the wrong order - the error object is the first argument and the object found is the second one.
The .findOne method callback must have the following parameters function (err, res). So you are setting them in a reversed order.
Check http://mongoosejs.com/docs/api.html#model_Model.findOne

Sails 1.0, How to call customToJSON from an Action2?

Well, I have a user model that needs to implement customToJSON and make the removal of the password for the object to be returned json.
When I put the "responseType" in "exits" as "json" everything happens fine, and the password is taken out of the response. However, the responseType: "json" will be deprecated according to the message in the terminal that sends the responseType empty, however the customToJSON is not called. Can anyone help me understand this?
this is the model code:
[...]
attributes: {
name: {
type: 'string',
required: true,
},
email: {
type: 'string',
required: true,
},
password: {
type: 'string',
minLength: 6,
required: true,
},
},
customToJSON: function() {
return _.omit(this, ['password']);
},
[...]
this is the action code:
module.exports = {
friedlyName: 'Users List',
description: 'User list -> all users',
exits: {
success: {
}
},
fn: async (inputs, exits) => {
var users = await User.find();
return exits.success(users);
}
}
this is the message if you put the "responseType: 'json'":
The json response type will be deprecated in an upcoming release. Please use `` (standard) instead (i.e. remove responseType from the success exit.)
I defined custom responses inside api/responses folder.
So, for example... for 200 OK response create api/responses/ok.js
and then inside action2 controller method add this to exits:
exits: {
success: {
description: 'Returns ok response from api/responses/ok.js',
responseType: 'ok'
}
}, ...
Example of my simple api/responses/ok.js
module.exports = function ok(data) {
return this.res.status(200).json(data);
};
You can create responses for each status/exit you need and later on it's easy to maintain exits... I have responses for: badRequest, serverError, forbidden, notFound and unauthorized

When using Joi with Hapi, how does one setup a require on one key but allow any and all other keys?

I'm trying to write a Joi validation for a JSON object coming into a Hapi handler. So far the code looks like this:
server.route({
method: 'POST',
path: '/converge',
handler: function (request, reply) {
consociator.consociate(request.payload)
.then (function (result) {
reply (200, result);
});
},
config: {
validate: {
payload: {
value: Joi.object().required().keys({ knownid: Joi.object() })
}
}
}
});
You can see the Joi object validation so far in the config: validate: code section above. The JSON coming in looks like this.
"key": '06e5140d-fa4e-4758-8d9d-e707bd19880d-testA',
"value": {
"ids_lot_args": {
"this_id": "stuff",
"otherThign": "more data"
},
"peripheral_data": 'Sample peripheral data of any sort'
}
In this JSON above the key and value at the root of the object are required, and the section called ids_lot_args is required. The section that starts with peripheral_data could be there or not, or could be any other JSON payload. It doesn't matter, only key and value at the root level and ids_lot_args inside the value are required.
So far, I'm stumbling through trying to get the Joi validation to work.
Any ideas on how this should be setup? The code repo for Joi is located at https://github.com/hapijs/joi if one wants to check it out. I've been trying the allow all functions on objects to no avail so far.
You just need to call the unknown() function on the value object:
var schema = Joi.object({
key: Joi.string().required(),
value: Joi.object({
ids_lot_args: Joi.object().required()
}).unknown().required()
});
You can use the "allowUnknown" parameter:
validate : {
options : {
allowUnknown: true
},
headers : {
...
},
params : {
...
},
payload : {
...
}
}
}
Try using Joi.any()
server.route({
method: 'POST',
path: '/converge',
handler: function (request, reply) {
consociator.consociate(request.payload)
.then (function (result) {
reply (200, result);
});
},
config: {
validate: {
payload: {
key: Joi.string().required(),
value: Joi.object({
ids_lot_args: Joi.object().required(),
peripheral_data: Joi.any()
})
}
}
}});

Categories