Skip to content

Instantly share code, notes, and snippets.

@assafweinberg
Last active June 26, 2019 11:44
Show Gist options
  • Save assafweinberg/4c7ef4e833d0493c693b to your computer and use it in GitHub Desktop.
Save assafweinberg/4c7ef4e833d0493c693b to your computer and use it in GitHub Desktop.
SQL Join with LokiJS
import loki from 'lokijs';
import _ from 'lodash';
class relationalWrapper {
constructor(dbName) {
this.db = new loki('dbName');
}
//Utility to strip loki meta data
_stripMetaData(obj) {
var returnValue = _.clone(obj);
delete returnValue.$loki;
delete returnValue.meta;
return returnValue;
}
/**
* Convenience method to create objects that contain fields from objects in two collections (like sql join)
* The objects returned are clones of the original, so updating them won't update the originals in the DB.
* @param left {String || ARRAY} name of left side collection or data array to use for left side
* @param right {String || ARRAY} name of right side collection or data array to use for right side
* @param joinField {String} name of property to join collections on.
* @param joinFieldRight {String} (optional) Name of the join property for the right side collection if supplied. If not supplied, joinField is used.
*/
innerJoin(left, right, joinField, joinFieldRight) {
//Populate data for left array if only a collection name is specified
if(!Array.isArray(left)) {
var leftColl = this.db.getCollection(left);
left = leftColl.find();
}
//If no left data, bail early
if(!left || left.length == 0)
return [];
//Strip metadata from left side
left = left.map((item)=>{
return this._stripMetaData(item);
});
//If no right join key is specified, use the left key
joinFieldRight = joinFieldRight || joinField;
//Get data for right array if only a collection name is specified
if(!Array.isArray(right)){
var rightColl = this.db.getCollection(right);
right = rightColl.find();
}
//if no right data, bail early by returning left data
if(!right || right.length == 0)
return _.cloneDeep(left);
//strip metadata from right side
right = right.map((item)=>{
return this._stripMetaData(item);
});
var results = [];
left.forEach((leftItem)=>{
var leftKey = leftItem[joinField];
let leftObj = _.assign({},leftItem);
//Get all matches in right table
var rightItems = right.filter((item)=>{
return item[joinFieldRight] === leftKey;
});
//If no right matches, just add the left item attributes
if(rightItems.length === 0){
results.push(leftObj);
//Inner join - create a separate object for each match in the right column
} else {
rightItems.forEach((rightItem)=>{
let combined = _.clone(leftObj)
for(var key in rightItem){
var prefix = (leftObj.hasOwnProperty(key) && key !== joinField) ? "right_" : ''
combined[prefix + key] = rightItem[key];
}
results.push(combined);
});
}
});
return results;
}
}
export default relationalWrapper;
@mimiz
Copy link

mimiz commented Dec 31, 2015

Hi,

Thank you for your gist !
Could you please provide a usage example ? because i was not able to use it ...
I do something like this ...

var rw = new RelationalWrapper('mydb.json');
rw.innerJoin("colA", "colB", "key", "foreignKey");

And i have the following error :

/.../app/RelationalWrapper.js:32
        left = leftColl.find();
                       ^
TypeError: Cannot read property 'find' of null

I would really appreciate a usage example ...

Regards

Remi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment