Javascript function to return Elasticsearch results - javascript

I'm trying to write a JavaScript function that returns results of an Elasticsearch v5 query. I can't figure out where and how to include 'return' in this code. With the following, segmentSearch(id) returns a Promise object,{_45: 0, _81: 0, _65: null, _54: null}.
_65 holds an array of the correct hits, but I can't figure out how to parse it. The console.log(hits) produces that same array, but how can I return it from the function?
var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
host: 'localhost:9200',
log: 'trace'
});
segmentSearch = function(id){
var searchParams = {
index: 'myIndex',
type: 'segment',
body: {
query: {
nested : {
path : "properties",
query : {
match : {"properties.source" : id }
},
inner_hits : {}
}
}
}
}
return client.search(searchParams).then(function (resp) {
var hits = resp.hits.hits;
console.log('hits: ',hits)
return hits;
}, function (err) {
console.trace(err.message);
});
}

I would instanitate a new array outside of your client.search function in global scope and array.push your 'hits' Then access your newly filled array.
let newArr = [];
client.search(searchParams).then(function (resp) {
for(let i = 0; i < resp.hits.hits.length; i++){
newArr.push(resp.hits.hits[i]);
}
console.log('hits: ',newArr)
return newArr;
}, function (err) {
console.trace(err.message);
});

First of all, elasticsearch js client is working with Promise ( I think using callback is also possible).
Using Promise is a good way to handle asynchronous computation.
In your question, you have already done something with a promise:
var search = function(id)
{
var searchParams = { /** What your search is **/}
return client.search(searchParams)
}
This call is returning a Promise.
If we consider handleResponse as the function in your then
var handleResponse = function(resp)
{
var hits = resp.hits.hits;
console.log('hits: ',hits)
return hits; //You could send back result here if using node
}
In your code, handleResponse is called once the promise is fullfield. And in that code you are processing data. Don't forget you are in asynchronious, you need to keep working with promise to handle hits.
By the way, in your question, what you have done by using "then" is chaining Promise. It is normal to have Promise.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
Promise.prototype.then is returning a Promise.
var segmentSearch = function ( id )
{
return search
.then( handleResponse //here you got your hit )
.then( function ( hits )
{
console.log(hits) //and here you have done something with hits from search.
})
}

I neglected to post my fix, sorry about that. The sequence is to create searchParams, perform client.search(searchParams) which returns a Promise of hits, then process those hits:
segmentSearch = function(obj){
// retrieve all segments associated with a place,
// populate results <div>
let html = ''
var plKeys = Object.keys(obj)
var relevantProjects = []
for(let i = 0; i < plKeys.length; i++){
relevantProjects.push(obj[plKeys[i]][0])
var searchParams = {
index: 'myIndex',
type: 'segment',
body: {
query: {
nested : {
path : "properties",
query : {
match : {"properties.source" : id }
},
inner_hits : {}
}
}
}
}
client.search(searchParams).then(function (resp) {
return Promise.all(resp.hits.hits)
}).then(function(hitsArray){
...write html to a <div> using hits results
}
}

Related

Infinite functions calls like 'string'.replace().replace()

I'm not really sure how to explain so I will start with the output.
I need to return this:
{
replies:
[
{ type: 'text', content: 'one' }
{ type: 'text', content: 'two' }
{ type: 'text', content: 'three' }
],
conversation: {
memory
}
}
And I wanted to return that through in-line statement.
So I would like to call something like:
reply.addText('one').addText('two').addText('three').addConversation(memory)
Note that addText can be called infinite times while addConversation can be called only one time. Also conversation is optional, in that case, if conversation is absent the conversation object should not appear in the output.
To create a custom structured object use a constructor, say Reply.
To call instance methods on the return value of method calls, return the instance object from the method.
Choices to prevent multiple additions of conversation objects include throwing an error (as below) or perhaps logging a warning and simply not add additional objects after a first call to addConversation.
Write the code to implement the requirements.
For example using vanilla javascript:
function Reply() {
this.replies = [];
}
Reply.prototype.addText = function( content) {
this.replies.push( {type: "text", content: content});
return this;
}
Reply.prototype.addConversation = function( value) {
if( this.conversation) {
//throw new Error("Only one conversation allowed");
}
this.conversation = {conversation: value};
return this;
};
Reply.prototype.conversation = null;
// demo
var reply = new Reply();
reply.addText( "one").addText("two").addConversation("memory?");
console.log( JSON.stringify( reply, undefined," "));
(The console.log uses JSON stringify to avoid listing inherited methods)
A possible implementation is to create a builder as follows:
function create() {
const replies = []; // store all replies in this array
let conversation; // store the memory here
let hasAddConversationBeenCalled = false; // a state to check if addConversation was ever called
this.addText = function(content) {
// add a new reply to the array
replies.push({
type: 'text',
content
});
return this; // return the builder
};
this.addConversation = function(memory) {
if (!hasAddConversationBeenCalled) { // check if this was called before
// if not set the memory
conversation = {
memory
};
hasAddConversationBeenCalled = true; // set that the memory has been set
}
return this; // return the builder
}
this.build = function() {
const reply = {
replies
};
if (conversation) { // only if converstation was set
reply.conversation = conversation; // add it to the final reply object
}
return reply; // finally return the built respnse
}
return this; // return the new builder
}
You can then use it as follows:
const builder = create();
const reply = builder.addText('one').addText('two').addText('three').addConversation({}).build();
Here is a link to a codepen to play around with.
If you specifically want to add assemble this via multiple function calls, then the builder pattern is your best bet, as vader said in their comment.
However, if the goal is to simply create shorthand for concisely building these objects, it can be done using a function that takes the list of text as an array.
const buildObject = (textArray, memory) => {
return Object.assign(
{},
{
replies: textArray.map(x => {
return {
type: 'text',
value: x
}
})
},
memory ? {conversation: memory} : null
)
}
var memory = { };
//with memory
console.log(buildObject(['one', 'two', 'three'], memory ))
//without memory
console.log(buildObject(['one', 'two', 'three']));
Fiddle example: http://jsfiddle.net/ucxkd4g3/

Passing gathered value in API call from first method to second in same object

I have a object which contains two methods. The first one is calling API and storing response in variable.
In second method I execute first one using this.nameOfFirstMethod() and then I want to do some calculation basing on numbers which I collected in API call in first method.
To make it more clear take a look at code, start reading at second method:
this.currencyConverter = {
getRatio: function(selectedCurrency) {
var selectedCurrency = selectedCurrency;
$http({
url: 'http://api.fixer.io/latest?base='+selectedCurrency+'&symbols=PLN,CHF,EUR,USD,GBP',
method: 'GET'
})
.then(function(response) {
var currentCurrency = {
toPLN: response.data.rates.PLN,
toCHF: response.data.rates.CHF,
toEUR: response.data.rates.EUR,
toUSD: response.data.rates.USD,
toUSD: response.data.rates.GBP
};
console.log("Succesful store currentCurrency");
return currentCurrency;
}, function(response) {
console.log("Problem occure while downloading money current currency!");
console.log(response.data);
});
},
convertMoney: function(selectedCurrency,priceField) {
var priceField = priceField;
var selectedCurrency = selectedCurrency;
console.log('selectedCurrency in service: '+selectedCurrency);
console.log('priceField in service: '+priceField);
this.getRatio(selectedCurrency);
console.log(currentCurrency);
/*
var converted = {
PLN: function() { return priceField * $rootScope.currentCurrency.toPLN; },
USD: function() { return priceField * $rootScope.currentCurrency.toUSD; },
EUR: function() { return priceField * $rootScope.currentCurrency.toEUR; },
CHF: function() { return priceField * $rootScope.currentCurrency.toCHF; },
GBP: function() { return priceField * $rootScope.currentCurrency.toGBP; }
};
*/
}
}
Here is GIST of same code if someone doesn't like StackOverflow styling:
https://gist.github.com/anonymous/e03de4de1af407bf70f4038acd77c961
Please open this gist because I will now explain basing on specific line.
So in the line 30 I execute first method.
In line 9 I'm storing retrieved data in variable and in line 17 returning this data (in order to use it in second method).
Finally I want to console.log this in second object in line 32 (for now only console.log I will do my maths later on).
It doesn't work with this return, the line with console.log in second method cause following error:
ReferenceError: currentCurrency is not defined
you don't assign the return value of getRatio to a variable
it should be
currentCurrency = this.getRatio(selectedCurrency);
And you should work with promises correctly.
So change it to something like this (not tested)
this.currencyConverter = {
getRatio: function(selectedCurrency) {
var selectedCurrency = selectedCurrency;
return $http({
url: 'http://api.fixer.io/latest?base='+selectedCurrency+'&symbols=PLN,CHF,EUR,USD,GBP',
method: 'GET'
})
.then(function(response) {
var currentCurrency = {
toPLN: response.data.rates.PLN,
toCHF: response.data.rates.CHF,
toEUR: response.data.rates.EUR,
toUSD: response.data.rates.USD,
toUSD: response.data.rates.GBP
};
console.log("Succesful store currentCurrency");
return currentCurrency;
}, function(response) {
console.log("Problem occure while downloading money current currency!");
console.log(response.data);
});
},
convertMoney: function(selectedCurrency,priceField) {
var priceField = priceField;
var selectedCurrency = selectedCurrency;
console.log('selectedCurrency in service: '+selectedCurrency);
console.log('priceField in service: '+priceField);
var currentCurrency = this.getRatio(selectedCurrency);
currentCurrency.then(res => console.log(res));
//console.log(currentCurrency);
}
}

Javascript Promises: Iterate over all object keys arrays and then resolve

I have this JS object:
let setOfWords = {
"nouns": [
"work",
"construction",
"industry"
],
"verbs": [
"work"
],
}
I'm using the google translate API which calls a REST resource, so I need to wait for the response of each translation and then resolve the same object structure but with the translated words.
function translateByCategory(){
let translatedObj = {};
return new Promise(function(resolve, reject){
Object.keys(obj).forEach(function(category){
if (translatedObj[category] == undefined) {
translatedObj[category] = [];
}
setOfWords.forEach(function(word){
google.translate(word, 'es', 'en').then(function(translation){
translatedObj[category].push(translation.translatedText);
});
});
// return the translatedObj until all of the categories are translated
resolve(translatedObj);
});
});
}
You can use Promise.all() to wait for all promise fulfillments (or first rejection):
var translateRequests = [];
Object.keys(setOfWords).forEach(function(category){
setOfWords[category].forEach(function(word){
translateRequests.push(google.translate(word, 'es', 'en'));
});
});
});
Promise.all(translateRequests).then(function(translateResults){
//do something with all the results
});
See the docs: Promise.all()
Individual promises need to be aggregated with Promise.all() and fundamentally, that's what's missing.
But you can do better by reducing the number of calls to the Google service.
The Google translate API allows multiple text strings to be translated in one hit by passing an array of words instead of one word per call, giving you a performance advantage though probably not price advantage - google currently charges for its translation service "per character", not "per call".
I can't find any documentation for google.translate() but, with a few assumptions, you may be able to write :
function translateByCategory(obj, sourceCode, targetCode) {
let translatedObj = {};
var promises = Object.keys(obj).map(function(key) {
return google.translate(obj[key], sourceCode, targetCode).then(function(translations) {
translatedObj[key] = translations.map(function(t) {
return t.translatedText || '-';
});
}, function(error) {
translatedObj[key] = [];
});
});
return Promise.all(promises).then(function() {
return translatedObj;
});
}
If that doesn't work, then this documentation explains how to call google's RESTful translation service directly.
You should be able to write :
function translateTexts(baseParams, arrayOfStrings) {
let queryString = baseParams.concat(arrayOfStrings.map(function(str) {
return 'q=' + encodeURIComponent(str);
})).join('&');
return http.ajax({ // some arbitrary HTTP lib that GETs by default.
url: 'https://translation.googleapis.com/language/translate/v2?' + queryString,
}).then(function(response) {
return response.data.translations.map(function(t) {
return t.translatedText || '-';
});
}, function(error) {
translatedObj[key] = []; // on error, default to empty array
});
}
function translateByCategory(obj, sourceCode, targetCode) {
let baseParams = [
'key=' + MY_API_KEY, // from some outer scope
'source=' + sourceCode, // eg 'en'
'target=' + targetCode // eg 'es'
];
let translatedObj = {};
let promises = Object.keys(obj).map(function(key) {
return translateTexts(baseParams, obj[key]).then(function(translations) {
translatedObj[key] = translations;
}, function(error) {
translatedObj[key] = []; // on error, default to empty array
});
});
return Promise.all(promises).then(function() {
return translatedObj;
});
}
In either case, call as follows :
let setOfWords = {
"nouns": [
"work",
"construction",
"industry"
],
"verbs": [
"work"
],
};
translateByCategory(setOfWords, 'en', 'es').then(function(setOfTranslatedWords) {
console.log(setOfTranslatedWords);
});
The method suggested by #hackerrdave can be modified to make it more compatible with async await feature of JavaScript, do this like the following:
function translateByCategory(){
return new Promise(function(resolve, reject){
var translateRequests = [];
Object.keys(setOfWords).forEach(function(category){
setOfWords[category].forEach(function(word){
translateRequests.push(google.translate(word, 'es', 'en'));
});
});
Promise.all(translateRequests).resolve(resolve(translateRequests));
});
}
So now you can do something like:
let translatedObj = await translateByCategory();
And you will get what you want in "translatedObj".

Why is $httpProvider.interceptors returning an `undefined` value

I've created a basic AngularJS app that consumes Yelp's API and am having trouble using $httpProvider.interceptors to parse my response.
Here is my app:
var app = angular.module("restaurantList", []);
My yelpAPI service (not pictured) authenticates my API request and generates a HTTP request. I then output the data received to the Web Console like so:
app.controller("mainCtrl", ["$scope", "yelpAPI", function ($scope, yelpAPI) {
$scope.restaurants = [];
yelpAPI.get(function (data) {
$scope.restaurant = data;
console.log($scope.restaurant);
});
}]);
Here is data from my request:
Object {region: Object, total: 37, businesses: Array[20]}
I want the array located in the businesses property. So, I figured it would be a good idea to use $httpProvider.interceptors to parse the objects found, in the Object.businesses array.
Here is what $httpProvider.interceptors looked like, when I made my initial request:
app.config(function ($httpProvider) {
$httpProvider.interceptors.push(function () {
return {
response: function (response) {
return response;
}
}
});
});
Here is what $httpProvider.interceptors looks like now:
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
response: function(response) {
var old_response = response.businesses,
new_response = [];
for (var i = 0; i < old_response.length; i++) {
var obj = old_response[i],
new_obj = {
restaurant_name: obj.name,
phone_number: obj.display_phone,
yelp_rating: obj.rating,
reservation_url: obj.reservation_url
};
new_response.push(new_obj);
}
return new_response;
}
}
});
});
Now, I'm receiving an error that says TypeError: Cannot read property 'businesses' of undefined. Is there something I'm overlooking?
EDIT #1
I used console.log(response) inside the interceptor to print my response and found that response.businesses should actually be response.data.businesses. Which resolves my error but now my $http call returns undefined. Any idea what my new problem could be?
EDIT #2
app.factory("yelpAPI", function($http, nounce) {
return {
get: function(callback) {
var method = "GET",
url = "http://api.yelp.com/v2/search";
var params = {
callback: "angular.callbacks._0",
oauth_consumer_key: "my_oauth_consumer_key",
oauth_token: "my_oauth_token",
oauth_signature_method: "HMAC-SHA1",
oauth_timestamp: new Date().getTime(),
oauth_nonce: nounce.generate(),
term: "American",
sort: 2,
limit: 20,
radius_filter: 4000,
deals_filter: true,
actionlinks: true
};
var consumerSecret = "my_consumer_secret",
tokenSecret = "my_token_secret",
signature = oauthSignature.generate(method, url, params, consumerSecret, tokenSecret, {
encodeSignature: false
});
params["oauth_signature"] = signature;
$http.jsonp(url, {
params: params
}).success(callback);
}
}
});
In return angular wait object with {data : }:
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
response: function(res) {
var old_response = res.businesses,
new_response = [];
for (var i = 0; i < old_response.length; i++) {
var obj = old_response[i],
new_obj = {
restaurant_name: obj.name,
phone_number: obj.display_phone,
yelp_rating: obj.rating,
reservation_url: obj.reservation_url
};
new_response.push(new_obj);
}
return {data : new_response};
}
}
});
});
Return as {data : new_response}
Emir helped me resolve this issue but essentially, whenever you use $httpProvider.interceptors you have to update response.data and return the entire response object (i.e. not just a new array, as I did) because $http selects the data property for you.

Parse Promise and Parse Relation toghether - Javascript API

Ok guys! Could someone help me?
I have a parse DB as follow:
Person(id, name)
Product(id,serial)
Invoice(id,<Relation>products, personId)
I need to build a function that returns an array of Objects,
in each Object person.name, invoice.id,invoice.products
I'm not so good with english so I wish you understand what i mean.
so i use Parse.Promise to query the "person" and the "invoice"
function Obj(name, invoiceId, products) {
return {
name: name || '',
invoiceId: invoiceId || '',
products: products || []
};
}
function retreiveData(aName) {
var retval = [],
personQuery = new Parse.Query("Person");
personQuery.equalTo('name', aName).first().then(function(person) {
var invoiceQuery = new Parse.Query("Invoice");
return invoiceQuery.equalTo('personId', person.id).query().find();
}), then(function(invoices) {
//until here everythinks work fine
// now i've the invoices list and i have to look for related products
var promise = new Parse.Promise();
_.each(invoices, function(invoice) {
var obj = new Obj(aName, invoice.id),
promise = new Parse.Promise();
invoice.relation('products').query().find().then(function(products) {
obj.products = products;
// here i expect to have the obj fulfilled with all data
retval.push(obj);
promise.resolve();
});
return promise;
});
return promise;
});
}
var aName = 'Paul';
retreiveData(aName).then(function(retval) {
console.log('success');
/* here i have to have somethin like
retval=[{
name='paul',
invoiceId='12412412',
products=[{prod1},{prod2},{prod3}]
},{
name='paul',
invoiceId='67413412',
products=[{prod5},{prod10},{prod33}]
}]
*/
});
any idea?
i know the inner promise is wrong, but i don't understand how to fix
Thanks guyz!

Categories

Resources