Realm App Architecture with Full Sync - how to sync in multi-realm architecture - realm

Following App Architecture Guidelines, I'd like to keep some of the common data in sync within my on-demand realm.
As a simple example:
Cars sync for all clients and are stored in /commonRealm
Trips only sync for the relevant user and are stored in /~/driverRealm
Below is a minimal schema for the app
const CarSchema = {
name: 'Car',
primaryKey: 'id',
properties: {
id: {type: 'string'},
make: {type: 'string'},
model: {type: 'string'}
}
};
const TripSchema = {
name: 'Trip',
primaryKey: 'id',
properties: {
id: {type: 'string'},
name: {type: 'string'},
car: {type: 'Car'}
}
};
commonRealm includes the CarSchema only.
driverReam includes the CarSchema and TripSchema. Cars are never updated by the driver and purpose is really for lookup.
I cannot find an example of how to keep Cars in sync between the realms so that any updates in commonRealm are propagated to the driver's driverRealm.
Listening to the commonRealm Cars object with a subscribe() doesn't help as you cannot get access to data that has been deleted etc.
From what I can deduce from the docs, the architecture for this should actually be something like:
const CarSchema = {
name: 'Car',
primaryKey: 'id',
properties: {
id: {type: 'string'},
make: {type: 'string'},
model: {type: 'string'}
}
};
const TripSchema = {
name: 'Trip',
primaryKey: 'id',
properties: {
id: {type: 'string'},
name: {type: 'string'},
carId: {type: 'string'} // foreign key
}
};
commonRealm would include the CarSchema and driverReam would only include the TripSchema with carId as a type string for the Car.id - not a type of Car. It would be up to the client to run a map/loop to manually join to Car from the commonRealm like:
let tripsWithCar = [];
trips.forEach(trip => {
trip.car = commonRealm.objects('Car').filtered(`id == "${trip.carId}"`)[0];
tripsWithCar.push(trip);
});
I Would like to get some feedback from developers with more experience if this is the correct approach.

Related

Several different entities in one reference field

I need to make a referene with different entities within one reference. Is it possible to do this with MikroORM ?
Example of what I need:
export const userSchema = new EntitySchema<any>({
class: userEntity,
tableName: 'users',
properties: {
uuid: { type: 'uuid', primary: true },
bills: {
reference: '1:m',
entity: () => BillsFromBank || BillsFromAnotherBank,
mappedBy: (a) => a._user,
},
},
});
Is it possible to create some conditions for such a connection 1 reference field with different entities?

How to fix 'RealmObject cannot be called as a function' realm-js error?

In a react-native project using Realm-js, I've just created a clone of the app, integrated all libs, and copied over all src directories.
The app builds installs and runs on Android.
When i go through the authentication flow (which utilizes realm to store auth data), i ultimately get an error:
[ Error: RealmObject cannot be called as a function ]
login function:
async function login(username, password) {
try {
const result = await Api.login({
username: username,
pass: password,
});
const userAuthResult = await Db.updateAuth(result);
setUserAuth(userAuthResult);
} catch (err) {
console.log('[ ERROR ]:', err)
if (!err.message || err.message.includes('Network Error')) {
throw new Error('Connection error');
}
throw new Error('Wrong username or password');
}
}
and ive narrowed down the issue to Db.updateAuth(...)
updateAuth:
export const updateAuth = (params) => {
console.log(' [ HERE 1 ]')
const auth = {
id: params.id,
token: params.token,
refreshToken: params.refresh_token,
tokenExpiresAt: Math.floor(Date.now() / 1000) + 600, //params.expires_at,
federatedToken: params.federatedToken ?? '',
federatedTokenExpiresAt: params.federatedTokenExpiresAt ?? 0,
username: params.username,
name: params.name,
roleName: params.role_name,
roleId: params.role_id,
lastLogin: Math.floor(Date.now() / 1000),
};
console.log(' [ HERE 2 ]')
realm.write(() => {
console.log(' [ HERE 3 ]')
realm.create('Authorizations', auth, 'modified'); // PROBLEM
});
return auth;
};
inspecting the schema, i found theres no federatedToken propereties, yet in the auth update object, there are two. not sure why it wouldnt be throwing an error in the original non-cloned app.
authorizations schema:
AuthorizationsSchema.schema = {
name: 'Authorizations',
primaryKey: 'id',
properties: {
id: 'int',
token: 'string',
refreshToken: 'string',
tokenExpiresAt: 'int',
username: 'string',
name: 'string',
roleName: 'string',
roleId: 'int',
lastLogin: 'int',
},
};
Realm.js (class declaration) -> https://pastebin.pl/view/c903b2e2
from realm instantiation:
let realm = new Realm({
schema: [
schema.AccountSchema,
schema.AuthorizationsSchema,
schema.AvailableServiceSchema,
schema.FederatedTokensSchema,
schema.NoteSchema,
schema.PhotoSchema,
schema.PhotoUploadSchema,
schema.PrintQueueSchema,
schema.ProductSchema,
schema.ReportSchema,
schema.ServicesSchema,
schema.UploadQueueJobSchema,
schema.InvoicesSchema,
schema.TestSchema
],
schemaVersion: 60,
deleteRealmIfMigrationNeeded: true,
//path: './myrealm/data',
});
this logs the 1, 2, and 3 statements. The issue seems to come from the 'problem' line. Im not sure what exactly this error means, as there doesnt seem to be anything in realm's repo about it, and in the app this was cloned from, there was no issue with this line. I can also see other lines are throwing similar errors later on the user flows
Anyone know what this is about? or where i can learn more?
React-native: v64.2
realm-js: 10.6.0 (app cloned from was v10.2.0)
MacOS: 11.3 (M1 architecture)
in order to create you have the first call, the realm.write a method like this.
const storeInDataBase = (res,selectedfile) => {
try{
realm.write(() => {
var ID =
realm.objects(DocumentConverstionHistory).sorted('HistoryID', true).length > 0
? realm.objects(DocumentConverstionHistory).sorted('HistoryID', true)[0]
.HistoryID + 1
: 1;
realm.create(DocumentConverstionHistory, {
HistoryID: ID,
Name:`${selectedfile.displayname}.pdf`,
Uri:`file://${res.path()}`,
Date: `${new Date()}`
});
})
}catch(err){
alert(err.message)
}
}
Here is the schema file
export const DATABASENAME = 'documentconverter.realm';
export const DocumentConverstionHistory = "DocumentConverstionHistory"
export const DocumentConverstionHistorySchema = {
name: "DocumentConverstionHistory",
primaryKey: 'HistoryID',
properties: {
HistoryID: {type: 'int'},
Name: {type: 'string'},
Uri: {type: 'string?'},
Type: {type: 'string?'},
Size: {type: 'string?'},
Date: {type: 'date?'}
}
};

Query backlink on two fields with same type

I have a relationship between Product and its units describe in schema as below:
public static schema: Realm.ObjectSchema = {
name: Product.schemaName,
primaryKey: 'sku',
properties: {
name: {type: 'string'},
baseUnit: {type: SoldableUnit.schemaName},
derivedUnits: {type: 'list', objectType: SoldableUnit.schemaName},
},
};
public static schema: Realm.ObjectSchema = {
name: SoldableUnit.schemaName,
embedded: true,
properties: {
unitName: {type: 'string'},
sellingPrice: {type: 'double'},
weight: {type: 'float'},
barcode: {type: 'string', optional: true, indexed:true},
},
};
The idea is a product can have one baseUnit and many derivedUnits, and both baseUnit and derivedUnits are SoldableUnit with the same schema. I want to query product by barcode. Here I have to return the product, providing a barcode which maybe equals to barcode of baseUnit or derived units. After some research, I find out that I can use backlink to return inverse relationship. The problem here is SoldableUnit occurs both in baseUnit and derivedUnits and I dont know how to achieve this goals? And whether the schema design of Product is appropriate?

Subschema as array item with AutoForm, SimpleSchema

I am trying to get subschemas to work as an array, which I assume is the correct way to handle my problem (but please correct me if I am wrong!). I provide a simplified working example to show my problem, based on the BooksSchema example provided by the AutoForm package. In my example, I have a collection of Libraries, and one of the fields in the 'Libraries' object is supposed to be the library's collection of books. Rendering the AutoForm does not give me any input labels as defined in my Book collection, but instead just shows one (1) empty text input field.
Schemas:
import SimpleSchema from 'simpl-schema';
SimpleSchema.extendOptions(['autoform']);
BooksSchema = new SimpleSchema({
title: {
type: String,
label: "Title",
max: 200
},
author: {
type: String,
label: "Author"
},
copies: {
type: Number,
label: "Number of copies",
min: 0
},
lastCheckedOut: {
type: Date,
label: "Last date this book was checked out",
optional: true
},
summary: {
type: String,
label: "Brief summary",
optional: true,
max: 1000
}
}, { tracker: Tracker });
LibrariesSchema = new SimpleSchema({
collection: {
type: Array
},
'collection.$': {
type: BooksSchema,
minCount: 1
}
});
LibrariesSchema.extend(BooksSchema);
Libraries = new Mongo.Collection("libraries");
Libraries.attachSchema(LibrariesSchema);
AutoForm:
{{> quickForm collection="Libraries" id="insertBookForm" type="insert"}}
Thank you so much in advance for your time, really been struggling with this for a long time now!
In my case I was indeed able to resolve the issue by using John Smith's example without the brackets.
LibrariesSchema = new SimpleSchema({
'books': {
type: BooksSchema,
minCount: 1
}
});
LibrariesSchema = new SimpleSchema({
'books': {
type: [BooksSchema],
minCount: 1
}
});
Arrays of a specific types, for use in check() or schema definitions, are specified as [SomeType], eg. [String], or [BooksSchema] in your case.

Saving an object in mongodb

I am trying to save variant options as an object, something like that:
{'1' : {optionTitle: 'title', optionPrice: 12}, '2': {....}}
//schema
RestMenuVariants.attachSchema(new SimpleSchema({
restRefId: {type: String},
createdBy:{type: String},
title: {type: String},
options: {type: Object},
sortId: {type: String, optional: true},
createdAt: {type: Date}
}));
//part of the method addMenuVariantItem
return RestMenuVariants.insert({
restRefId: restId,
createdBy: Meteor.userId(),
createdAt: new Date(),
title: title,
options: options,
sort_id: sortId
});
// part of the event for loop which creates the object
variantOptions[i] = {optionTitle: $(element).val(), optionPrice: $(elementPrice).val()};
}
// and calling the method
Meteor.call('addMenuVariantItem', this._id, this.createdBy, variantTitle, variantOptions, function(error, result){.....})
I don't get any check or other errors, the variant is saved but when I look for the item at the console i see that the options is an empty object:
//var cursor = RestMenuVariants.findOne({_id: id});
//console.log(cursor.options)
Object {}
What am I missing?
Thanks.
It looks like variantOptions is being created as an array but your schema only expects an object.
Change:
options: {type: Object}
to
options: {type: [Object], blackbox: true },
in your schema definition.
The blackbox: true option tells simple-schema to ignore the structure of the objects being put into the options array.
Also note that an array is != a nested object with numbered keys as you have in your description. You won't get:
{
'1': {optionTitle: 'title', optionPrice: 12},
'2': {....}
}
Instead you'll see:
[
{ optionTitle: 'title', optionPrice: 12 },
{....}
]

Resources