View query is empty on Android but isn't on iOS

I built a Hybrid mobile app using couchbase lite and Cloudant. The iOS version of the app works great however the android version is not returning any results from the queried views.

So far from troubleshooting these are what I’ve found out:

  • Replication was successfully between with couchbase lite and Cloudant
    in both directions
  • The /{db}/_all_docs REST API works perfectly
  • The /{db}/{doc} REST API get document by Id also works properly
  • The design documents were successfully replicated to the local
    database and I was able to retrieve a design document via the
    /{db}/{design-doc-id} REST API

The strange thing is that the “total_rows” equals to 0 as shown below:

{"total_rows":0,"offset":0,"rows":[]} 

Below is sample of the design document:

map: function(doc) {
	if(doc.class_name == "test_results" && doc.type_of_test == "steps") {
	    emit(doc._id)
	}
}

Even when I created a new design document similar to the above and then insert a document the result of the view still has a “total_rows” set to 0 however in cloudant the result is not empty.

Hi, could you give us a sample of your document and the code generating the view on Android? Thanks

// excerpt from my controller

SharedService.getSteps($sessionStorage.PROFILEID).then(function(result) {

    $scope.stepsData = (result)? result.RESULT : 0;

}, function(error) {

    console.log('error: ', error);
    navigator.notification.alert("Your request was not successful. Please check your connection and try again later.", null, 'Information');
});

// excerpt from my shared service

getSteps: function(patientId) {

    var defer = $q.defer();
    var view = 'ddoc_test_results/by_ptt';
    var options = {
        "startkey": [ patientId, "steps", startOfDay ],
        "endkey": [ patientId, "steps", endOfDay ],
        "inclusive_end": true
    };

    CouchBaseMobile.queryView(view, options).then(function(response) {

        var data = (response.length)? response[response.length-1].doc : null;
        defer.resolve(data);

    }, function(error) {

        defer.reject(error);

    });

    return defer.promise;
}

// excerpt from my couchbase service

Service.prototype.queryView = function(view, options) {

    var self = this;
    var defer = $q.defer();
    var showRaw = false;

    if (view && options) {

        // Defaults
        options.include_docs = true;
        options.attachments = true;

        if (options.hasOwnProperty('showRaw')) {

            showRaw = true;
            delete options.showRaw;
        }

        if (options.hasOwnProperty('key')) {

            options.key = JSON.stringify(options.key);
        }

        if (options.hasOwnProperty('startkey')) {

            options.startkey = JSON.stringify(options.startkey);
        }

        if (options.hasOwnProperty('endkey')) {

            options.endkey = JSON.stringify(options.endkey);
        }

        var segments = view.split('/');
        var method = 'GET';
        var path = self.getDbName() + '/_design/' + segments[0] + '/_view/' + segments[1];

        self.request(path, method, options, null).then(function (response) {

            var data = (showRaw)? response.data : response.data.rows;
            defer.resolve(data);

        }, function (error) {

            defer.reject(error);
        });

    } else {

        defer.reject('view and options arguments are required');
    }

    return defer.promise;
};

@eforth Did you ever figure out this issue? I am facing the same.

@ldoguin I am also having an issue where views work perfectly on IOS, but not Android.
I confirmed that data replication had happened between the server and cblite.

I created views:
const views = { returnAll: { map: function(doc) { emit(doc); }.toString() } }; database.createDesignDocument('_design/voters25', views);

I confirmed that the views had been created with a:
database.getDesignDocument('_design/voters25').then((result) => {console.log(result)});

However, when I search for the actual view data, I receive an empty array:
database.queryView('_design/voters25', 'returnAll').then((result) => {console.log(result)});

==>
Object {total_rows: 0, offset: 0, rows: Array[0]}

Any assistance would be most appreciated!

I haven’t found solution to the problem.

Thanks for the reply @eforth

@hideki Do you have any suggestions?

Thanks,
Chris

Hi @eforth and @chris.duflo,

Do both of you use Cloudant for the server? And do you use CBL v1.3.0? If so, issue could be in the replication. See https://github.com/couchbase/couchbase-lite-android/issues/978 Please double check if data is replicated into the client database?

If this is not replication issue, can you query with REST API instead of using coax? We’d like to know if this is core module issue or coax issue.

Thanks,
Hideki

I’ve switched to IBM Cloudant.

Hi @hideki

Thanks for the quick response!

We are not using Cloudant, we are using Couchbase Server 4.1
We are using CBL v1.3.0

Replication does not seem to be our issue since I can do a call for all of my voter documents, but a call to my voter views is empty.

We are currently using ng-couchbase-lite to make RESTful calls to the local database

Thanks,
Chris

Hi @hideki

Do you have any other suggestions of solutions we might be able to try?

Thanks again,
Chris

Hi @chris.duflo,

To understand your issue, could you please share map function and query codes?

Thanks,
Hideki

Hi @hideki

Please see below and let me know if this is what you were looking for:

couchbaseService:

_createViews(dbName, views) {
const database = new this.$couchbase(this.url, dbName);
const deferred = this.$q.defer();

database.createDesignDocument(‘_design/’ + dbName, views).then((result) => {
this.$log.log(result);
deferred.resolve(result);
}).catch(
(err) => {
this.$log.error(err);
deferred.resolve(err);
});

return deferred.promise;
}

_queryView(dbName, view) {
const database = new this.$couchbase(this.url, dbName);
const deferred = this.$q.defer();

database.queryView(‘_design/’ + dbName, view).then((result) => {
this.$log.log(result);
deferred.resolve(result);
}).catch(
(err) => {
this.$log.error(err);
deferred.reject(err);
});

return deferred.promise;
}

View creation and query:

const testViews = {
auth: {
map: function(doc) {
emit({ first: doc.first, last: doc.last });
}.toString()
}
};

this.couchbaseService._createViews(‘test’, testViews);

this.couchbaseService._queryView(‘test’, ‘auth’).then((result) =>

Thanks again,
Chris

emit({ first: doc.first, last: doc.last })

This is using an object (dictionary, map…) as a key. We don’t recommend doing that, because the keys of the object don’t have a defined ordering, so the sort order will be implementation-dependent. You should use an array instead for a compound key, with the primary key as the first object.

Hi @jens

Thanks for the input, we’ll implement this change on our end. This is actually just an example, we are seeing the same issue when using the following as a test:

emit(doc)

Thanks,
Chris

emit(doc) has the same problem, of course, because doc is also an object. (And it doesn’t make any sense, because there’s no indication of how the docs should be sorted.)

@jens and @hideki - thank you so much! This suggestion solved my issue, Android is now displaying the view properly like iOS.

emit([doc.vitals.first, doc.vitals.last], { first: doc.vitals.first, last: doc.vitals.last, party: doc.partyAssigned });

1 Like

@eforth, FYI ^ if you are still looking for a solution

@chris.duflo,
Additional comment.
emit(key) without value parameter does not work with CBL Android. With emit(key), value is passed as undefined. CBL Android can not understand it. The value parameter of emit(key, value) is always required.

Thanks for the explanation @hideki. Super helpful!