balderdashy / sails

Realtime MVC Framework for Node.js
https://sailsjs.com
MIT License
22.84k stars 1.95k forks source link

Linking with waterline collections returns garbage #2845

Closed Mrono closed 8 years ago

Mrono commented 9 years ago

Sometimes the collection doesn't of a linked record is mangled

Thing.js

module.exports = {
    attributes: {
        id: {
            type: 'string',
            primaryKey: true
        },
        name: {
            type: 'string',
            unique: true
        },
        points: {
            collection: 'point',
            via: 'thing'
        }
    },

    beforeCreate: function(values, callback) {
        values.id = sails.uuid.v4();
        callback();
    }
};

Point.js

module.exports = {
    attributes: {
        id: {
            type: 'string'
        },
        value: {
            type: 'string'
        },
        thing: {
            model: 'thing',
            required: true
        }
    },

    beforeCreate: function(values, callback) {
        values.id = sails.uuid.v4();
        callback();
    }
};

Returned data from rest call

    {
        "thing": {
            "0": "d",
            "1": "8",
            "2": "d",
            "3": "7",
            "4": "9",
            "5": "0",
            "6": "3",
            "7": "f",
            "8": "-",
            "9": "c",
            "10": "a",
            "11": "0",
            "12": "b",
            "13": "-",
            "14": "4",
            "15": "9",
            "16": "c",
            "17": "7",
            "18": "-",
            "19": "b",
            "20": "1",
            "21": "c",
            "22": "9",
            "23": "-",
            "24": "1",
            "25": "e",
            "26": "d",
            "27": "7",
            "28": "9",
            "29": "2",
            "30": "f",
            "31": "9",
            "32": "1",
            "33": "8",
            "34": "2",
            "35": "f",
            "bold": "\u001b[1md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[22m",
            "underline": "\u001b[4md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[24m",
            "strikethrough": "\u001b[9md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[29m",
            "italic": "\u001b[3md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[23m",
            "inverse": "\u001b[7md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[27m",
            "grey": "\u001b[90md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "black": "\u001b[30md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "yellow": "\u001b[33md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "red": "\u001b[31md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "green": "\u001b[32md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "blue": "\u001b[34md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "white": "\u001b[37md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "cyan": "\u001b[36md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "magenta": "\u001b[35md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "greyBG": "\u001b[49;5;8md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "blackBG": "\u001b[40md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "yellowBG": "\u001b[43md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "redBG": "\u001b[41md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "greenBG": "\u001b[42md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "blueBG": "\u001b[44md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "whiteBG": "\u001b[47md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "cyanBG": "\u001b[46md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "magentaBG": "\u001b[45md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[49m",
            "rainbow": "\u001b[31md\u001b[39m\u001b[33m8\u001b[39m\u001b[32md\u001b[39m\u001b[34m7\u001b[39m\u001b[35m9\u001b[39m\u001b[31m0\u001b[39m\u001b[33m3\u001b[39m\u001b[32mf\u001b[39m\u001b[34m-\u001b[39m\u001b[35mc\u001b[39m\u001b[31ma\u001b[39m\u001b[33m0\u001b[39m\u001b[32mb\u001b[39m\u001b[34m-\u001b[39m\u001b[35m4\u001b[39m\u001b[31m9\u001b[39m\u001b[33mc\u001b[39m\u001b[32m7\u001b[39m\u001b[34m-\u001b[39m\u001b[35mb\u001b[39m\u001b[31m1\u001b[39m\u001b[33mc\u001b[39m\u001b[32m9\u001b[39m\u001b[34m-\u001b[39m\u001b[35m1\u001b[39m\u001b[31me\u001b[39m\u001b[33md\u001b[39m\u001b[32m7\u001b[39m\u001b[34m9\u001b[39m\u001b[35m2\u001b[39m\u001b[31mf\u001b[39m\u001b[33m9\u001b[39m\u001b[32m1\u001b[39m\u001b[34m8\u001b[39m\u001b[35m2\u001b[39m\u001b[31mf\u001b[39m",
            "zebra": "d\u001b[7m8\u001b[27md\u001b[7m7\u001b[27m9\u001b[7m0\u001b[27m3\u001b[7mf\u001b[27m-\u001b[7mc\u001b[27ma\u001b[7m0\u001b[27mb\u001b[7m-\u001b[27m4\u001b[7m9\u001b[27mc\u001b[7m7\u001b[27m-\u001b[7mb\u001b[27m1\u001b[7mc\u001b[27m9\u001b[7m-\u001b[27m1\u001b[7me\u001b[27md\u001b[7m7\u001b[27m9\u001b[7m2\u001b[27mf\u001b[7m9\u001b[27m1\u001b[7m8\u001b[27m2\u001b[7mf\u001b[27m",
            "stripColors": "d8d7903f-ca0b-49c7-b1c9-1ed792f9182f",
            "zalgo": "d̴͎̼͇͚̞̮̰̥̜̬͔̖̦͕͔̬̳̦͍̪ͧ̓̀̓ͮ͑ͅ8̛͖͚͎̦͚͉̲̘̖͚̼͍̼̬̩̱̯̲̒̃̃̓d̟̟̫̱̟͕̱̟̣̱͙̮͙͎̝̟̖̗̯͇̲̠͉̱̬̫̖͙̘͖̣͍̽͒͂͐̃̎ͣ̉ͬ̊͝7͉̼̠̠͕̦̲̭̜̞͇̺͎͖̫̳̪͓̜͔̮̝̲̠̻̰̬͙̲̳͖̂́̊ͤ̉͆̋̂̈ͮ̅͐̆ͫ͝ͅ9̞̠̘͔͎̣̮̣̤̯͚̋ͬ̀ͪ͗͆̊͘ͅ0̪͕̮̟͖̙̟͈͍̲̗̱͍̱̖͍̤͈̟̥͉̰̹͖̝̲͔̟̏̿̀ͫ̂͐͒̔̃̕ͅ3̸̩̙̰̬̲̜͓̘̫̩̤̯̫͎͇͙̙̙̜̺̬̙̤̘̹̼̺͚̤̪̗̜̱̘̳̙̰͙̰̪̗̞͓͕͕̰̖͈̥̦͕͖̗̫̜̘̭̬ͫ͗̈̄͆̉ͤ̅ͯͬͤͣf̩͕͙͈̯̜̓̔̇̂́-͇̪̰̭̦̤͈͎̣͕̯̝̘̖̺̮͓͇̺̯̝̪̖̭̹̞̻̟̱͉̝͉͕͍͚̯̬̱͕̩͒̈̎̔ͦͤͬͤ̂̕ͅċ͕͖̲̼̟̯̲͈̫͔̳̠̪̗̝̱͓̮̪͚͉͓̹̤̹̮̰̼̰͕̯̰̺̞̬͎̩̜̜̫̰͔̟͙̦͚̲̭̳̼̟̟͖̟͖͂̽ͤ͋̀͠ͅa̧̫͕̦̠͓̩̮̮̯̰͍̺̮̲͙̠͔̪̣̤̤̖͓͛͊ͥ͐̀0̧͈̻̙̞͈̟̘̘̘͍̹ͭ̎̏̈̊̽̆ͣͪ̆̋͌̋ͧ̇ͅͅb̟͚̩̹͖̟̟̹̻͚͉͈̲͙͖̫̱ͤͬͧ́̾̂͌̓̄͌̈̓̚͟ͅ-̶̬̟̫͖̜̗̺̜̜͍̻̳͚͇̲̰͉͚̪̙͇͙̘͉̗͇̉ͪͬ̎̀ͭ͆̇̾͌͂͗̑ͨ̎4̻͓̤̳̪̥͐ͭ͆̏ͮ̑ͣ̀9̛̮͙͙̱ͬ̈ͦ͂ͦ͆c̦̮̪͈̭̥̪̭̭̄͌̐̔ͮ̚͟7̶̩̲̟̼͖̟̖͓̗̼̙͓̠̦͇̭̼̪̮͉̲͉̖̼͙̣͙̖̤̺̳̗͓̲͙̯̤̦͇̹͍͚̼̯̺͉̮̻̙̻̦̟̠̟̩̲̘͊̇͆̐̅ͤͭ͛ͦ̂̄͂̅̎ͨ͋͌ͅ-̩͎͎͍̞̫̟͙̱͍̘̬͙̻̹̺̙̭̪̯̥̗̫͔̬̹̻͈ͧ̆̔̇̈̃̿ͤ̀̚͠ͅḇ̩̲̜͇͇̯̠̪̦̳͍̮̻̲̲̞̫͈͎͍͙͓̹̮̞̜̻͙̲̰̝̜̺̪̣̗͔͖̰͍̙̦͇͙̲̪͖̺̖͔͎͖̪̰͖̦̲̺̮̑̍ͦͥ̽̂͠1̮̤̲̰̰̗̺̞̼̗̬̣̻͍̮̰̩̭͚̪̳͖̯̦̣͕̮͈̹̮̗̳̰̬̳̺̼̪̱̩͕̲̱̰̻̙͎͈͋ͫ̿ͥͤ̋͊͊̉́ͮ̅̀͠ͅc͙̬͇̮̭̬͉̻̥̼̯̻̜̣̦̩̖̥̯̲̜̭̞̖̭͍̘̩̠͕͍̹͕͍̖͓̀̆̔̌̐̀ͅ9̦̲̼̫ͫ͂̽̒̈͊ͪ̿ͣ͗̏͊͛͐ͩ̒̚͘-͔͔̭͖̙̻̠͈̪͇̙̭̥̯̜̤̖̼̩͕̼̗͔̰͉̗͙̽̆̈̄͋̈̈́ͧͭ͌͆ͭ̚͝ͅ1̢͖̝͍͎̳̳̙̪̘̖̣̹̫͍͎͚̭̩͕͕͎̯̞̻̹̺̜͚̱͓̥̥̳̞͇̙̭̘̩̩͈̰͎̰͎̰̯̳̗̋ͩ͆ͭͮͭ͌̌̇ͭͣ̋̚ͅȅ̳̩͖͍̗̻̼̲̫͕͎̲̪̰̯̘̻͓̮̦̣̼̘̞̤͚̫͉̈͆̃ͣͩͮ̂ͨ̓ͦ̌͜d̡̫̜͖̹̤͓̰͕̻̦̮͍̥̹̹̼̮̩̱̫͇̼͎̪̺̗̝̜̖͉̭͕͚̯̺̟̦̺̘̯̩̳̰͕͇̯͙̲̭̲̬̰̠̤̖̱͇̮̣͓ͪ͑ͫͭ́̔ͫͧ̆͆ͪ̏̌̃ͦͭͅ7̦͖̻̬̮͙̝̻̝͙͉̑̽ͤͭͥ̆̚͝ͅ9̧̲̫͎̜̙̬͈̼̝̺̠̞͖̭̙̖̝͉͙̯̥̲̩̹̞͚̙̣̱̮̱̤̩̝͇̭̺̯̦̭̦͖̰̩̉ͪ͂͆̓ͭͦͨ͌͗ͣ̅̽ͣ̏ͤ̑ͅ2̱̜̬̮̬̠̻̩͌ͩ̅̏ͧ̏̔̌̑͗͊͊̀f͆ͮ̒͛̔ͭ̇̄͊ͩͥ̍͑͌ͦ̀̚ ҉̫͚̥̖̣̠̤͍͖͍̮̖͔̥̯͙͎͖̱̻̗̩ͅ9̖̖̫̼͈͉͚͎̟̞̰̩͉͉̿̅ͤ́́ͤ̈ͩ͠ͅ1̧̳̘̪̙͖͎̩͎͖͕̳̼̠̲͙̠̫̫̞̋͌ͦ͒ͧ͋ͅ8̹͙̞̭̗̱̈ͫͩ͒̇͠2͚̮͙̦̫͎̩͓̦̭̲̗̜̝̗̰̘̭̪͗̋̓̄ͪ͘f̧̘͈͓̖̳̳̠͍̤̱̜͓̭̯̻͓͖͕̖̰̟̤̘̺̤̙̹̜͖̝̳̜̞̼̓̿̃ͩ̊͊̉̌̓̚",
            "silly": "\u001b[31md\u001b[39m\u001b[33m8\u001b[39m\u001b[32md\u001b[39m\u001b[34m7\u001b[39m\u001b[35m9\u001b[39m\u001b[31m0\u001b[39m\u001b[33m3\u001b[39m\u001b[32mf\u001b[39m\u001b[34m-\u001b[39m\u001b[35mc\u001b[39m\u001b[31ma\u001b[39m\u001b[33m0\u001b[39m\u001b[32mb\u001b[39m\u001b[34m-\u001b[39m\u001b[35m4\u001b[39m\u001b[31m9\u001b[39m\u001b[33mc\u001b[39m\u001b[32m7\u001b[39m\u001b[34m-\u001b[39m\u001b[35mb\u001b[39m\u001b[31m1\u001b[39m\u001b[33mc\u001b[39m\u001b[32m9\u001b[39m\u001b[34m-\u001b[39m\u001b[35m1\u001b[39m\u001b[31me\u001b[39m\u001b[33md\u001b[39m\u001b[32m7\u001b[39m\u001b[34m9\u001b[39m\u001b[35m2\u001b[39m\u001b[31mf\u001b[39m\u001b[33m9\u001b[39m\u001b[32m1\u001b[39m\u001b[34m8\u001b[39m\u001b[35m2\u001b[39m\u001b[31mf\u001b[39m",
            "input": "\u001b[30md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "verbose": "\u001b[36md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "prompt": "\u001b[90md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "info": "\u001b[32md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "blank": "\u001b[37md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "data": "\u001b[90md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "help": "\u001b[36md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "warn": "\u001b[33md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "debug": "\u001b[34md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "error": "\u001b[31md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m",
            "crit": "\u001b[31md8d7903f-ca0b-49c7-b1c9-1ed792f9182f\u001b[39m"
        },
        "value": "76.4",
        "id": "122d8346-3554-4336-bc87-04d42fc2b759",
        "createdAt": "2015-04-19T02:59:33.755Z",
        "updatedAt": "2015-04-19T02:59:33.755Z"
    }

the values of 0-35 are the contents of the things ID field it's trying to link to

Mrono commented 9 years ago

Also, i'm using sails-disk, I haven't tried anything else yet

ghost commented 9 years ago

i dont know why but maybe this will useful :-) id: { type: 'uuid', }

rmckeel commented 8 years ago

Hello @tjwebb, I am able to duplicate, but as far as I can tell it doesn't have anything to do with waterline. I am using 'lokijs' library to insert records; occasionally, I will access the loki collection. It should return

  data:  [ 'my real data' ],
  ...

And instead I get a 'trashed' object with similar junk to what is seen above, additionally some of the object's functions don't exist (undefined), arrays with real data in them turned into {}, etc.

{
  bold: '\u001b[1mmyname123\u001b[22m',
  underline: '\u001b[4mmyname123\u001b[24m',
  strikethrough: '\u001b[9mmyname123\u001b[29m',
  italic: '\u001b[3mmyname123\u001b[23m',
  inverse: '\u001b[7mmyname123\u001b[27m',
  grey: '\u001b[90mmyname123\u001b[39m',
  black: '\u001b[30mmyname123\u001b[39m',
  yellow: '\u001b[33mmyname123\u001b[39m',
  red: '\u001b[31mmyname123\u001b[39m',
  green: '\u001b[32mmyname123\u001b[39m',
  blue: '\u001b[34mmyname123\u001b[39m',
  white: '\u001b[37mmyname123\u001b[39m',
  cyan: '\u001b[36mmyname123\u001b[39m',
  magenta: '\u001b[35mmyname123\u001b[39m',
  greyBG: '\u001b[49;5;8mmyname123\u001b[49m',
  blackBG: '\u001b[40mmyname123\u001b[49m',
  yellowBG: '\u001b[43mmyname123\u001b[49m',
  redBG: '\u001b[41mmyname123\u001b[49m',
  greenBG: '\u001b[42mmyname123\u001b[49m',
  blueBG: '\u001b[44mmyname123\u001b[49m',
  whiteBG: '\u001b[47mmyname123\u001b[49m',
  cyanBG: '\u001b[46mmyname123\u001b[49m',
  magentaBG: '\u001b[45mmyname123\u001b[49m',
  rainbow: '\u001b[31mt\u001b[39m\u001b[33mh\u001b[39m\u001b[32mr\u001b[39m\u001b[34me\u001b[39m\u001b[35ma\u001b[39m\u001b[31mt\u001b[39m\u001b[33ms\u001b[39m\u001b[32mV\u001b[39m\u001b[34m2\u001b[39m',
  zebra: 't\u001b[7mh\u001b[27mr\u001b[7me\u001b[27ma\u001b[7mt\u001b[27ms\u001b[7mV\u001b[27m2',
  stripColors: 'myname123',
  zalgo: 't̺̙̼̬̻̘̫̲̠̘͍̳͍͔͖̪͔̙̜̮̱̜̼̮͕̞̖̣̰̯̗̯̠̭͓̫̭̟̠͓̗̟̫ͪ̋ͭͯ̏ͬ̉ͥ̚͝h̶̘̩̺̞̪͕͎͎͚͓̣̲̹̬̱̱̲̻͉̹̻̮͉̝̻͇̜̫͔͙̩̪̺̺̩̣̺͈̜͚̩̥͔̯̺͈̼̫̤͖̦̩͚̪̖̹̞̱̣̣̬̋̉͒̓ͪ̿͐̚ͅͅͅr̸̤̜̮̫͔̼̜̼̺͖͓̩̭͍̹̫̘͙̬͕̦̹͍̲͓̞̘̮̝̟̥̟̦͈͎̮̠̥̝̝͖͈̮̝͖̱̠͇̝̳͌͐̍ͥͨͧ̏̀̔ͬͣ͒̚ͅe̹͙̣̘̥̜͙͈͇̪̱̗̋̆̏̔̐̓͐ͨ̀̄ͭ͂̑̐̌̕a͖̦̻͓̫̘̣̘̝̜̰͍̼̫̣͇̝̠̙͚̬̩̣̦̦̟̫̳͎̗̣̙͖͈̖̱͕̻̠̻͕̖̹̮̠͙̻̞̪͓̩̙͉̖͙͖͔̝ͬͣ̾̓̾̐̑ͮ̐ͭ͐ͧͥ̂̚̚͘ͅͅẗ̛̙̜̥̝͓͚̯̘̥̮̼͚͈̫̫͇̻̻̣̘̪̣̬̫̼̪̮̹̞̩̘̯̳ͥ̿͂̒͛͑̋͐̂ͫ̔͗ͅͅs̨͉͖̖̖͉̣͉͖̞̰̦̼̳̹̖͔̬̝̰̫̦̫̬̘͎̖̖̰̝̠͙̜̜͙͎̜̝̮̝͙̬͉͙̫̥̪̙̥̻̬͇͂̈̓̂̾͛̀̏ͧ̈ͧ͂̔ͅV̹̬̫͙͙͔̦̩̟̼͔̗̥͙͍̯̰̦͓͎̹͔̞̩̠͖͎͖͔̙͉̘̗̫̲̘̯͚̝̰̐͛́ͯ̓͋̈́͗ͮ̃̏́ͤ̓̑͢2̙̜̭̠̭͉̟̬͖̜̞̺̳̜̳̱͂̆ͣ͐͑̌̀̓͂̃̋̒̈̅ͧ͝',
  silly: '\u001b[31mt\u001b[39m\u001b[33mh\u001b[39m\u001b[32mr\u001b[39m\u001b[34me\u001b[39m\u001b[35ma\u001b[39m\u001b[31mt\u001b[39m\u001b[33ms\u001b[39m\u001b[32mV\u001b[39m\u001b[34m2\u001b[39m',
  input: '\u001b[30mmyname123\u001b[39m',
  verbose: '\u001b[36mmyname123\u001b[39m',
  prompt: '\u001b[90mmyname123\u001b[39m',
  info: '\u001b[32mmyname123\u001b[39m',
  blank: '\u001b[37mmyname123\u001b[39m',
  data: '\u001b[90mmyname123\u001b[39m',
  help: '\u001b[36mmyname123\u001b[39m',
  warn: '\u001b[33mmyname123\u001b[39m',
  debug: '\u001b[34mmyname123\u001b[39m',
  error: '\u001b[31mmyname123\u001b[39m',
  crit: '\u001b[31mmyname123\u001b[39m' },
  data: {},
  idIndex: {},
...

I will keep investigating, but any idea where this data comes from? Is this an issue with JS memory getting tromped somehow? I can reliably reproduce after about 20 seconds of running my app. The only adapter I am using is a modified nedb disk-based adapter for session storage, which has been working properly for the last few months.

Thanks,

Ryan

P.S. debug info: sails -v = 0.11.2 node --version = v0.12.5 npm --version = 3.3.8

P.P.S. I created a new issue here https://github.com/balderdashy/sails/issues/3403 since this issue is closed. Thanks ~

rmckeel commented 8 years ago

Please ignore my previous comment as my problem was unrelated to Sails; I found a logic error in my code. I was munging the object myself, and the result was running lodash mapValues over the top of the loki function. I've included sample code below in case it helps anyone down the road, where objectToMunge is a LokiJS collection (or other) object.

Thanks, Ryan

    objectToMunge = _.mapValues(objectToMunge, function (data) {
      return _.omit(data, function (n, stamp) {
        // omit any values with a key older than cuttoffStamp
        return (stamp * 1000) < cutoffStamp;
      });
    });
David-Melo commented 8 years ago

I'm getting this exact behavior, however, I'm not using any mapping functions. I'm trying to Populate an association and I'm getting this same garbage output.

David-Melo commented 8 years ago

https://github.com/balderdashy/sails/issues/2033

I've been reading around and actually found some more leads. It seems my issue it might be related to the primary key on the model I'm attempting to populate from.

mikermcneil commented 8 years ago

Thanks @David-Melo. I think you're on the right track with the primary key. If you find out anything further, please post it here and/or open an issue in Waterline.

dash- commented 8 years ago

I'm having this same issue with an association using my own published adapter, sails-derby. Since the adapter is pretty new and not heavily battle-tested, it could have something to do with that adapter. However, that adapter does pass all tests in the sails adapter tests except the query() test, because Derby doesn't like MySQL/PostgreSQL type SQL. It also has been working fine for all associations thus far. I doubt my issue stems from the primary key I'm using. It has a very unique name "G_OTH_ANNOTATIONS_OBJECT_ID", and a very benign value (typically a numeric string like "3532323". Other primary keys have very similar names and very similar values and work fine.

Since this issue is something I must resolve for my work, I will figure out what is wrong and will submit a solution here (as well as a pull-request if applicable).

dash- commented 8 years ago

Please re-open this issue. There is indeed a bug, and I have tracked down the responsible code.

lib/waterline/model/lib/model.js

  // Attach attributes to the model instance
  for (var key in attrs) {
    this[key] = attrs[key];

    if (this.associationsCache.hasOwnProperty(key)) {
      this.associationsCache[key] = _.cloneDeep(attrs[key]);
    }
  }

That for(var key in attrs) { is the source of the bug. It does not check for whether the attribute has the property itself, and should be replaced with something like _.forEach or something appropriately similar that is mindful of whether it is an object's own properties.

Of course, it is also getting a bad value for attrs to give the above mess of code -- it is getting a string rather than a real object. That issue is caused somewhere else, but that method (the Model constructor) should of course attempt to validate its input (attrs) and do something appropriate (throw perhaps) when it comes across a bad value. Perhaps something like:

if(! _.isObject(attrs)) {
  throw new Error('Expected attrs to be an object, got ' + typeof attrs);
}
dash- commented 8 years ago

Next, a bug further up in the call chain is inappropriately calling collection._model (ultimately invoking the above bad for loop):

lib/waterline/query/finders/joins.js:197

    // If the value isn't an array it's a populated foreign key so modelize it and attach
    // it directly on the attribute
    collection = self.collections[joinKey];
    value[key] = collection._transformer.unserialize(value[key]);
    value[key] = new collection._model(value[key], { showJoins: false });

If value[key] is a non-object, this shouldn't run. You would think it would always be an object, but what if the join didn't return any results? This is precisely my main issue and the reason I'm only seeing this bug on weird rare occasions: I'm occasionally joining to a table that does not possess the foreign key, because my primary table uses the same field for joining to various different tables based on a "type" field. So, if the join is to the wrong table for that particular row, the data for the join has only nulls, and this field is becoming a regular field (so not an object).

It would be nice to have a check above, like:

lib/waterline/query/finders/joins.js:~165

// Ensure the value is an object or an array
if (! _.isObject(value[key])) return;
dash- commented 8 years ago

Finally, the only query being executed in the join method of my adapter (sails-derby) is returning something similar to the below object (fields and data changed for security purposes, of course):

{
    FIELD_PK: '123456',
    FIELD_X: 'SOMESTRING',
    FIELD_Y: 'ANOTHERSTRING',
    FIELD_FK___FOREIGN_FIELD_PK: null,
    FIELD_FK___FORIEGN_FIELD_FIELD_X: null,
    // etc
}

The method then removes the fields containing __ after parsing the fields and putting them into cachedChildren. (This is just copying the way the balderdash sails-mysql handles joins -- this whole method borrows heavily from the same method of sails-mysql). Then, as it processes the instructions (again copying from sails-mysql), it loops through the cachedChildren looking for the child with PK that matches the parent's FK. But it never finds it, because the child is null including the Primary Key. It's a flaw in the basic logic - and in my defense as the sails-derby author - logic I copied from sails-mysql. ;-)

This means that the null child will never be associated with the parent, and the parent's unmatched foreign key field will revert to just being a regular old field with a regular old value. This regular value field will eventually be passed along to lib/waterline/query/finders/joins.js for processing. However, since the method in that file expects an object rather than a regular value (and doesn't check), this finally turns into weird object with all of the mysterious attributes of a string in sails like bold, underline, etc.

Mystery solved, but it's also not trivial to fix.