WatermelonDB icon indicating copy to clipboard operation
WatermelonDB copied to clipboard

Do not synchronize record or table

Open osvaldokalvaitir opened this issue 3 years ago • 7 comments

I am facing two synchronization problems.

1 - I have a table that serves to store application data. I can't use Watermelon's LocalStorage. How can I prevent this table from being synchronized.

2 - I have an order page that cannot be sent to the server until the user has entered all the information. The user can close the application and continue with the current order, it happens that until he finishes I cannot send it to the server. The solution I would have at the moment would be to make a 'new_order' table to create and then move to the 'order', but I would have to create a relationship for two tables, which I don't think is right, the best would be if I had a status so that I could ignore this record.

I found this problem in the issue, but I don't know what the solution was: https://github.com/Nozbe/WatermelonDB/issues/206

For the time being in item 2, I'm resolving by placing order._raw._status = 'draft' I'm doing tests now, I don't know if it's going to break something now or in the future.

osvaldokalvaitir avatar May 10 '21 13:05 osvaldokalvaitir

I believe if we had a way to overwrite

const createdQuery = Q.where(columnName('_status'), 'created')
const updatedQuery = Q.where(columnName('_status'), 'updated')

for fetchLocal is a start...

sidferreira avatar May 10 '21 20:05 sidferreira

The test did not work with order._raw._status = 'draft'.

After a while, the status automatically changes to 'created'.

Until I have a good solution, I will duplicate the order table and I will prevent this table from being synchronized.

osvaldokalvaitir avatar May 11 '21 15:05 osvaldokalvaitir

@sidferreira thanks for dedicating your time to my problem =)

osvaldokalvaitir avatar May 12 '21 11:05 osvaldokalvaitir

2 - I have an order page that cannot be sent to the server until the user has entered all the information. The user can close the application and continue with the current order, it happens that until he finishes I cannot send it to the server. The solution I would have at the moment would be to make a 'new_order' table to create and then move to the 'order', but I would have to create a relationship for two tables, which I don't think is right, the best would be if I had a status so that I could ignore this record.

I have something similar to this right now in my application. The user has go through a couple pages before the creation process is complete. I basically just either send the partially completed data through the navigation params or I save the data to AsyncStorage and fetch it later when needed and clear it after the record is created

KrisLau avatar Nov 10 '21 15:11 KrisLau

2 - I have an order page that cannot be sent to the server until the user has entered all the information. The user can close the application and continue with the current order, it happens that until he finishes I cannot send it to the server. The solution I would have at the moment would be to make a 'new_order' table to create and then move to the 'order', but I would have to create a relationship for two tables, which I don't think is right, the best would be if I had a status so that I could ignore this record.

I have something similar to this right now in my application. The user has go through a couple pages before the creation process is complete. I basically just either send the partially completed data through the navigation params or I save the data to AsyncStorage and fetch it later when needed and clear it after the record is created

As there is no way to inform which table I want to synchronize, I created a table that holds temporary data and then sends it to the table I want. And in the synchronization function I delete the temporary table so that it doesn't go in synchronization.

osvaldokalvaitir avatar Nov 11 '21 11:11 osvaldokalvaitir

I have something similar in my application. I build an adapter layer on top of wdb synchronize. An adapter is designed to transform database changes into a new one, something like this:

(imagine an interceptor in backend development)

interface DatabaseAdapter {
    toLocal(changes: SyncDatabaseChangeSet, database: Database): Promise<SyncDatabaseChangeSet>;
    toRemote(changes: SyncDatabaseChangeSet, database: Database): Promise<SyncDatabaseChangeSet>;
}

Then I have a registry to hold all the adapters:

interface AdapterRegistry {
    register(adapter: DatabaseAdapter): void;
}
class AdapterRegistry implements AdapterRegistry, DatabaseAdapter {
    private registry: DatabaseAdapter[] = []
    register(adapter: DatabaseAdapter): void {
        this.registry.push(adapter);
    }
    
    toLocal(changes: SyncDatabaseChangeSet, database: Database): Promise<SyncDatabaseChangeSet> {
        return this.registry.reduce((promise, adapter) => {
            return promise.then(changes => adapter.toLocal(changes, database));
        }, Promise.resolve(changes));
    }
    
    toRemote(changes: SyncDatabaseChangeSet, database: Database): Promise<SyncDatabaseChangeSet> {
        return this.registry.reduce((promise, adapter) => {
            return promise.then(changes => adapter.toRemote(changes, database));
        }, Promise.resolve(changes));
    }
}

After pulling changes from backend, I then pass the changes to AdapterRegistry and return the result to wdb. Before pushing changes to backend, I pass the changes to AdapterRegistry and push the result to backend.

export const adapterRegistry = new AdapterRegistry();

export function sync(options) {
	const database = options.database;
    return synchronize({
            ...options,
	    pullChanges: async (...args) => {
			const pullResult = await options.pullChanges(...args);
			return {
				...pullResult,
				changes: await adapterRegistry.toLocal(pullResult.changes, database);
			}
	    },
	    pushChanges: async (pushArgs) => {
	                pushArgs.changes = await adapterRegistry.toRemote(pushArgs.changes, database);
			await options.pushChanges(pushArgs);
	    }
    })
}

As to this thread, I assume a DatabaseAdapter like this will be OK:

class IgnoreTablesDatabaseAdapter implements DatabaseAdapter {
    private ignoredTableNames: string[]
    
    constructor(ignoredTableNames: string[]) {
        this.ignoredTableNames = ignoreTableNames;
    }
    
    async toLocal(changes: SyncDatabaseChangeSet, database: Database): Promise<SyncDatabaseChangeSet> {
        return changes;
    }
   
    async toRemote(changes: SyncDatabaseChangeSet, database: Database): Promise<SyncDatabaseChangeSet> {
        return Object.keys(changes)
	        .filter(tableName => !this.ignoredTableNames.includes(tableName))
	        .reduce((result, tableName) => {
		        result[tableName] = changes[tableName];
		        return result;
		}, {})
    }
}

KennanChan avatar Jul 25 '23 09:07 KennanChan

The test did not work with order._raw._status = 'draft'

@osvaldokalvaitir i did try your way out, with a little bit of changes.

i used order._raw._status = 'synced' instead of order._raw._status = 'draft'

Alarees avatar Mar 23 '24 20:03 Alarees