View returns an empty index on app, while running perfectly fine on server

HI,

I have written a view and inserted it into the couchbase server through the sync gateway. When I click on “Show Results” in the couchbase console, it returns one document. This view is a production view and not a development view. When I tried accessing this view from the android application, it returned an empty index.
I also tried writing a view in the application. This too returned an empty index. I can’t seem to find the issue. Any help would be appreciated. Thank you!

This is the view after it got inserted through sync gateway by Rest API calls to the admin port(:4985) of the sync gateway:-

function(doc,meta) {
	                    var sync = doc._sync;
	                    if (sync === undefined || meta.id.substring(0,6) == "_sync:")
	                      return;
	                    if ((sync.flags & 1) || sync.deleted)
	                      return;
	                    delete doc.sync;
	                    meta.rev = sync.rev;
						(function (doc, meta) {  if (doc.type != null && doc.type== "ReferenceClass")  { emit(doc.type, doc); }}) (doc, meta); } 

This returns one document with type=“ReferenceClass”. There is only one document in the database with this type.


This is the view I wrote in the android application:-

com.couchbase.lite.View viewItems = database.getView(String.format("%s/%s", "design_document", "view2"));
viewItems.setMap(new Mapper() {
            @Override
            public void map(Map<String, Object> document, Emitter emitter) {
                if (document.get("type") != null && document.get("type") == "ReferenceClass")
                    emitter.emit(document.get("type"), document);
            }
        }, "1.0");

Here, I have also tried updating the version number to “2”, yet the problem persists.


This is where I’m querying and building the index:-

Query query = database.getView("design_document/view2").createQuery();

        QueryEnumerator result = query.run();
        System.out.println("*************** query count="+result.getCount());
        for(Iterator<QueryRow> it=result; it.hasNext();){
            QueryRow row=it.next();
            System.out.println("*************** row="+row.toString());
        }

This is the output on the logcat:-

 Re-indexing view: design_document/view2
 I/CBLite﹕ main Begin transaction (level 1)
 V/View﹕ lastSequence (3) == dbMaxSequence (3), nothing to do
 I/CBLite﹕ main Committing transaction (level 1)
 V/View﹕ Query design_document/view2: SELECT key, value, docid, revs.sequence FROM maps, revs, docs WHERE maps.view_id=? AND revs.sequence = maps.sequence AND docs.doc_id = revs.doc_id ORDER BY key LIMIT ? OFFSET ? | args: [2, 2147483647, 0]
 D/CBLite﹕ Query view design_document/view2 completed in 6 milliseconds
 I/System.out﹕ *************** query count=0

Views on the server are not the same thing as views on the client.

The Android map function you wrote looks correct. Are you sure that the local database contains a document with "type":"ReferenceClass"?

Yes. I inserted a document of type “ReferenceClass” using the phone, so it should be on the CBlite database. I haven’t inserted it into the “_local” database though.

I also tried getting the document by Id. This worked fine for documents inserted from the phone, as well as for documents inserted into the server from other phones.

I inserted the following line of code after the map function:-

System.out.println("************** view index=" + viewItems.dump().toString());

to which i received the following result:-

I/System.out﹕ ************** view index=[]

I also tried changing the

if (document.get("type") != null && document.get("type") == "ReferenceClass")

statement to

 if (document.get("type") != null && document.get("type").equals("ReferenceClass"))

I have a few questions here:-
1> Do client views only query the client database?
2> If yes, then how do we query the server database to get the complete index of matching documents?
3> Also, which part of the client databse do client views query? Just the non-syncable “_local” one or even the syncable part?

It seems to have been an issue with the property I specified as “type”. I changed the key name from “type” to “type_doc” and now the query runs fine. So now my map function looks like :-

com.couchbase.lite.View viewItems = database.getView(String.format("%s/%s", "design_document", "view2"));
viewItems.setMap(new Mapper() {
            @Override
            public void map(Map<String, Object> document, Emitter emitter) {
                if (document.get("type_doc") != null && document.get("type_doc") == "ReferenceClass")
                    emitter.emit(document.get("type_doc"), document);
            }
        }, "1.0"); 

I guess it was looking at “type” as being JSON or otherwise. I’m not sure. Could someone please explain the details? There was no “type” key in the document other than the “type” I inserted, but I guess a wrong “type” value was being read from somewhere else.

Also, I have found out that the query returns documents not on the CBlite database. So it must be running on all documents on the server.

Hi @Abhilash,

One comment for following code

In JavaScript, “==” compare value. But, in Java, “==” compares the address. So “==” should not be used for value comparison. Use equals or equalsIgnoreCase for String comparison.

Also type should be fine. If it is possible, could you please print entire document by retrieving document by ID?

Thanks!

There’s no reason that should make a difference — there’s nothing magical about the property name "type".

Also, I have found out that the query returns documents not on the CBlite database. So it must be running on all documents on the server.

That’s not possible. The code you showed queries only the local database. I don’t believe the Java version of CBL even contains any code to query the server. How do you know those docs are not in the local db?

1> Do client views only query the client database?

Yes.

2> If yes, then how do we query the server database to get the complete index of matching documents?

We don’t have a full mechanism to do that yet. I’ve been working on an API and implementation (on iOS) but it’s not ready for release yet.

3> Also, which part of the client databse do client views query? Just the non-syncable “_local” one or even the syncable part?

Do you mean the docs exposed by APIs like putLocalDocument? Those are never indexed or queried. They’re really not useful for much other than storing little bits of information you might need locally. (The replicator uses them to store checkpoints.) They are not where you would ordinarily store your real data, and most apps don’t use them. (To be honest, they mostly exist just for compatibility with CouchDB.)

I found the following piece of information on the couchbase server documentation which lead me to change the name of the key “type” to “type_doc”.

If your dataset includes documents that may be either JSON or binary, then you do not want to create a view that outputs individual fields for non-JSON documents. You can fix this by using a view that checks the metadata type field before outputting the JSON view information:

function(doc,meta) {
if (meta.type == “json”) {
emit(doc.firstname.toLowerCase(),null);
}
}

Again, here they are checking for “meta.type”. It should not apply to “doc.type”, yet I thought to just try it out, and after that one particular change, the code just magically worked.

After your reply, I changed back from the key “type_doc” to “type”. You were right, the code still works for the key name “type”. Don’t know why the issue existed in the first place now. :confused:

I uninstalled the application from the phone and re-installed it. Shouldn’t that clear the CBlite database as well? I think since the CBlite database is bundled with the app itself, uninstalling the application should clear it. Is what I think correct?

Also, thanks a lot for answering those questions! Eagerly waiting for that API! (y)

I have one more question. Now say we’re installing the app on the phone for the first time. The CBlite database builds for the first time, and this is empty. When it syncs with the server, documents are replicated into the CBlite database. I’m guessing the CBlite database doesn’t replicate ALL the documents on the server. If this is true and views index only the local database, we will miss out on the documents that were not replicated. Is there any method currently available to build an index on the server using server views, and pass that index containing the IDs to the CBlite database?

Hi, @hideki

Yes, I did change the “==” to “.equals”.

As of the current version of my code, I have also changed if(document.get("type")!=null) to if(document.containsKey("type"))

And yes, type does seem to work now. Read my previous reply. Here is the entire document when I retreived by ID :-

{mobileNo=12345, channels=channel_user, _rev=1-e94b7ab23885108753fc8e26d177dd0b, emailID=xyz@abc.com, name=apple, _id=f0746809-a13f-463a-8351-bba338e382cb, user=865800023013854, type=ReferenceClass}

By default it replicates all the documents that the user has access to (i.e. that are in channels that the user can access.) If you want to replicate a subset of docs, then you need to configure the sync function and access privileges so only those docs are accessible. Or you can create channels for those subsets and have the client specify one or more channels when it replicates.

Is there any method currently available to build an index on the server using server views, and pass that index containing the IDs to the CBlite database?

No. The only communication between client and server is via the replicator.