0

I got a question regarding the solr-client module of nodejs. I'm using this module for querying against a solr-index.

The module itself works fine as long as I don't have to wait for finishing of the query and as long I need the result only as a async result.

But currently I cannot find out, how I will be able to await the finishing of a search request and use the result in a sequential way.

I have the follwing method in my manager

SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);

var docs = null;
var finished = false;
var deferred = Q.defer();

var request = solrClient.search(query, function(err,obj){
    if (!err) {
        if (obj.response.numFound > 0) {
            deferred.resolve(obj.response.docs);
        } else {
            deferred.resolve(null);
        }
    } else {
        deferred.reject(err);
    }
});

var records = null;
var promise = deferred.promise;
promise.then(function(result) {
    records = result;
}).fail(function(error){
    records = error;
});

return records;

};

The problem here is, that I try to wait for the result of the query and use it as return value of "promisedQuery".

I try since days to use this method in a sequential call, also with different additional modules like "wait.for", "q", etc. but nothing seems to work.

The callback function of the solr-client will always be executed after the manager-method has already returned. Also the promise-methods will be even called after the return from the manager-method.

Can someone help me out on that topic or have some tips, how I can await the response of the solr-client-search operation and then give it back in a sequential way?

Thanks for any help.

Udo Gerhards

user2009117
  • 61
  • 1
  • 7

3 Answers3

1

over one week, it seems now that I have found a solution:

SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);

var docs = null;
var deferred = Q.defer();

var request = solrClient.search(query, function(err,obj){
    if (!err) {
        if (obj.response.numFound > 0) {
            deferred.resolve(obj.response.docs);
        } else {
            deferred.resolve(null);
        }
    } else {
        deferred.reject(err);
    }
  });

return deferred.promise;
};

in all other managers, which are calling the above function:

...
var dbPromise = this.solrManager.promisedQuery(query);

var _self = this;
return Q.async(function*(){
    var result = yield dbPromise;
    return result;
});
...

After first tests, it seems that synchronized methods will wait until the promise is settled.

The only thing is, that it runs only with NodeJs version 0.11.10, which supports generator functions, with activated --harmony-flag and "q"-module.

Best regards

Udo

user2009117
  • 61
  • 1
  • 7
0

You are just using the promises a bit incorrectly. Instead of returning records, you need to return 'deferred.promise'. It should look something like this (note that you don't need the callback you passed into promisedQuery).

SolrManager.prototype.promisedQuery = function(query) {
    var solrClient = solr.createClient(this.configuration.cores.page),
    deferred = Q.defer();

    solrClient.search(query, function(err,obj){
        if (!err) {
            if (obj.response.numFound > 0) {
                deferred.resolve(obj.response.docs);
            } else {
                deferred.resolve(null);
            }
        } else {
            deferred.reject(err);
        }
    });

    return deferred.promise;
};

To use it you would do something like:

SolrManager.promisedQuery(myquery)
    .then(function (data) {
        // data is whatever your 'resolved' in promisedQuery
    }, function (err) {
        // err is whatever you rejected in promisedQuery
    });
Ryan Quinn
  • 1,175
  • 1
  • 15
  • 28
  • Hello rquinn, yes i know. I also tried to return directly the promise, but the result is the same, only in the calling method. For e.g. I'm calling the "promisedQuery" from an adapted "valueOf"-method. I currently do not know how to handle the promise correctly within the "valueOf"-method to return the results from the database. The code above is a snapshot of one of my tries. :) – user2009117 Oct 26 '14 at 11:24
  • You aren't returning the promise correctly in your example. That is why you aren't getting the value, I assume. – Ryan Quinn Oct 26 '14 at 13:28
  • Yes, you are right! I have changed it now to your advise. Please see below how it looks like now! :) – user2009117 Oct 26 '14 at 14:22
  • i'm glad it is working now. i'm not sure where to see your changes? maybe you can edit your question and add what you did to fix it at the end? oh nevermind i see it now. – Ryan Quinn Oct 26 '14 at 14:33
  • See below. I've added the adapted version to clarify my question ... concretly I want to override the "toString"- and the "valueOf"-method of a custom object, to give its contents back as a string. And the value of the object should contain in parts the results from the database. – user2009117 Oct 26 '14 at 14:36
0

based on rquinns answer I've changed the code like follows:

SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);

var docs = null;
var finished = false;
var deferred = Q.defer();

var request = solrClient.search(query, function(err,obj){
    if (!err) {
        if (obj.response.numFound > 0) {
            deferred.resolve(obj.response.docs);
        } else {
            deferred.resolve(null);
        }
    } else {
        deferred.reject(err);
    }
});

return deferred.promise;
};

...

DemoObject.prototype.toString = function() {
 return SolrManager.promisedQuery(this.query).then(function(result){
   return result['title'];
 }).fail(function(error){
   return error;
 });
};

DemoObject.prototype.typeOf = function() {
 return SolrManager.promisedQuery(this.query).then(function(result){
   return result['title'];
 }).fail(function(error){
   return error;
 });
};

I think, this is the right way to use the "promise"-object. But what happens when i do the follwing:

...
var demoObject = new DemoObject();
demoObject.query = "id:1";
console.log(''+demoObject);
...

or if I use "demoObject" by concatenating it to a string

...
var string = "Some string "+demoObject;
...

In case of the string concatenation, I'm currently not sure that the string will contain also the title field from the database. Same for console output.

Will nodejs be so intelligent that it resolves for e.g. the string concatenation "after" the results from the database will be available?

BR

Udo

user2009117
  • 61
  • 1
  • 7
  • i think this is a bad idea, because you are making toString and typeOf asynchronous methods now. to log the result of your toString you would have to do something like demoObject.toString().then(function (data) { console.log(data);}) – Ryan Quinn Oct 26 '14 at 14:41
  • Yes, that's exactly my problem! I want to work on results from an async method in synchronous methods. When I add the "demoObject" to a string, I also want to give back the index results. In my code, there is no other chance than overriding these both methods because of the target of use of these objects. Will there be any chance, that I'm able to wait in these methods until the result from the index is served? I tried different things, like "setTimeout", controlling loops, other modules etc. but nothing worked. Maybe you have some suggestions, how I could await the results from the index. – user2009117 Oct 26 '14 at 14:50
  • If not, maybe this could also be a suggestion to the solr-client-module programmer to provide some sync-methods on the client. – user2009117 Oct 26 '14 at 14:51
  • promises are the way to do this. the point of promises is to let you write your code in a synchronous-like way, for async code. i'm not sure why you would want to force synchronous behavior, but maybe this will help: http://stackoverflow.com/questions/21819858/how-to-wrap-async-function-calls-into-a-sync-function-in-node-js-or-javascript – Ryan Quinn Oct 26 '14 at 14:54
  • The whole programmings are part of a specific vm context where i want to do use these objects as global objects which should be exchanged over the context-object itself. After evaluating the code which is running in the vm these objects should be exchanged over the context object to the "normal" programmings. Concretely I want to create global objects within the vm like in normal javascript programmings and use them afterwards in my nodejs code. These objects will then, for e.g. be used in a template engine for generating output. – user2009117 Oct 26 '14 at 15:06