How to use client.updateByQuery elasticsearch library for nodejs? [duplicate] - node.js

You have an Elasticsearch index with two docs:
[
{
"_index": "myIndex",
"_type": "myType",
"_id": "es1472002807930",
"_source": {
"animal": "turtle",
"color": "green",
"weight": 20,
}
},
{
"_index": "myIndex",
"_type": "myType",
"_id": "es1472002809463",
"_source": {
"animal": "bear",
"color": "brown"
"weight": 400,
}
}
]
Later, you get this updated data about the bear:
{
"color": "pink",
"weight": 500,
"diet": "omnivore",
}
So, you want to update the "color" and "weight" values of the bear, and add the "diet" key to the "bear" doc. You know there's only one doc with "animal": "bear" (but you don't know the _id):
Using the Nodejs driver, what updateByQuery syntax would update the "bear" doc with these new values?
(NOTE: this question has been entirely edited to be more useful to the SO community!)

The answer was provided by Val in this other SO:
How to update a document based on query using elasticsearch-js (or other means)?
Here is the answer:
var theScript = {
"inline": "ctx._source.color = 'pink'; ctx._source.weight = 500; ctx._source.diet = 'omnivore';"
}
client.updateByQuery({
index: myindex,
type: mytype,
body: {
"query": { "match": { "animal": "bear" } },
"script": theScript
}
}, function(err, res) {
if (err) {
reportError(err)
}
cb(err, res)
}
)

The other answer is missing the point since it doesn't have any script to carry out the update.
You need to do it like this:
POST /myIndex/myType/_update_by_query
{
"query": {
"term": {
"animal": "bear"
}
},
"script": "ctx._source.color = 'green'"
}
Important notes:
you need to make sure to enable dynamic scripting in order for this to work.
if you are using ES 2.3 or later, then the update-by-query feature is built-in
if you are using ES 1.7.x or a former release you need to install the update-by-query plugin
if you are using anything between ES 2.0 and 2.2, then you don't have any way to do this in one shot, you need to do it in two operations.
UPDATE
Your node.js code should look like this, you're missing the body parameter:
client.updateByQuery({
index: index,
type: type,
body: {
"query": { "match": { "animal": "bear" } },
"script": { "inline": "ctx._source.color = 'pink'"}
}
}, function(err, res) {
if (err) {
reportError(err)
}
cb(err, res)
}
)

For elasticsearch 7.4 you could use
await client.updateByQuery({
index: "indexName",
body: {
query: {
match: { fieldName: "valueSearched" }
},
script: {
source: "ctx._source.fieldName = params.newValue",
lang: 'painless',
params: {
newValue: "newValue"
}
}
}
});

Related

How to filter data by field that contains specific string in ElasticSearch, Node Js?

I have simple Node Js application.
I want get filtered data by Path field, that contains 'get' word.
For example my data is like below:
"_source": {
"time": "2020-03-12T01:25:41.61836-07:00",
"level": "Info",
"info": {
"IpAddress": "0.0.0.0",
"Path": "/api/test/getTest/1",
"QueryString": "",
"UserAgent": "",
"LogDate": "2020-03-12T08:25:41.6220806Z",
"Username": "cavidan.aliyev",
"NodeId": "123456"
}
In other words my entity object's structure like as below:
{
time,
level,
info: {
IpAddress,
Path,
QueryString,
UserAgent,
LogDate,
Username,
NodeId
}
}
My query is like below:
client.search({
index: collectionName,
body: {
from: (params.currentPage - 1) * params.pageSize,
size: params.pageSize,
"query": {
"bool": {
"must": mustArr,
"filter": [
{
"match_all": {}
}
]
}
}
}
}, function (err, res) {
if (err) {
reject(err);
}
else {
let result = res.hits.hits. map(x => x._source);
resolve(result);
}
});
How I can filter data by Path field, that contains 'get' word?
Please help me, thanks
You can make use of Wildcard Query inside the filter query you have. I'm assuming that you are making use of Standard Analyzer for info.Path field.
Note that for the sake of simplicity I've just mentioned what should be going inside the filter query you have.
If info.Path is nested type:
POST <your_index_name>/_search
{
"query": {
"bool": {
"filter": { <--- Note this
"nested": {
"path": "info",
"query": {
"wildcard": {
"info.Path": {
"value": "*get*"
}
}
}
}
}
}
}
}
If info.Path is object type:
POST <your_index_name>/_search
{
"query": {
"bool": {
"filter": { <--- Note this
"wildcard":{
"info.Path": "*get*"
}
}
}
}
}
Important Note: Wildcard search slows the query performance, and if you have a control on the Elasticsearch's index, then you should definitely look at ngram search model, which creates n-gram tokens at index-time as mentioned in this link.
Let me know if this helps!
If you don't want returned data with "get" keywords, your wildcard should type into the must_not.
For example:
POST <your_index_name>/_search
{
"query": {
"bool": {
"must_not":{
"filter": {
"wildcard":{
"info.Path": "*get*"
}
}
}
}
}
}

Create and update field Mongoose NodeJs

{
"_id": {
"$oid": "5a4e5b1d09fb590058bfdf86"
},
"name": "ProjectStore",
"imageURL": "none",
"longitude": 0,
"latitude": 0,
"rating": 5,
"leads": [
{
"customerId": "5a0c57db65a4931768716566",
"customerName": "testuser",
"interested": "testuser",
"_id": {
"$oid": "5a4e5b5409fb590058bfdf88"
}
}
],
"items": [
{
"name": "chat",
"categoryID": "5a0c2d292235680012bd12c9",
"semiCatID": "5a0c2d5f2235680012bd12cb",
"_id": {
"$oid": "5a4e5b3009fb590058bfdf87"
}
}
],
"__v": 2
}
I added my DB log, I try to write query that will let me update/push information to "LeadStatus".
This varible should be inside each object in the "leads" array.
I have the keys for the main id "5a4e5b1d09fb590058bfdf86"
I have the second key for the specific lead "5a4e5b5409fb590058bfdf88"
I just dont know how to write the query, for now i got this.... and got error.
Store.update(
{ _id: req.body.store._id, 'leads._id': 'req.body.lead._id', },
{ $set: { 'leads.$.LeadStatus': 'open' }, },
(err, result) => {
if (err) {
res.status(500)
.json({ error: 'Unable to update leads.', });
} else {
res.status(200)
.json(result);
}
}
);
Please Help,
Thanks.
I'm assuming you want the updated doc returned (you can choose to not do so as well, switch option new to false). You have to assign an object the value that you're updating and provide all the fields within the subfield you're updating, otherwise it will remove everything else. That's why you have to do a findOne first:
return Store.findOne({ '_id': "5a4e5b1d09fb590058bfdf86" })
.then((store) => {
if (!store) { // patient was not found for some reason
throw new Error(`Store id was not found`);
}
const options = {
new: true,
};
const updatedStore = Object.assign({}, store.toObject(), {
leads: [
{
"leadStatus": "open",
"customerId": "5a0c57db65a4931768716566",
"customerName": "testuser",
"interested": "testuser",
"_id": {
"$oid": "5a4e5b5409fb590058bfdf88"
}
}
],
});
return Store.findOneAndUpdate(
{ '_id': req.body.store._id },
{ $set: updatedStore },
options);
});
In case you're curious, you can build pre/virtual hooks off of findOneAndUpdate (no documentation for this), you won't lose anything from not using update.

How do you pass arguments to elasticsearch inline scripts using the nodejs driver?

The situation I want to solve is to update documents in a titles index with titles matching certain parameters using the nodejs driver for elasticsearch. The current query I'm using is this
client.updateByQuery({
index: "title",
type: "type",
body: {
"query": {
"bool": {
"filter": [
{ "term": { "tvSeriesId": 123} },
{ "term": { "tvSeriesNumber": 456} }
]
}
},
"script": {
"inline": "ctx._source.someRandomField = 'abc'"
}
}
}, function(err, res) {
if (err) {
reportError(err)
}
cb(err, res)
}
)
I want the script to be dynamic where I can pass a value to the script to change the value of a field. For example, the value of someRandomField could be declared in a previous variable before the query call. How can i pass that variable to the script?
You can use the script params to do this:
client.updateByQuery({
index: "title",
type: "type",
body: {
"query": {
"bool": {
"filter": [
{ "term": { "tvSeriesId": 123} },
{ "term": { "tvSeriesNumber": 456} }
]
}
},
"script": {
"inline": "ctx._source.someRandomField = newValue"
"params": {
"newValue": newRandomFieldValue
}
}
}
}, function(err, res) {
if (err) {
reportError(err)
}
cb(err, res)
}
)
newRandomFieldValue would be a variable in your Node.js code that is then used as a script parameter.

How to query documents where the query should have an and clause?

How do I use the nodejs driver for elastic search do achieve the following scenario. The scenario is that I have to update all documents in a "title" index where the tvSeries id is given and where the tvSeriesNumber is given?
I only want to match documents that fulfill both queries. I know that I should be using the client.updateByQuery method but I'm not sure what to put for the query body.
How about this? You probably need to modify the script in order to update whatever fields you want to.
client.updateByQuery({
index: "title",
type: "type",
body: {
"query": {
"bool": {
"filter": [
{ "term": { "tvSeriesId": 123} },
{ "term": { "tvSeriesNumber": 456} }
]
}
},
"script": {
"inline": "ctx._source.xyz = 'abc'"
}
}
}, function(err, res) {
if (err) {
reportError(err)
}
cb(err, res)
}
)

MongoDB: Update property of subarray just updates the first element

The matching element looks like that:
{
"_id": {
"$oid": "519ebd1cef1fce06f90e3157"
},
"from": "Tester2",
"to": "Tester",
"messages": [
{
"username": "Tester2",
"message": "heeey",
"read": false
},
{
"username": "Tester",
"message": "hi!",
"read": false
},
{
"username": "Tester2",
"message": "test",
"read": false
}
],
}
Now I try to set the read property to the current date just of the subelements where the username is not equal to Tester:
var messages = db.collection('messages');
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157"),
messages: {
$elemMatch: { username: { $ne: "Tester" } }
}
},
{ $set: { 'messages.$.read': new Date() } },
{ multi: true }, function(error, result) {
console.log(error);
console.log(result);
});
But just the first messages subelement read property updates:
{
"_id": {
"$oid": "519ebd1cef1fce06f90e3157"
},
"from": "Tester2",
"to": "Tester",
"messages": [
{
"username": "Tester2",
"message": "heeey",
"read": {
"$date": "2013-01-01T00:00:00.000Z"
}
},
{
"username": "Tester",
"message": "hi!",
"read": false
},
{
"username": "Tester2",
"message": "test",
"read": false
}
],
}
What's wrong with the code?
I'm using node.js v0.10.8 and MongoDB v2.4.3 together with node-mongodb-native.
There's nothing wrong with your code; the $ operator contains the index of the first matching array element. The {multi: true} option only makes the update apply to multiple documents, not array elements. To update multiple array elements in a single update you must specify them by numeric index.
So you'd have to do something like this:
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157")
},
{ $set: {
'messages.0.read': new Date(),
'messages.2.read': new Date()
} },
function (err, result) { ... }
);
This is similar to question: How to Update Multiple Array Elements in mongodb
var set = {}, i, l;
for(i=0,l=messages.length;i<l;i++) {
if(messages[i].username != 'tester') {
set['messages.' + i + '.read'] = new Date();
}
}
.update(objId, {$set:set});
I think ArrayFilters can be used in this case
I know it's a little bit late to answer this question but if you use the $[] operator, it will do the trick.
more details here
https://www.mongodb.com/docs/manual/reference/operator/update/positional-all/
I was able to use it inside an updateMany operation, but it should work also for update
var messages = db.collection('messages');
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157"),
messages: {
$elemMatch: { username: { $ne: "Tester" } }
}
},
{ $set: { 'messages.$[].read': new Date() } },
{ multi: true }, function(error, result) {
console.log(error);
console.log(result);
});

Resources