How to make sync function ignore specific doc types?

When a user logs into our solution from the web we create an “ActivityLog” document and updates the “User” document with latest login. When replicating to Couchbase Lite we really just want to ignore the ActivityLog documents. This is my sync_gateway.json config file:

{
	"log": ["*"],
	"adminInterface": "0.0.0.0:4985",
	"interface": "0.0.0.0:4984",
	"databases": {
		"data": {
			"use_views":false,
			"num_index_replicas":0,
			"bucket": "data",
			"server": "http://ds9:8091",
			"username": "xxxx",
			"password": "yyyyyyy",
			"enable_shared_bucket_access": true,
			"import_docs": true,
			"users": { "GUEST": { "disabled": true, "admin_channels": ["!"] } },
			"sync": `function (doc, oldDoc) {
   function _log(t){
       console.log('SG: ' + t);
  }
  // Document deleted?
console.log("XX: sync...");
  if (oldDoc && oldDoc._deleted){
    // Server doc. deleted -> just propagate this!
    _log('oldDoc deleted, id: ' + (oldDoc._id || 'no id!'));
    channel('!');
    return;
  }
  if (doc && doc._deleted){
    // Server doc. deleted -> just propagate this on the public channel!
    _log('doc deleted, id: ' + (doc._id || 'no id!') + ', ' + (oldDoc ? ('old key=' + oldDoc.key + ', userkey=' + oldDoc.userkey) : 'no oldDoc'));
    channel('!');
    return;
  }
  // Document type is mandatory
  if (!doc.type){
    throw({forbidden: "Document type is required."});
  }
  if ((doc.type == 'EnvLake' || doc.type == 'EnvMeasurement' || doc.type == 'Feedback' || doc.type == 'ActivityLog') && oldDoc){
    throw({forbidden: "Document type not allowed to sync to mobile..."});
  }
  // All public docs are available in the app
  if (doc.ispublic) {
    _log('public, id: ' + (doc._id || 'no id!'));
    channel('!');
    //  return;
  }
  // All non-club fishing trips and catches are available (for stats)
  if ((doc.type == 'FishingTrip' || doc.type == 'Catch') && doc.clubonlykey == undefined) {
   _log('non-club trips, id: ' + (doc._id || 'no id!'));
   channel('!');
  }
  // All non-specific user info is available (for stats)
  if (doc.type == 'User') {
    _log('User doc, id: ' + (doc._id || 'no id!'));
    channel('!');
  }
  // Only docs "owned" by user can be updated
  var key;
  if(doc.type == 'User'){
    key = doc.key;
  } else if(doc.type == 'FishingTrip' || doc.type == 'Catch' || doc.type == 'Photo' || doc.type == 'Private' ||Â doc.type == 'Image'){
    key = doc.userkey;
  }
  if(key){
    _log('User owned, id: ' + (doc._id || 'no id!') + ', type: ' + doc.type + ', user: ' + doc.userkey);
    requireUser(key);
    channel('channel.' + key);
    access(key,'channel.' + key);
  }

  // Creation of new Feedback docs (perhaps without userkey)?
  // Creation of new docs?
  // Updates to existing docs?
  // Readonly for all non-user specific docs...
}`,
      		"allow_conflicts": false,
      		"revs_limit": 20
        }
    }
}

As you can see I have tried to make “ActivityLog” a forbidden type. However, when I look in the sg_info.log file I see:

2019-05-08T12:25:23.078+02:00 [INF] Import: Created new rev ID for doc "ActivityLog:359576920ABD3F6EC12583F40039412C" / "1-0ee5e9a08f9bfdc415ae63052cabff41"
2019-05-08T12:25:23.131+02:00 [INF] CRUD: Stored doc "ActivityLog:359576920ABD3F6EC12583F40039412C" / "1-0ee5e9a08f9bfdc415ae63052cabff41" as #289410
2019-05-08T12:25:23.132+02:00 [INF] Import: Created new rev ID for doc "User:BA171123846CEBF1C1257CB2002DA330" / "796-8193f650cd91eb97cd59f87c566c6d31"
2019-05-08T12:25:23.147+02:00 [INF] CRUD: Stored doc "User:BA171123846CEBF1C1257CB2002DA330" / "796-8193f650cd91eb97cd59f87c566c6d31" as #289411
2019-05-08T12:25:23.152+02:00 [INF] Cache: Received #289410 after  36ms ("ActivityLog:359576920ABD3F6EC12583F40039412C" / "1-0ee5e9a08f9bfdc415ae63052cabff41")
2019-05-08T12:25:23.152+02:00 [INF] Cache: Initialized cache for channel "*" with options: &{ChannelCacheMinLength:50 ChannelCacheMaxLength:500 ChannelCacheAge:1m0s}
2019-05-08T12:25:23.154+02:00 [INF] Cache: #289410 ==> channels {*}
2019-05-08T12:25:23.154+02:00 [INF] Cache: Received #289411 after  11ms ("User:BA171123846CEBF1C1257CB2002DA330" / "796-8193f650cd91eb97cd59f87c566c6d31")

So I can see that the sync gateway does do some work on the doc. Can/should I do anything to prevent this?

Oh, and by the way, my attempts of logging from my sync function does not create any log messages ::frowning:
…so if you have an idea of how to make that work I would also appreciate that :wink:

@jda Hi

I would first recommend that you read our documentation on the use of the _sync function this is full of useful tips and examples which should help with what you are trying to achieve. It also contains information on what olddoc is as you expressed confusion over this in another forum post: https://docs.couchbase.com/sync-gateway/2.5/sync-function-api.html

throw is indeed a method of rejecting a document from being imported to Sync Gateway. If you add a new document every time a user logs in then for that newly created ActivityLog the olddoc field will be null and therefore it won’t fall into that forbidden statement.

Thanks!

Hi @JRascagneres

Thanks for your response!

I have read the API description several times - but a couple of things are not quite clear to me about doc and oldDoc. In the beginning of the API description under doc it says:

This matches the JSON that was saved by the Couchbase Lite (CBL) and replicated to Sync Gateway.

… which indicates that the doc represents the Couchbase Lite document. And this is backed by the small example for a readonly database:

function(doc) {
   throw({forbidden: "read only!"})
}

which also indicates that the doc cannot be written back from CBL to the Couchbase server.

However, my tests kind of indicate that it is not entirely the case… So at the moment I’m “guessing” that the doc is always the current document being replicated either from the CBL OR from the server. And then `oldDoc is the previous version of at the “opposite” end.

But then how can I know “which way” the replication is going - and this e.g. reject the deletion of a public document on the server - but allow a deletion of that same document to be replicated to the local CBL in case the document was deleted at the server?

In the other post of mine that you refer to @bbrks explains that when writing from the server to CBL this always happens as the administrator - but the opposite direction the operation (write/delete) is carried out as the user logged into the sync gateway - i.e. if I understand it correctly. So using requireAdmin() will let the document be written in CBL (if it’s in a channel that matches) - but the same document being written/deleted in CBL and sent back will be rejected (if the user is not an admin).

It’s just a little difficult to test this when I cannot get any of my own console messages printed anywhere :innocent:

Hi again

One of the scenarios I was trying to cover by asking about oldDoc was to have certain documents only reside on the server - and not in the mobile app. I see that leaving out “&& oldDoc” of the formula will do that - if I understand it correctly (now) that the doc document is just the current document no matter what direction the sync. is.

However, I have a use case that I don’t know how to solve:

A user can send feedback to us from the app. - and this can also be as an anonymous user (i.e. not logged in) - so I may not have a userkey to test for. Feedbacks can also be created directly from a browser on the server - so a Feedback document may not have been synced before. However, we don’t want these feedback documents to be synced out to the mobile.

How could I obtain that?

I understand that we could even let the local documents expire once replicated - but it is not very important. We just don’t want any feedback docs. to be sent from the server to the CBL.

I guess that not adding the feedback doc to the public channel should avoid it getting written to the CBL? But will it still be created on the server?

Thanks for any input!