Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Select specific cols(issue #143) #384

Closed
wants to merge 16 commits into from

3 participants

@jvskelton
  • created model.select to query specific columns
  • either returns array of data or array of objects(see README)
  • error handling for adapters that have not implemented the functionality(currently only supported in jvskelton/jugglingdb-myql until merge).
@jvskelton

*correction jvskelton/mysql-adapter

@1602
Owner

I would suggest to implement it in a different way: in mysql adapter, adapter.all method you can check for filter.attributes and replace * in query with the list of attributes when filter.attributes is present, no reason for adding another core method which will work only for limited set of adapters, especially in this particular case when it can be implemented by extending existing feature.

@1602 1602 closed this
@jvskelton

My only concern with that however, would be the fact that currently, I return either an array of data(if one attributed is given) or an array of object literals(if more the one attribute is given), which means the return type would be different. I felt that returning a model instance with potentially several undefined values, along with the model specific functions attached was a bit redundant(not to mention could cause problems if that instance was saved). What are your thoughts on that?

@anatoliychakkaev
Collaborator
@jvskelton

@anatoliychakkaev This is a bit delayed, but just to clarify, you do or do not want a new method in the jugglinddb core(which is currently how it works). I believe the last comment conflicts with your first comment. Thanks.

@1602
Owner
  1. new method in jugglingdb which will call .all with {attributes:[]} property and also ensure that returned dataset only includes required attributes (removing all unexpected attributes).
  2. in adapter: hook up attributes property in adapter.all method

my comments are not in conflict. first comments meaning is that we don't need additional method - all you need is just add support of 'attributes' clause in adapter.all method. second comment suggests to add core method for convenience and cross-adapters compatibility, so that this feature will work in the same way in all adapters not only adapters with 'attributes' clause support in adapter.all method. but you have to filter object manually in case if it contains unexpected attributes. hope that makes sense.

@jvskelton

Ah, ok that makes sense, thank you for clarifying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 20, 2014
  1. @jvskelton

    Modified adapter.all to ignore properties not included in attributes(…

    jvskelton authored
    …if attributes are present)
  2. @jvskelton
  3. @jvskelton

    removed console.log

    jvskelton authored
Commits on Feb 24, 2014
  1. @jvskelton

    Added new function 'select' to enable selection of specific cols sinc…

    jvskelton authored
    …e the returned data type may be different, depending on the attributes given(if any). Added .select to readme
  2. @jvskelton

    format fix

    jvskelton authored
  3. @jvskelton

    Update README.md

    jvskelton authored
  4. @jvskelton
  5. @jvskelton
  6. @jvskelton
  7. @jvskelton

    removed semi colon

    jvskelton authored
  8. @jvskelton

    changed travis ci img url

    jvskelton authored
  9. @jvskelton
  10. @jvskelton
Commits on Feb 28, 2014
  1. @jvskelton

    Update README.md

    jvskelton authored
    set travis ci Image back to 1602/jugglingdb
  2. @jvskelton
  3. @jvskelton
This page is out of date. Refresh to see the latest.
View
8 README.md
@@ -1,5 +1,5 @@
[![Stories in Ready](https://badge.waffle.io/1602/jugglingdb.png?label=ready)](https://waffle.io/1602/jugglingdb)
-## About [<img src="https://secure.travis-ci.org/1602/jugglingdb.png" />](http://travis-ci.org/#!/1602/jugglingdb)
+-## About [<img src="https://secure.travis-ci.org/1602/jugglingdb.png" />](http://travis-ci.org/#!/1602/jugglingdb)
[JugglingDB(3)](http://jugglingdb.co) is cross-db ORM for nodejs, providing
**common interface** to access most popular database formats. Currently
@@ -215,6 +215,12 @@ Post.all(cb)
Post.all({where: {userId: user.id}, order: 'id', limit: 10, skip: 20});
// the same as prev
user.posts(cb)
+// all posts
+Post.select(cb)
+// specific column from Post(returns array of data, ex. [1,2,3,4])
+Post.select({where: {userId: user.id}, attributes: ['id']}, cb)
+// specific columns from Post(returns array of objects, ex [{id: 1, name: 'Todd'}, {id: 2, name: 'Bill'})
+Post.select({where: {userId: user.id}, attributes: ['id' , 'name']}, cb)
// get one latest post
Post.findOne({where: {published: true}, order: 'date DESC'}, cb);
// same as new Post({userId: user.id});
View
64 lib/adapters/memory.js
@@ -105,6 +105,70 @@ Memory.prototype.fromDb = function(model, data) {
return data;
};
+Memory.prototype.select = function all(model, filter, callback) {
+ var self = this;
+ var table = this.table(model);
+ var nodes;
+ if (filter && filter.attributes) {
+ nodes = filter.attributes;
+ } else {
+ nodes = Object.keys(this.cache[table]).map(function (key) {
+ return this.fromDb(model, this.cache[table][key]);
+ }.bind(this));
+ }
+ if (filter) {
+
+ // do we need some sorting?
+ if (filter.order) {
+ var props = this._models[model].properties;
+ var orders = filter.order;
+ if (typeof filter.order === "string") {
+ orders = [filter.order];
+ }
+ orders.forEach(function (key, i) {
+ var reverse = 1;
+ var m = key.match(/\s+(A|DE)SC$/i);
+ if (m) {
+ key = key.replace(/\s+(A|DE)SC/i, '');
+ if (m[1].toLowerCase() === 'de') reverse = -1;
+ }
+ orders[i] = {"key": key, "reverse": reverse};
+ });
+ nodes = nodes.sort(sorting.bind(orders));
+ }
+
+ // do we need some filtration?
+ if (filter.where) {
+ nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
+ }
+
+ // limit/skip
+ filter.skip = filter.skip || 0;
+ filter.limit = filter.limit || nodes.length;
+ nodes = nodes.slice(filter.skip, filter.skip + filter.limit);
+
+ }
+
+ process.nextTick(function () {
+ if (filter && filter.include) {
+ self._models[model].model.include(nodes, filter.include, callback);
+ } else {
+ callback(null, nodes);
+ }
+ });
+
+ function sorting(a, b) {
+ for (var i=0, l=this.length; i<l; i++) {
+ if (a[this[i].key] > b[this[i].key]) {
+ return 1*this[i].reverse;
+ } else if (a[this[i].key] < b[this[i].key]) {
+ return -1*this[i].reverse;
+ }
+ }
+ return 0;
+ }
+};
+
Memory.prototype.all = function all(model, filter, callback) {
var self = this;
var table = this.table(model);
View
64 lib/model.js
@@ -507,6 +507,70 @@ AbstractClass.all = function all(params, cb) {
};
/**
+ * Find all instances of Model, matched by query, unless specific columns are
+* provided in the attributes param, which will return either an array of objects(if multiple columns provided)
+* or an array of column data if only one provided.
+ * make sure you have marked as `index: true` fields for filter or sort
+ *
+ * @param {Object} params (optional)
+ *
+ * - where: Object `{ key: val, key2: {gt: 'val2'}}`
+* - attributes: Array '['id, 'name']
+ * - include: String, Object or Array. See AbstractClass.include documentation.
+ * - order: String
+ * - limit: Number
+ * - skip: Number
+ *
+ * @param {Function} callback (required) called with arguments:
+ *
+ * - err (null or Error)
+ * - Array of instances
+ */
+AbstractClass.select = function all(params, cb) {
+ if (stillConnecting(this.schema, this, arguments)) return;
+
+ if (arguments.length === 1) {
+ cb = params;
+ params = null;
+ }
+ if (params) {
+ if ('skip' in params) {
+ params.offset = params.skip;
+ } else if ('offset' in params) {
+ params.skip = params.offset;
+ }
+ }
+ var constr = this;
+ if (this.schema.adapter.schema) {
+ this.schema.adapter.select(this.modelName, params, function (err, data) {
+ if (data && data.forEach) {
+ if (!params || !params.onlyKeys) {
+ data.forEach(function (d, i) {
+ var obj = new constr;
+ if (params && !params.attributes) {
+ obj._initProperties(d, false);
+ if (params && params.include && params.collect) {
+ data[i] = obj.__cachedRelations[params.collect];
+ } else {
+ data[i] = obj;
+ }
+ }
+ });
+ }
+ if (data && data.countBeforeLimit) {
+ data.countBeforeLimit = data.countBeforeLimit;
+ }
+ cb(err, data);
+ }
+ else
+ cb(err, []);
+ });
+ } else {
+ cb(new Error('Select functionality has not been implemented in this adapter.'))
+ }
+};
+
+/**
* Iterate through dataset and perform async method iterator. This method
* designed to work with large datasets loading data by batches.
*
View
23 test/basic-querying.test.js
@@ -131,7 +131,30 @@ describe('basic-querying', function() {
done();
});
});
+ });
+ describe('select', function () {
+
+ it('should query collection and return given attribute as array', function(done) {
+ User.select({attributes: ['id']}, function(err, users) {
+ should.exists(users);
+ should.not.exists(err);
+ users.should.be.instanceOf(Array);
+ done();
+ });
+ });
+
+ it('should query collection and return given attributes', function(done) {
+ User.select({attributes: ['id', 'name']}, function(err, users) {
+ should.exists(users);
+ should.not.exists(err);
+ should.not.exists(users.mail);
+ should.not.exists(users.order);
+ should.not.exists(users.id);
+ should.not.exists(users.name);
+ done();
+ });
+ });
});
describe('count', function() {
Something went wrong with that request. Please try again.