When you delete a docment manually on the Enterprise Edition 5.0.1 build 5003 bucket,but this docment won't be synced to android client!

when this bug occure, i find oldDocment has only meta data, no channel and other properties,so this docment can’t sync to client on the sync_gateway channels.

What version of SGW are you using ? Assuming, its 1.5, make sure you have shared bucket access enabled …

thanks your reply, i update the last sync_gateway (2.0.0(773;5a497fd)),the last couchbase service 5.1. this question is happed also. i find oldDoc is null! this my sync_gateway.json

{
  "log": ["*"],
  "adminInterface":"0.0.0.0:4985",
  "interface": "0.0.0.0:4984",
  "databases": {
    "kitchen": {
       "import_docs": "continuous",
       "enable_shared_bucket_access":true,
       "server": "http://127.0.0.1:8091",
       "bucket":"kitchen",
       "username":"******",
       "password":"******",
       "users": 
        {
        "gysz": {"password": "******"},
        "admin": {"password": "******", "admin_roles": ["admin"]} 
        },
        "roles": {
            "moderator": {},
            "admin": {}
          },
           "unsupported": {
        "replicator_2":true
      },
	"sync":
	`
  function sync(doc, oldDoc) 
  {

var baseDataTag = "BaseData";                                      //写入doc._id的基础数据标识
var userDataTag = "UserData";  
                                    //写入doc._id的用户数据标识
console.log('===================1');
checkData();  //检查数据格式是否符合入库要求
console.log('===================2');
/**
* @singleUser          将channelId做为单网点用户名称
* @multiUserParent     将parentChannelId做为多网点总公司名称
*/
var singleUser = getChannelId();
console.log('===================3');
var channelId = getChannelId();  //取得doc.channelId
console.log('===================4');
/**
 * 对单店用户和网点经营模式的数据同步通道进行授权
 */
if (isClass("CompanyC")) 
{
   var multiUserParent = getParentChannelId();

    if (isCreate()) 
    {                                                  //如果是创建数据
        requireRole("moderator");    

        channel(channelId);  //               //如果为单门店,将基础数据和用户数据通道直接赋给用户
        channel(channelId + baseDataTag);

        if (multiUserParent == null) 
        {                 
             
            access(singleUser, channelId);                             //将用户数据通道赋值给单用户
            access(singleUser, channelId + baseDataTag);               //将基础数据通道赋值给单用户
        } else {                                                       //否则是多网点模式,则父级节点也同步子节点的数据                             
            access(singleUser, channelId);                             //将用户数据通道赋值给单用户
            access(singleUser, channelId + baseDataTag); 
              //将基础数据通道赋值给单用户
            access(multiUserParent, channelId);                        //将用户数据通道赋值给父节点用户
            access(multiUserParent, channelId + baseDataTag);          //将基本数据通道赋值给父节点用户
        }
    } 
    else if(isUpdate())
    {    
                                                           //否则是删除更更改,只有自己更新或删除自己创建的数据
        requireUser(singleUser);
        validateReadOnly("channelId", doc.channelId, oldDoc.channelId);
        channel(channelId);  //               //如果为单门店,将基础数据和用户数据通道直接赋给用户
        channel(channelId + baseDataTag);
    }
    else
    {
        channel(channelId);  //               //如果为单门店,将基础数据和用户数据通道直接赋给用户
        channel(channelId + baseDataTag);
    }
           //入到基础数据通道
} else {  

    if(!isDelete())        
    {
        if(isUpdate())
        {
        console.log('===================a');
        requireUser(singleUser);                                       //只能更新或删除自己的数据
        console.log('===================b');
        validateReadOnly("channelId", doc.channelId, oldDoc.channelId);
        console.log('===================c');
        }

        if (doc.dataType == baseDataTag) 
        {
             channel(channelId + baseDataTag);   
            console.log('===================d');
        }                                  //基础数据
                                //入到基础数据通道
        else if (doc.dataType == userDataTag) 
        {                            //业务数据
        console.log('===================e');
        validateNotEmpty("createdYear", doc.createdYear);  
         console.log('===================f');            //必须有年份
        channel(channelId + userDataTag + doc.createdYear); 
             console.log('===================g');
        access(singleUser, channelId + userDataTag + doc.createdYear); //自己具有访问权限
        console.log('===================r');
        }

    }
    else //普通数据删除
    {
        console.log('===================5');
        if (oldDoc.dataType == baseDataTag)      
        {
            console.log('===================6');
            channel(channelId + baseDataTag); 
        }                             //基础数据
                                     //入到基础数据通道
        else if (oldDoc.dataType == userDataTag) 
        {                               
        channel(channelId + userDataTag + oldDoc.createdYear);            //入到业务数据通道
        } 
    }
    console.log('===================7');
     channel(channelId);  // 
 }                                                     
     
  
/**
 * 检查doc的数据格式是否正确
 */
function checkData() 
{
    if(!isDelete())
    {
        validateNotEmpty("channelId", doc.channelId);                       // 验证字段中是否有通道名称
        validateNotEmpty("className", doc.className);                       // 验证字段中是否有类名称
        if (!hasPrefix(doc._id, doc.className + ".")) 
        {                     // 验证文档ID开头是否含有类名称,如果没有抛出异常
          throw ({ forbidden: "Doc id must be prefixed by className" });
        }
    } 
}

function getType() {
    return (isDelete() ? oldDoc.className : doc.className);
}

function getChannelId() {

    if(isDelete())
    {
         console.log('11===================');
         console.log(oldDoc.channelId+'===================');
    }
   
    return (isDelete() ? oldDoc.channelId : doc.channelId);
}

function getParentChannelId() {
    return (isDelete() ? oldDoc.parentChannelId : doc.parentChannelId);
}

function isClass(className) 
{
    if(isDelete())
    {

     if (oldDoc.className == className)
        {
        return true;
        } 
        else {
        return false;
        }

    }
    else
    {
        if (doc.className == className)
        {
            return true;
        } 
        else {
            return false;
        }
    }
   
}


function isCreate() {
    // Checking false for the Admin UI to work
     console.log('oldDoc='+oldDoc);
    return ((oldDoc == false) || (oldDoc == null || oldDoc._deleted) && !isDelete());
}

function isUpdate() 
{
    console.log('isCreate()='+isCreate());
    console.log('isDelete()='+isDelete());

    return (!isCreate() && !isDelete());
}

function isDelete() {

    console.log('doc._deleted='+doc._deleted);
    
    return (doc._deleted == true);
}

function validateNotEmpty(key, value) {
    if (!value) {
        throw ({ forbidden: key + " is not provided." });
    }
}

function validateReadOnly(name, value, oldValue) {
    if (value != oldValue) {
        throw ({ forbidden: name + " is read-only." });
    }
}

// Checks whether the provided value starts with the specified prefix
function hasPrefix(value, prefix) {
    if (value && prefix) {
        return value.substring(0, prefix.length) == prefix
    } else {
        return false
    }
 }
}
	`
    }
  }
}

If you remove or delete a document via SDK or directly couchbase server, the oldDoc will be null. You don’t have to do anything specific in terms of putting deleted documents into channel - the SGW will take care of it for you. The document is already in a channel and would now be updated with the _deleted flag as true.
This test would be the check to see if document is removed.

  function isRemoved() {
    return( isDelete() && oldDoc == null);
  }

So this is all you would have to do in your sync function …for example

function sync(doc, oldDoc) {
  /* sanity check */
  // check if document was removed from server or via SDK
  // In this case, just return
  if (isRemoved()) {
    return;
  }

// rest of your sync function ...

hi,priya
when i update a docment on the couchbase server, the sync function isUpdate() not happen, the reasion is oldDoc is null. i don’t know why!

Looks like I am missing something. oldDoc will be null as expected.
As I said before

thanks to priya,i will test it tomorrow! i remove some funtion in the sync_gateway.json.

hi,priya,thanks to you! i change my sync_gateway.json as your way, that solve my issue. but I can’t tell what the situation is isDelete() or isRemove, What’s the difference between them, What kind of operation is isDelete() or isRemove() on the server or via SDK. Can you explain to me?thanks!

If you delete a doc via the SGW API or couchbase Lite API, it would be deleted, you are adding a new revision / tombstone revision to doc. So doc will have isDeleted flag set to true and oldDoc will exist (the oldDoc is typically body of parent revision).

When you remove a document via the SDK or via the Admin UI, it would be in “removed” state. The body of the doc is gone - so oldDoc will be nil.

1 Like