Elasticsearch cache - node.js

I'm trying to delete a document from buzz index in ElasticSearch. Just after deleting I fetch a list of existing entries and deleted item is still in the list.
async.series([
function(callback) {
self._db.DELETE('buzz/news/' + self._id, {}, {}, function(err, response) {
return callback(err);
});
},
Requesting entry by id states found:false
function(callback) {
self._db.GET('buzz/news/' + self._id, {
v: Date.now()
}, {}, function(err, result) {
console.log('result: ', JSON.stringify(result));
callback(err);
});
},
Performing a search by criteria still returns this entry in the list.
function(callback) {
self._db.GET('buzz/news/_search', {
v: Date.now()
}, qq, function(err, result) {
console.log('result: ', JSON.stringify(result.hits.hits.map(function(hit) {
return hit._id;
})));
callback(err);
});
},], function(err){...});
All the code is inside one async.series.
Here's the criteria:
var qq = {
"query": {
"filtered": {
"query": {
"query_string": {
"fields": ["title"],
"query": "*"
}
},
"filter": {
"bool": {
"must": [],
"_cache": false
}
}
}
},
"sort": [{
"pubDate": "desc"
}],
"from": "0",
"size": "30"
}
db.GET and db.DELETE are just wrappera to simple HTTP request.
'Manual' Kibana or Postman reuquests to get entries list after deletion are ok. Item is not in the list. So everything is working correctly, except of, maybe, some 'caching' issue.

You should refresh the indices for the index to be updated before fetching it.
client.indices.refresh([params, [callback]])

Related

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)
}
)

Update many documents in mongoDB with different values

I am trying to update two documents in mongoDB, with two different values. I made it with two different callbacks, but is it possible to do it with only one request?
My solution:
mongo.financeCollection.update(
{ 'reference': 10 },
{ $push:
{ history: history1 }
}, function (err){
if (err){
callback (err);
}
else {
mongo.financeCollection.update(
{ 'reference': 20 },
{ $push:
{ history: history2 }
}, function (err){
if (err){
callback(err);
}
else {
callback(null);
}
});
}
});
Sorry if it is a stupid question but I just want to optimize my code!
Best to do this update using the bulkWrite API. Consider the following example for the above two documents:
var bulkUpdateOps = [
{
"updateOne": {
"filter": { "reference": 10 },
"update": { "$push": { "history": history1 } }
}
},
{
"updateOne": {
"filter": { "reference": 20 },
"update": { "$push": { "history": history2 } }
}
}
];
mongo.financeCollection.bulkWrite(bulkUpdateOps,
{"ordered": true, "w": 1}, function(err, result) {
// do something with result
callback(err);
}
The {"ordered": true, "w": 1} ensures that the documents will be updated on the server serially, in the order provided and thus if an error occurs all remaining updates are aborted. The {"w": 1} option determines the write concern with 1 being a request acknowledgement that the write operation has propagated to the standalone mongod or the primary in a replica set.
For MongoDB >= 2.6 and <= 3.0, use the Bulk Opeartions API as follows:
var bulkUpdateOps = mongo.financeCollection.initializeOrderedBulkOp();
bulkUpdateOps
.find({ "reference": 10 })
.updateOne({
"$push": { "history": history1 }
});
bulkUpdateOps
.find({ "reference": 20 })
.updateOne({
"$push": { "history": history2 }
});
bulk.execute(function(err, result){
bulkUpdateOps = mongo.financeCollection.initializeOrderedBulkOp();
// do something with result
callback(err);
});

Passing two query results into a response

I have a query that fetches the top 5 people for a leaderboard. In robomongo this query works fine.
When I do something like
var leaderboard = User.find({points: {$exists: true}}, {
"firstname": 1,
"lastname": 1,
"points": 1
}).sort({points : -1}).limit(5)
console.log('leaderboard');
I get a lot of meaningless json with [object] almost everywhere.
How would I execute this query for use with mongoose + express so I can pass through to the view as an array of
firstname, lastname, points
So I can loop it through in the view?
My complete code is
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
User.find({}, function(err, docs) {
// console.log(docs);
});
// Get total points after submit
var leaderboard = User.find({points: {$exists: true}}, {
"firstname": 1,
"lastname": 1,
"points": 1
}).sort({points : -1}).limit(5).toArray();
console.log(leaderboard);
User.find({
points: {
$exists: true
}
}, function(err, docs) {
if(err){
console.log(err);
//do error handling
}
//if no error, get the count and render it
var count = 0;
for (var i = 0; i < docs.length; i++) {
count += docs[i].points;
}
var totalpoints = count;
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: totalpoints
});
});
} else {
// not logged in
return res.redirect('/login');
}
});
So you are really only returning a cursor here and not executing the query. You can of course always nest the queries, but you can be a bit nicer and use async.waterfall to avoid the indenting mess.
Also I would use .aggregate() rather than looping all the documents just to get a total. And mongoose automatically converts results to an array, so the .toArray() is not required here:
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
async.waterfall(
[
function(callback) {
User.find(
{ "points": { "$exists": true } },
{
"firstname": 1,
"lastname": 1,
"points": 1
}
).sort({points : -1}).limit(5).exec(callback);
},
function(leaderboard,callback) {
User.aggregate(
[
{ "$match": { "points": { "$exists": true } }},
{ "$group": {
"_id": null,
"totalpoints": { "$sum": "$points" }
}}
],
function(err,result) {
callback(err,result,leaderboard)
}
);
}
],
function(err,result,leaderboard) {
if (err) {
console.log(err);
//do error handling
} else {
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: result[0].totalpoints,
leaderboard: leaderboard
});
}
}
);
} else {
// not logged in
return res.redirect('/login');
}
});
So you get your leaderboard result and just put it in the response, much as is done in the example here.
An alternate approach is using async.parallel, since you don't need the output of the first query within the second. In that case the results of both queries are sent to the callback at the end, much like above. Again you just use the results in your final response.
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
async.parallel(
{
"leaderboard": function(callback) {
User.find(
{ "points": { "$exists": true } },
{
"firstname": 1,
"lastname": 1,
"points": 1
}
).sort({points : -1}).limit(5).exec(callback);
},
"totalpoints":function(callback) {
User.aggregate(
[
{ "$match": { "points": { "$exists": true } }},
{ "$group": {
"_id": null,
"totalpoints": { "$sum": "$points" }
}}
],
function(err,result) {
callback(err,result[0].totalpoints)
}
);
}
},
function(err,results) {
if (err) {
console.log(err);
//do error handling
} else {
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: results.totalpoints,
leaderboard: results.leaderboard
});
}
}
);
} else {
// not logged in
return res.redirect('/login');
}
});

Node.JS + MongoDB aggregation framework average value

I have been thinking of using Node and MongoDb for a ratings system for an Android app, so far I have implemented Create, Update and Delete fine but am at a loss as to what to do when I want to get the average rating back from the DB. Here is the data structure I gave decided upon:
[
{
"_id": "1",
"ratings": [
{
"value": "5",
"user": "CoolUser1"
},
{
"value": "4",
"user": "CoolUser2"
}
]
},
{
"_id": "2",
"ratings": [
{
"value": "4",
"user": "CoolUser3"
},
{
"value": "2",
"user": "CoolUser4"
}
]
}
]
Basically what I need is the average value in ratings for a given _id.
Here is the Update method I am using to give a sense of how I have setup the mongoDB connection:
exports.updateRating = function(req, res) {
var id = req.params.id;
var rating = req.body;
console.log('Updating a rating: ' + id);
console.log(JSON.stringify(rating));
ratings.update({'_id': id},
{$push: rating},
{upsert:true},
function(err, result) {
if (err) {
console.log('Error updating rating: ' + err);
res.send({'error':'An error has occurred'});
} else {
console.log('' + result + ' document(s) updated');
res.send(rating);
}
}
);
}
Is it critical to store ratings.value as a string?
This will work, but only if you convert rating to number before safe
ratings.aggregate([
{ $match: { _id: id } },
{ $unwind: '$ratings' },
{ $group: { _id: null, avg: { $avg: '$ratings.value' } } }
], function(err, result) {
console.log(result);
db.close();
});

Resources