Ivan Asmer 6 lat temu
rodzic
commit
bba299a358
2 zmienionych plików z 136 dodań i 38 usunięć
  1. 56 33
      index.js
  2. 80 5
      mm.js

+ 56 - 33
index.js

@@ -10,18 +10,39 @@ const delay       = ms => new Promise(r => setTimeout(r.bind(ms), ms))
     const Savable     = mm(db)
 
     class Notebook extends Savable{
+        static get relations(){
+            return {
+                owner: "notebook"
+            }
+        }
+    }
 
+    class User extends Savable{
+        static get relations(){
+            return {
+                children: "parent",
+                parent: "children",
+                notebook: "owner",
+            }
+        }
     }
     Savable.addClass(Notebook)
+    Savable.addClass(User)
 
-    let notik = await Savable.m.Notebook.findOne(ObjectID('5c7c064d2ed0f4c9ab4cba4e'))
+    //let notik = await Savable.m.Notebook.findOne(ObjectID('5c7c064d2ed0f4c9ab4cba4e'))
 
-    let SilniyeMans = await Savable.m.Savable.find({ $or: [{surname: 'Silniy'}, {surname: 'Silnaya'}]})
-    for (let manPromise of SilniyeMans){
-        let man = await manPromise;
+    //let SilniyeMans = await Savable.m.Savable.find({ $or: [{surname: 'Silniy'}, {surname: 'Silnaya'}]})
+    //for (let manPromise of SilniyeMans){
+        //let man = await manPromise;
 
-        console.log('man', man.name, man.surname, man.createdAt)
-    }
+        //console.log('man', man.name, man.surname, man.createdAt)
+        //notik.owner = man
+        ////notik.owner = [man]
+        ////notik.owner = new Set([man])
+        //break;
+    //}
+
+    //await notik.save()
 
 
 
@@ -33,33 +54,35 @@ const delay       = ms => new Promise(r => setTimeout(r.bind(ms), ms))
 
     //while(true){
         //await (new Savable({timestamp: (new Date).getTime(), r: Math.random()})).save()
-        //let person = new Savable({
-            //name: 'Mykola',
-            //surname: 'Silniy',
-            //phones: ['105', '1'],
-            //children: [
-                //new Savable({
-                    //name: 'Marina',
-                    //surname: 'Silnaya',
-                    //phones: ['105', '1000503']
-                //}),
-                //new Savable({
-                    //name: 'Andrey',
-                    //surname: 'Silniy',
-                    //phones: ['103', '1000502']
-                //}),
-                //new Savable({
-                    //name: 'Fedor',
-                    //surname: 'Ivanova',
-                    //phones: ['102', '1000504'],
-                    //notebook: new Notebook({
-                        //brand: 'dubovo'
-                    //})
-                //})
-            //]
-        //})
-
-        //await person.save()
+        let person = new User({
+            name: 'Mykola',
+            surname: 'Silniy',
+            phones: ['105', '1'],
+            children: [
+                new User({
+                    name: 'Marina',
+                    surname: 'Silnaya',
+                    phones: ['105', '1000503'],
+                    parent: []
+                }),
+                new User({
+                    name: 'Andrey',
+                    surname: 'Silniy',
+                    phones: ['103', '1000502'],
+                    parent: new Set
+                }),
+                new User({
+                    name: 'Fedor',
+                    surname: 'Ivanova',
+                    phones: ['102', '1000504'],
+                    notebook: new Notebook({
+                        brand: 'dubovo'
+                    })
+                })
+            ]
+        })
+
+        await person.save()
         //console.log(person)
 
         //await delay(1000)

+ 80 - 5
mm.js

@@ -1,8 +1,11 @@
 const ObjectID    = require("mongodb").ObjectID;
 const asynchronize = require('./asynchronize').asynchronize
 
+let i=0;
+
 module.exports = db => {
     const identityMap = {}
+
     class Savable {
         constructor(obj, empty=false){
             //TODO check type for return right class 
@@ -79,12 +82,73 @@ module.exports = db => {
             return db.collection(this._class)
         }
 
-        async save(){
+        async save(noRefs=false){
             if (this.empty) return;
 
+            const syncRelations = async () => {
+                if (noRefs) return
+
+                if (!(this && this.__proto__ && this.__proto__.constructor && this.__proto__.constructor.relations)) return 
+
+
+                async function getValueByField(field, savable) {
+                    let path = field.split('.');
+                    await savable;
+                    let result = savable;
+                    let prev;
+                    let lastKey = path.pop()
+                    while (prev = result, result = result[path.shift()] && path.length);
+                    return {value: prev[lastKey], obj: prev, lastKey};
+                }
+
+                let setBackRef = async (backRef, foreignSavable) => {
+                    console.log('BACKREF for', backRef, foreignSavable.name)
+                    const {value: backRefValue, 
+                            obj: backRefObj, 
+                        lastKey: backRefKey} = await getValueByField(backRef, foreignSavable)
+
+                    if (backRefValue instanceof Array){
+                        console.log('backref -to-many array')
+                        if (!backRefValue.includes(this)){
+                            backRefValue.push(this)
+                        }
+                    }
+                    //else if (backRefValue instanceof Set){
+                        //console.log('backref -to-many set')
+                        //backRefValue.add(this)
+                    //}
+                    else {
+                        console.log('backref -to-one')
+                        backRefObj[backRefKey] = this
+                    }
+                    await foreignSavable.save(true)
+                }
+
+
+
+                for (const relation in this.__proto__.constructor.relations){
+                    const backRef = this.__proto__.constructor.relations[relation]
+
+                    let {value, obj, lastKey: key} = await getValueByField(relation, this)
+                    if (value){
+                        if (value instanceof Savable){
+                            console.log('one-to-*')
+                            await setBackRef(backRef, value)
+                        }
+                        if (value instanceof Array /*|| value instanceof Set*/){
+                            console.log('many-to-*')
+                            for (const foreignSavable of value){
+                                await setBackRef(backRef, foreignSavable)
+                            }
+                        }
+                    }
+                }
+            }
+
             async function recursiveSlicer(obj){
                 let result = obj instanceof Array ? [] : {}
                 for (const key in obj){
+
                     if (obj[key] && typeof obj[key] === 'object'){
                         if (obj[key] instanceof Savable){
                             if (!(obj[key]._id)){
@@ -105,17 +169,23 @@ module.exports = db => {
 
             const {_id, _empty, then, ...toSave} = await recursiveSlicer(this)
 
+            //TODO: UPSERT
             if (!this._id){ //first time
                 const { insertedId } = await this.collection.insertOne(toSave)
                 this._id = insertedId
-
             }
             else { //update
                 await this.collection.updateOne({_id: this._id},  {$set: toSave}).catch(err => console.log('UPDATE ERR', err))
             }
             identityMap[this._id.toString()] = this
+
+            await syncRelations()
         }
 
+
+
+
+
         static isSavable(obj){
             //console.log(obj._id, obj._class)
             return obj && obj._id && obj._class
@@ -130,7 +200,7 @@ module.exports = db => {
             return new Savable.classes[className](obj, empty)
         }
 
-        static addClass(_class){
+        static addClass(_class){ //explicit method to add class to Savable registry for instantiate right class later
             Savable.classes[_class.name] = _class
         }
 
@@ -162,10 +232,15 @@ module.exports = db => {
                 set(obj, propName, value){
                 }
             })
-
-
         }
 
+        static get relations(){ 
+            //empty default relations, acceptable: {field: foreignField}, where:
+            //field and foreign field can be Savable, Array or Set
+            //both fields can be specified as "field", "field.subfield" 
+            //or field: {subfield: foreignField} //TODO later if needed
+            return {}
+        }
     }
 
     Savable.classes                                  = {Savable}