Is To-One Relationships in Realm JavaScript only for one schema? - javascript

I am trying to insert my nested object to Realm with To-One Relationships method, but I got an unexpected result where all value of my nested object is the same thing as the value from the first of my nested object that has been Relationship
This is my schema looks like
const PhotoSchema = {
name: 'CUSTOMER_PHOTOS',
properties: {
base64: 'string'
}
};
const TimeSchema = {
name: 'CUSTOMER_TIMES',
properties: {
warranty: 'float',
finish: 'float'
}
};
const MainSchema = {
name: 'CUSTOMERS',
primaryKey: 'id',
properties: {
id: 'int',
name: 'string',
photo: {type: 'CUSTOMER_PHOTOS'},
time: {type: 'CUSTOMER_TIMES'},
}
};
And try to insert some data like this
import Realm from 'realm';
Realm.open({
path: 'mydb.realm',
schema: [PhotoSchema, TimeSchema, MainSchema]
})
.then((realm) => {
realm.write(() => {
realm.create('CUSTOMERS', {
id: Date.now(),
name: 'John',
photo: {
base64: 'ImageBase64'
},
time: {
warranty: 31,
finish: 7
}
})
})
})
.catch((error) => {
console.error(error)
});
The process of inserting data is successfully BUT I got unexpected result when successfully get that data from Realm
Unexpected Result in console.log()
{
id: 1601335000882,
name: "John",
photo: {
base64: "ImageBase64"
},
// This value is the same as PhotoSchema
time: {
base64: "ImageBase64"
}
}
I want to the actual result like this
{
id: 1601335000882,
name: "John",
photo: {
base64: "ImageBase64"
},
time: {
warranty: 21
finish: 7
}
}
I there anything wrong with my code? The Documentation is not too detail about the method, the explanation and the example is just like one word
UPDATE:
I got an unexpected result only in the console.log() and if I try to access the property directly like MY_DATA.time.warranty the result is what I expected
The Answer is: No
To-One Relationships method is not only for one Schema, and Thanks to Angular San for showing an example of Inverse Relationships method.

Try Inverse Relationships
I got an expected result with Inverse Relationships method. In this method, you have to add one property that connected to Main Schema and I want to call this a combiner property
const PhotoSchema = {
name: 'CUSTOMER_PHOTOS',
properties: {
base64: 'string',
combiner: {type: 'linkingObjects', objectType: 'CUSTOMERS', property: 'photo'}
}
};
const TimeSchema = {
name: 'CUSTOMER_TIMES',
properties: {
warranty: 'float',
finish: 'float',
combiner: {type: 'linkingObjects', objectType: 'CUSTOMERS', property: 'time'}
}
};
const MainSchema = {
name: 'CUSTOMERS',
primaryKey: 'id',
properties: {
id: 'int',
name: 'string',
photo: 'CUSTOMER_PHOTOS',
time: 'CUSTOMER_TIMES',
}
};

Related

Primary key autogenerating in RxDB

async function createDatabase() {
addRxPlugin(RxDBReplicationCouchDBPlugin);
addPouchPlugin(PouchdbAdapterIdb);
addRxPlugin(RxDBLeaderElectionPlugin);
const database = await createRxDatabase({
name: 'dictionaries',
storage: getRxStoragePouch('idb')
});
await database.addCollections({
dictionaries: {
schema: {
title: 'dictionary',
description: '',
version: 0,
primaryKey: 'id',
type: 'object',
keyCompression: false,
properties: {
id: {
type: 'string',
maxLength: 100,
},
name: {
type: 'string',
minLength: 1,
maxLength: 30,
},
},
required: ['id', 'name']
}
}
})
}
When I pass some value as an id, it is used as part of an auto-generated key:
const db = await createDatabase();
await db.dictionaries.newDocument({
id: 'bar',
name: 'English'
}).save(); // _doc_id_rev inside the db would be of format "bar::<auto-generated value>"
My question is is there any way to say RxDB that generating id's values for dictionaries is of its own concern or my single option is always to provide a dummy value, such as bar?

Mongoose: Access parent key from child object in Schema

I am working with mongoose schema and trying to get a parent obj in child from the child itself (I know it is not allowed in Javascript, but there are workarounds). This was my first implementation
const customer = mongoose.Schema({
name: String,
products_sold: [
{
name: String,
price: Number,
qty: Number,
},
{
name: String,
price: Number,
qty: Number,
},
],
messages: [
{
timestamp: {
type : Date,
default: Date.now
},
_my_key_: {
type: String,
default: () => {
// here i need to get products_sold.name in array like [products_sold[0].name, products_sold[1].name]
// this.products_sold does not work
},
},
}
]
})
I looked up some resources like this one. So i also tried
const customer = mongoose.Schema({
name: String,
products_sold: [
{
name: String,
price: Number,
qty: Number,
},
{
name: String,
price: Number,
qty: Number,
},
],
messages: [
{
timestamp: {
type : Date,
default: Date.now
},
_my_key_: {
type: String,
default: () => {
// here this.parent.products_sold does not work also
},
},
}
],
init: function(){
this.messages._my_key_.parent = this;
delete this.init;
return this;
}
}.init()
)
For Reference:
Mongoose Default Functions and This
This question does not answer mine.
EDIT # 1
I tried this with both arrow and regular function.
EDIT # 2
As per comment feedback from #Molda. After the above code, This is how i make the instance and save a record.
const Customer = mongoose.model('Customer', customer);
const customer = {
name: "John Doe",
products_sold: [
{
name: "product_name",
price: 1245,
qty: 2,
}
],
messages: [
{
// message timestamp will generate from default and _my_key_ too will generate from default
}
]
}
const callingFunc = async () => {
const cust = await Customer(customer);
await cust.save();
return cust;
};

RxDB populate an array within an array of objects from another collection

I am trying to populate an array of id's from another collection
My JsonSchema looks like this:
{
version: 0,
type: "object",
properties: {
id: {
type: "string",
primary: true
},
// This is populated as expected
working: {
type: "array",
ref: "othercollection",
items: {
type: "string"
}
},
// This is where I am having problems
notWorking: {
type: "array",
items: {
type: "object",
properties: {
// This property is not being populated
problem: {
type: "array",
ref: "yetanothercollection",
items: {
type: "string"
}
}
}
}
}
}
}
From the docs at https://pubkey.github.io/rxdb/population.html I should be able to:
Example with nested reference
const myCollection = await myDatabase.collection({
name: 'human',
schema: {
version: 0,
type: 'object',
properties: {
name: {
type: 'string'
},
family: {
type: 'object',
properties: {
mother: {
type: 'string',
ref: 'human'
}
}
}
}
}
});
const mother = await myDocument.family.mother_;
console.dir(mother); //> RxDocument
Example with array
const myCollection = await myDatabase.collection({
name: 'human',
schema: {
version: 0,
type: 'object',
properties: {
name: {
type: 'string'
},
friends: {
type: 'array',
ref: 'human',
items: {
type: 'string'
}
}
}
}
});
//[insert other humans here]
await myCollection.insert({
name: 'Alice',
friends: [
'Bob',
'Carol',
'Dave'
]
});
const doc = await humansCollection.findOne('Alice').exec();
const friends = await myDocument.friends_;
console.dir(friends); //> Array.<RxDocument>
So my question is why can I not access myDocument.notWorking[0].problem_?
Here is a screenshot of the console that might give you a better understanding of my situation:
As you can see the ingredients property is not populated with the data from the ingredients collection (not in picture). The taxes property, however, is populated.
This is not possible unless you use OEM methods.
https://rxdb.info/orm.html
const heroes = await myDatabase.collection({
name: 'heroes',
schema: mySchema,
methods: {
whoAmI: function(otherId){
// Return the item with id `otherId` from the other collection here
return 'I am ' + this.name + '!!';
}
}
});
await heroes.insert({
name: 'Skeletor'
});
const doc = await heroes.findOne().exec();
console.log(doc.whoAmI());

Mongoose populate denormalized data

I have a User model and a Book model. I want some data from my books to be denormalized on each User document, but still have the option to populate if needed. If I set ref: 'Book' on the books.$._id it gets populated inside the _id path which is unintended. I would like the population to overwrite the denormalized data.
How do I accomplish this?
in users.model.js:
const { Schema } = require('mongoose');
const UserSchema = new Schema({
name: String,
books: {
type: [
{
_id: Schema.Types.ObjectId,
title: String,
length: Number,
},
],
default: [],
},
});
Desired outcome
in users.controller.js:
app.get('/', async (req, res, next) => {
const users = await User.find({})
/*
users: [{
_id: ObjectId(),
name: 'Andrew',
books: [{
_id: ObjectId(),
title: 'Game of Thrones',
length: 298,
}, { ... }],
}, { ... }]
*/
});
app.get('/:id', async (req, res, next) => {
const book_id = req.params.id;
const user = await User.findById(book_id).populate({
path: 'books',
model: 'Book',
});
/*
user: {
_id: ObjectId(),
name: 'Andrew',
books: [{
_id: ObjectId(),
name: 'Game of Thrones',
length: 298,
author: 'Simone Dunow',
releasedOn: Date(),
price: 30,
...
}, { ... }],
}
*/
});
Schemas I've tried so far:
books: {
type: [
{
_id: Schema.Types.ObjectId,
title: String,
length: Number,
},
],
default: [],
ref: 'Book',
},
returns array of { _id: null }
books: {
type: [
{
_id: {
type: Schema.Types.ObjectId,
ref: 'Book',
},
title: String,
length: Number,
},
],
default: [],
},
books are populated inside of _id: { _id: { Book } }
books: {
type: [
{
type: {
_id: Schema.Types.ObjectId,
title: String,
length: Number,
},
ref: 'Book',
},
],
default: [],
},
throws exception: invalid type
const UserSchema = new Schema({
name: String,
books: [{
id: { type : Schema.Types.ObjectId, ref : 'Book'} //Whatever string you have used while modeling your schema
title: String,
length: Number,
}],
});
While using the schema you can populate as follows :
populate({ path: 'books.id' })
Output :
{
_id : // someid
name : "somename"
books : [
{
id : {//document referring to Books collection},
title : "sometitle",
length : //somelength
}, ...
]
}
To anybody that might be still looking to achieve a full replacement, full disclosure: It might be a bit hacky for some evangelists or even have a performance toll on high traffic apps, but if you really want to do it, you can tap into the toJSON method of the schema like the following:
UserSchema.method('toJSON', function () {
let obj = this.toObject();
obj.books = obj.books.map(
(book) => (Schema.Types.ObjectId.isValid(book.id)) ? book : book.id
);
return obj;
});
What's going on here is basically we're replacing the whole property with the populated result when the book.id has been populated otherwise we just return the original object by checking the validity of the book's id (when populated will be a full bloomed object rather than an id).

Cannot read property 'Types' of undefined -- using mongoose Schema.Types.ObjectId

What is the current behavior?
To run the script, I'm using babel-node since the script uses es6.
Cannot read property 'Types' of undefined
authorId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
^
If the current behavior is a bug, please provide the steps to reproduce.
I've defined the schema like this:
import * as mongoose from 'mongoose';
const Schema = mongoose.Schema;
const RecipeSchema = new Schema ({
authorId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
name: String,
description: String,
photos: [
{
name: String,
path: String,
isMain: Boolean,
alt: String
}
],
ingredients: [
{
name: String,
quantity: Number,
metricType: {
type: String,
enum: [ 'kg', 'g', 'mg', 'l', 'ml', 'unit' ],
default: 'unit'
}
}
],
preparement: String,
isForSell: { type: Boolean, required: true, default: false },
price: Number,
url: String,
portionNumber: Number,
time: Number,
grades: [
{
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
grade: Number,
date: Date
}
],
} );
export default mongoose.model ( 'Recipe', RecipeSchema );
And tried to seed the database with a function like this:
async function insert_recipe () {
const user = await User.findOne ( {} );
await Recipe.create ( {
authorId: user.id,
name: 'some name',
description: 'some description',
ingredients: [
{
name: 'foo',
quantity: 12,
metricType: 'g'
},
{
name: 'bar',
quantity: 50,
metricType: 'g'
}
],
preparement: 'how to do something like this',
isForSell: false,
portionNumber: '5',
time: 20
What is the expected behavior?
It should use the first user's ID that owns the recipe and create the recipe itself on the database.
Please mention your node.js, mongoose and MongoDB version.
I'm using the last versions for all of them in the current moment. (2017-09-15)
After a few trials, I found a solutions with a bit change in the Schema code.
import * as mongoose from 'mongoose';
import { Schema, model } from 'mongoose';
import User from '../models/user';
const RecipeSchema = new Schema ({
authorId: { type: Schema.Types.ObjectId, ref: 'User' },
name: String,
description: String,
photos: [
{
name: String,
path: String,
isMain: Boolean,
alt: String
}
],
ingredients: [
{
name: String,
quantity: Number,
metricType: {
type: String,
enum: [ 'kg', 'g', 'mg', 'l', 'ml', 'unit' ],
default: 'unit'
}
}
],
preparement: String,
isForSell: { type: Boolean, required: true, default: false },
price: Number,
url: String,
portionNumber: Number,
time: Number,
grades: [
{
user: { type: Schema.Types.ObjectId, ref: 'User' },
grade: Number,
date: Date
}
],
} );
export default model.call(require('mongoose'), 'Recipe', RecipeSchema);
So I'm basically importing Schema and model directly instead of using with mongoose.Schema or mongoose.model.
I also had to make a call with model in the end to reference mongoose, like model.call(require('mongoose'), 'Recipe', RecipeSchema);
Now everything worked fine.
Thanks btw!

Categories