qstore

work with collections in js

##Overview Work with collections in javascript

  • Create your collections.
  • Search and update data using queries.
  • Use computed fields.
  • Get collections changes.
  • Extend your query language.

###Simple examples Find all green apples from fruits collection:

fruits.find({type: 'apple', color: 'green'});

Find all apples and pears from fruits collection:

fruits.find({type: ['apple', 'pear']});

Which fruits can be red?

   fruits.getList({color: 'red'}, 'type');// ['apple', 'pear', 'strawberries'] 

See more examples

##Instalation

###Front-end

vanila-js style:

<script type="text/javascript" src="qstore/qstore.js"></script>

via bower package manager:

bower install qstore

###Back-end

nodejs:

npm install qstore

##API

###Initialisation

Using array of objects:

var fruits = new Qstore([
    {type: 'apple', color: 'red', weight: 0.25, price: 1.5},
    {type: 'pear', color: 'green', weight: 0.4, price: 2},
    {type: 'pear', color: 'red', weight: 0.3, price: 1.8},
    {type: 'apple', color: 'yellow', weight: 0.26, price: 1.2},
]);

Using reduced format:

var fruits = new Qstore({
    columns: ['type', 'color', 'weight', 'price'],
    rows: [
        ['apple', 'red', 0.25, 1.5],
        ['pear', 'green', 0.4, 2],
        ['pear', 'red', 0.3, 1.8],
        ['apple', 'yellow', 0.26, 1.2]
    ]
});

###Data search ####.find (query, [fields], [options])

Returns all objects which are valid for query. See examples of usage

  • query {Object|Array|Function|Bolean}
    If query is true then all rows will be returned.
    If query is Object or Array:
    { } - contains conditions separeted with and
    [ ] - contains conditions separeted with or
    Operators describe as $<operator name>, see Operators.
    Example:

// find all red or green fruits with price between 0.5 and 1.5
fruits.find({color: ['red', 'green'], price: {$gt: 0.5, $lt: 1.5}});

 
```js
// using regular expressions
fruits.find({type: /apple/});//returns all apples and pineapples

If query is Function:
Example:

// find all red fruits 
fruits.find(function (row) {
    return row.color == 'red';
});

If query is Object that contains functions:

Function with field-context:

// find all fruits with integer price or with 0.5$ price 
fruits.find({price: [0.5, function (price) { return price % 1 == 0}]);

Function with row-context:

// find all fruits with integer price or with 0.5$ price 
fruits.find([price: 0.5, function (row) { return row.price % 1 == 0});
  • [fields=true] {Array|Boolean}
    Array of field names which will be added to result.
    Example:

      fruits.find({type: 'apple'}, ['type', 'color', 'price']);

Also you can use fields aliases

  • [options] {Object}

    • limit: rowsCount
    • limit: [fromRow]
    • limit: [fromRow, RowsCount]

    Example:

      // find first two apples 
      fruits.find({type: 'apple'}, true, {limit: 2});
      
      // find two yellow fruits beginning with third yellow fruit 
      fruits.find ({color: 'yellow'}, true, {limit: [3,2]});
      

#####Deep search:

Deep search in object:

 
    var usersMessages = new Qstore ({
        columns: ['text', 'subject', 'user'],
        rows: [
            ['Hi', 'new year', {id: 1, name: 'Bob', company: {name: 'IBM', phone: '+9999'} }],
            ['Happy new year!', 'new year', {id: 2, name: 'Kate', company: {name: 'Microsoft', phone: '+8888'}}],
            ['How to learn javascript?', 'programming', {id: 2, name: 'Stan'}],
            ['Anyone want to dance?', 'new year', {id: 2, name: 'James'}]
        ]
    });
 
    // find all messages with subject 'New year' from user with name 'Bob' who works in 'IBM' company 
    
    messages.find({subject: 'new year', 'user.name': 'Bob', 'user.company.name': 'IBM'});
 
    // or 
    messages.find({subject: 'new year', user: {name: 'Bob', company: {name: 'IBM'} }});
    

Deep search in collection:

    
    // create collection of costumes 
    var costumes = new Qstore([
        {name: 'policeman', items: [ {name: 'tie', color: 'black'}, {name: 'cap', color: 'blue'}]},
        {name: 'fireman', items: [{name: 'hemlet', color: 'yellow'}]},
        {name: 'solder', items: [{name: 'hemlet', color: 'green'}]}
    ]);
    
    // find costumes which have hemlet in items 
    costumes.find({items: {name: 'hemlet'} });

#####Aliases:

You can create aliases for fields by using the syntax "fieldName:aliasName" or by alias map

    // create collection of messages 
    var messages = new Qstore ({
        columns: ['text', 'subject', 'user'],
        rows: [
            ['Hello world!', 'programming', {id: 1, name: 'Bob'}],
            ['Happy new year!', 'new year', {id: 2, name: 'Kate'}],
            ['How to learn javascript?', 'programming', {id: 2, name: 'Stan'}],
            ['Anyone want to dance?', 'new year', {id: 2, name: 'James'}]
        ]
    });
 
    // we need to select "text" and "user.name" from messages collection 
 
    // first way 
    messages.find({subject: 'new year'}, ['text', 'user.name:userName']);
 
    // second way 
    messages.find({subject: 'new year'}, {text: true, userName: 'user.name'});
 
    // [ {text: 'Happy new year!', userName: 'Kate'}, {text: 'Anyone want to dance?', userName: 'James'}] 

result of example:

    [
        {text: 'Happy new year!', userName: 'Kate'},
        {text: 'Anyone want to dance?', userName: 'James'}
    ]

Use "fieldname:" syntax to extract field values on one level up.

    // create collection of changes 
    var usersChanges = new Qstore ({
        columns: ['source', 'patch'],
        rows: [
            [{id: 2, name: 'Bob', age: 23}, {name: 'Mike'}],
            [{id: 4, name: 'Stan', age: 30}, {age: 31}]
        ]
    });
    
    var patchForDataBase = usersChanges.find(true, ['source.id:id', 'patch:']);
    // now patchForDataBase contains: 
    // [{id: 2, name: 'Mike'}, {id: 4, age: 31}]; 
    

#####Comparison of fields.

Use '$.fieldName' syntax to get the value of field

    // create collection of diet 
    var diet = new Qstore ({
        columns: ['month', 'breakfast', 'dinner'],
        rows: [
            ['april', {calories: 400, food: 'egg'}, {calories: 300, food: 'soup'}],
            ['may', {calories: 300, food: 'bacon'}, {calories: 500, food: 'soup'}],
            ['june', {calories: 350, food: 'porridge'}, {calories: 300, food: 'chicken'}]
        ]
    });
    
    // find diet where dinner calories less when breackfast calories 
    diet.find({'dinner.calories': {$lt: '$.breakfast.calories'} });

#####Queries concatenation:

    // create two filters 
    var filter1 = {type: 'apple'};
    var filter2 = {color: 'red'};
    
    // search rows valid for filter1 or filter2 
    var commonFilter1 = [filter1, filter2]
    
    // search rows valid for filter1 and filter2 
    var commonFilter2 = {$and: [filter1, filter2]};
    

####.search (query [,fields=true] [,options]) Same as .find but returns Qstore collection

    // get collection of red fruits sorted by type 
    fruits.search({color: 'red'}).sort({fieldName: 'type', order: 'asc');

search with extending

    clients.search({status: 'online'}).sendMessage('hello!');

####.findOne (query, [,fields=true] [,options])

find first row valid for query. It same as:

    .find(query, fields, {limit: 1})[0]

####Qstore.findIn (rows, query [,fields=true] [,options]) same as .find but work as static method with array.

    var users = [
        {name: 'user1', id: 1, email: 'user1@anymail.com'},
        {name: 'user2', id: 2, email: 'user2@anymail.com'},
        {name: 'user3', id: 3, email: 'user3@anymail.com'},
        {name: 'user4', id: 4, email: 'user4@anymail.com'}
    ];
    
    // find user with id = 3 
    Qstore.findIn(users, {id: 3});

####Qstore.test (object, query) Checks that the object match the query.

    var fruit = {type: 'pineapple', color: 'yellow', weight: 1, price: 4};
    
    // The fruit is yellow? 
    Qstore.test(fruit, {color: 'yellow'}); //true 
    
    // The fruit is pineapple or pear? 
    Qstore.test(fruit, {type: ['pear', 'pineapple']}); //true 
    
    // The fruit has "apple" in type? 
    Qstore.test(fruit, {type: {$like: 'apple'}}); //true 
    
    // Fruit price less when 1$ per kg 
    Qstore.test(fruit, function (fruit) { return fruit.price/fruit.weight < 1});//false 

####.getList ([query] [,fieldName='idx']); Returns list of values for fieldName.
Elements of the list are not repeated.

Examples:

    // list of all fruits colors 
    fruits.getList('color'); // ['red', 'green', 'yellow'] 
    
    // list of all pears colors 
    fruits.getList({type: 'pear'}, 'color');// ['green', 'red'] 
    
    // What fruits can be red? 
    fruits.getList({color: 'red'}, 'type');// ['apple', 'pear', 'strawberries'] 
    
    // get fruits types with idx in [3, 5, 6] 
    fruits.getList({idx: [3, 5, 6]}); //['pear', 'apple', 'banana'] 
    
    //get list of idx 
    fruits.getList();
    
    // list of deep fields 
    messages.getList('user.name'); // ['Bob', 'Kate', 'Stan', 'James'] 
 
    // you can also use function instead field name 
    fruits.getList({type: 'apple'}, function (fruit) {
        return fruit.color + ' ' + fruit.type}
    } // ['red apple', 'yellow apple', 'green apple'] 
 

####.each ([query=true,] fn) apply function for each row

  • [query=true] {Object|Function|Boolean} filter query
  • fn {Function} function to apply
 
    // add message to log for each fruit 
    fruits.each(function (rowiquery) {
        conslole.log('fruit №' + i + ' is ' + row.type);
    });
    
    // it will write: 
    // 
    // fruit №1 is apple 
    // fruit №2 is pear 
    // etc...  
    // 
    

###Operators Оperators are used to extending the query language of search operations. Each operator is function which returs true if item valid for query or false if not.

####Build-in operators

namedescription
$eqequals
$nenot equals
$gtmore then
$ltless then
$gtemore or equals then
$lteless or equals then
$andchange condition of [ ] operator from or to and
$like"like" search
$hascheck exsisting of value in array or in object or in string see $has operator

you can also add your operators - see addOperator method

$has operator:

   var clothes = new Qstore([
       {name: 'skirt', sizes: ['xs', 's', 'xl']},
       {name: 'jeans', sizes: ['m', 'xxl']},
       {name: 'skirt', sizes: ['xs', 's', 'xl']}
   ]);
   
   clothes.find({name: 'skirt', sizes: {$has: 'xs'}});
   
   clothes.find({name: 'skirt', sizes: {$has: ['xs', 's'] }});
   
   

####Qstore.addOperator (operatorName, function [isSimple=true])

Example:

    /* we need find fruits with integer price */
    
    // add "isInt" operator 
    Qstore.addOperator('isInt', function (leftright) {
        var isInt = (left % 1 == 0);
        return right ? isInt : !isInt
    });
    
    // find them 
    fruits.find({price: {$isInt: true}});
    
    // find other 
    fruits.find({price: {$isInt: false}});
    

####Qstore.removeOperator (operatorName)

Remove operator by operatorName.


<a name="functions"">

Functions used for runtime calculations in query. You may use build-in functions or you own functions

Example of usage "length" function

 
    // create collection of users 
    users = new Qstore ([
        {id: 1, name: 'Bob', friends: ['Mike', 'Sam']},
        {id: 2, name: 'Martin', friends: ['Bob']},
        {id: 3, name: 'Mike', friends: ['Bob', 'Martin', 'Sam']},
        {id: 4, name: 'Sam', friends: []}
    ]);
 
    // find users who have not any friend 
    users.find({'friends.$length': 0});
    
    // find users who have more than 2 friends 
    users.find({'friends.$length': {$gt: 2} });
     

Example of usage "max" and "min" function:

 
    // create collection of clothes 
    var clothes =  new Qstore([
        {name: 'skirt', sizes: [42, 48, 50]},
        {name: 'jeans', sizes: [48, 54]},
        {name: 'skirt', sizes: [42, 45, 48]}
    ]);
    
    // select name and maxSize of each item 
    clothes.find(true, ['name', 'sizes.$max:maxSize']);
    
    // find clothes with min size = 42 
    clothes.find({'size.$min': 42});
    

Functions also can be used in fields selection

 
    // select user name and count of friends 
    users.find(true, ['name', 'friends.$length:friendsCount']);
    

The grouping methods and getList method also supports the functions syntax:

 
    // get list of all first friends 
    users.getList('friends.$first')

You can use result of one function as arguments for another function:

    
    // get first letter in lower case of first friends 
    
    users.getList({'friends.$length': {$gt: 0}}, 'friends.$first.$first.$lower')
    

You can pass additional arguments to functions:

 
// create collection of сostumes for Halloween 
costumes = new Qstore([
        {name: 'policeman', items: [ {name: 'tie', color: 'black'}, {name: 'cap', color: 'blue'}]},
        {name: 'fireman', items: [{name: 'helmet', color: 'yellow'}]},
        {name: 'solder', items: [{name: 'helmet', color: 'green'}]},
        {name: 'zombie', items: [{name: 'skin', color: 'green'}, {name: 'brain', color: 'pink'}]}
    ]);
    
// get list of colors for each costume 
costumes.find(true, ['name', 'items.$getList("color"):colors']);
 
 

When you use functions, avoid redundant expressions. For example we need to find all costumes wich have yellow color. We may use find and lenght function:

    costumes.find({'items.$find({"color": "yellow"}).$length': {$gt: 0} });

But in this case the right way - using a deep search.

    costumes.find({'items': {color: 'yellow'}});

namedescription
$lengthlength of array, string or count of keys in object
$firstfirst item of array or first letter of string or first property of object
$minretunrs max of array
$maxreturns min of array
$absabsolute value
$finduse Qstore.findIn method
$mapOfuse Qstore.mapOf method
$indexByuse Qstore.indexBy method
$testuse Qstore.test method
$getListuse Qstore.getList method
$uppertranslate string to upper case
$lowertranslate string to lower case
$toNumbercast to Number
$toStringcast to String

You can also add your functions - see addFunction method


in development


in develomnent


###Fields selection


###Grouping

returns map of keys for collection

indexes {String|Array} key or array of keys

    // create collection of users 
    var users = new Qstore ([
        {id: 12, name: 'Bob', friends: ['Mike', 'Sam']},
        {id: 4, name: 'Martin', friends: ['Bob']},
        {id: 5, name: 'Mike', friends: ['Bob', 'Martin', 'Sam']},
        {id: 10, name: 'Sam', friends: []},
        {id: 15, name: 'Sam', friends: ['Mike']}
    ]);
 
    // index by user's name 
    users.indexBy('name');
    

Result of previous example:

{
    Bob: {id: 12, name: 'Bob', friends: ['Mike', 'Sam']},
    Martin: {id: 4, name: 'Martin', friends: ['Bob']}
    Mike: {id: 5, name: 'Mike', friends: ['Bob', 'Martin', 'Sam']},
    Sam: [{id: 10, name: 'Sam', friends: []}, {id: 15, name: 'Sam', friends: ['Mike']}]
}

If for one key exixting more then one item then values will be wrapped in array.

You can also use more then one key:

 
    // first indexed by name, and then by id 
    users.indexBy(['name', 'id']);

Result of previous example:

 
{
    Bob: {
        12: {id: 12, name: 'Bob', friends: ['Mike', 'Sam']}
    Martin: {
        4: {id: 4, name: 'Martin', friends: ['Bob']}
    },
    Mike: {
        5: {id: 5, name: 'Mike', friends: ['Bob', 'Martin', 'Sam']}
    },
    Sam: {
        10: {id: 10, name: 'Sam', friends: []},
        15: {id: 15, name: 'Sam', friends: ['Mike']}
    }
}
 

You can also use static implementation of .indexBy : Qstore.indexBy (items, indexes)


same as .indexBy, but it always wrap values in array

indexes {String|Array} key or array of keys

example:

 
    // create collection of shop's locations 
    window.shops = new Qstore ({
        columns: ['country', 'city', 'address'],
        rows: [
            ['UK', 'London', 'mace st. 5'],
            ['UK', 'York', 'temple ave. 10'],
            ['France', 'Paris', 'de rivoli st. 20'],
            ['France', 'Paris', 'pelleport st. 3'],
            ['Germany', 'Dresden', 'haydn st. 2'],
            ['Germany', 'Berlin', 'bornitz st. 50'],
            ['Germany', 'Munchen', 'eva st. 12'],
            ['Russia', 'Vladivostok', 'stroiteley st. 9']
        ]
    });
    
    // first group by country, and then by city 
    shops.mapOf(['country', 'city']);
    

in previous example .mapOf returns object like this:

 
{
    France: {
        Paris: Array[2],
    Germany: {
        Berlin: Array[1],
        Dresden: Array[1],
        Munchen: Array[1]
    },
    Russia: {
        Vladivostok: Array[1]
    },
    UK: {
        London: Array[1]
        York: Array[1]
    }
}
 

You can use function which returns index:

 
    shops.mapOf(function (shop) {
        return shop.country + ' - ' + shop.city
    });
 

Reslut:

    'France - Paris': Array[2]
    'Germany - Berlin': Array[1]
    'Germany - Dresden': Array[1]
    'Germany - Munchen': Array[1]
    'Russia - Vladivostok': Array[1]
    'UK - London': Array[1]
    'UK - York': Array[1]

You can also use static implementation of .mapOf : Qstore.mapOf (items, indexes)


Powerful method for creating grouped collections. Returns Qstore.

Group by country:

    var items =  shops.search({country: ['Germany', 'France']});
    var groups = items.groupBy('country');

Result of groups.rows:

    [
        {
            _g: Array[2],
            country: "France",
            idx: 1
        },
        {
            _g: Array[3],
            country: "Germany",
            idx: 2
        }
    ]

All items of group stored in special field _g.

Group first 4 items by country and city:

    var items = shops.search(true, true, {limit: 4});
    var groups = items.groupBy(['country', 'city']);

Result of groups.rows:

    [
        {
            _g: Array[1],
            city: "London",
            country: "UK",
            idx: 1
        },
        {
            _g: Array[1],
            city: "York",
            country: "UK",
            idx: 2
        },
        {
            _g: Array[2]
            city: "Paris"
            country: "France"
            idx: 3
        }
    ]

You can also use long syntax items.groupBy({country: true, city: true}) instead items.groupBy(['country', 'city']). You can use different ways of describing the same action:

    // group by field 'country' and set alias 'countryName' for this field 
    shops.groupBy('country:countryName');
    shops.groupBy({'country:countryName': true});
    shops.groupBy({countryName: 'country'});

Group by country and when by city:

    var items = shops.search({country: ['Germany', 'France']});
    var groups = items.groupBy('country', 'city').rows

Result of groups.rows:

[
    {
        _g: [
            {
                _g: Array[2],
                city: "Paris"
            }
        ],
        country: "France",
        idx: 1
    },
    {
        _g: [
            {
                _g: Array[1],
                city: "Dresden"
            },
            {
                _g: Array[1],
                city: "Berlin"
            },
            {
                _g: Array[1],
                city: "Munchen"
            }
        ],
        country: "Germany",
        idx: 2
    }
]
 

You also can use function instead field name:

    // group contacts by first letter 
    var groups = contacts.sort('name').groupBy({
        letterfunction (contact) {
            var firstLetter = contact.name.charAt(0);
            if (firstLetter <= 'H') return 'A - H';
            if (firstLetter >= 'R') return 'R - Z';
            return 'I - Q';
        }
    });
 

Result of groups.rows:

    [
        {
            _g: Array[5],
            idx: 1,
            letter: "A - H"
        },
        {
            _g: Array[4],
            idx: 2,
            letter: "I - Q"
        },
        {
            _g: Array[2],
            idx: 3,
            letter: "R - Z"
        }
    ]

Sometimes you may need to add additional field which must be linked with group. For this case you can use special directive $add:

 
    var groups = shops.groupBy({
        country: true,
        $add: {
            shopsCount: '_g.$length'
        }
    });
 

Result of groups.rows:

    {
        _g: Array[2],
        country: "UK",
        idx: 1,
        shopsCount: 2
    },
    {
        _g: Array[2],
        country: "France",
        idx: 2,
        shopsCount: 2
    },
    {
        _g: Array[3]
        country: "Germany"
        idx: 3
        shopsCount: 3
    },
    {
        _g: Array[1]
        country: "Russia"
        idx: 4
        shopsCount: 1
    }
 

Additional fields processing when operation of grouping was done, therefore in previous example we can got count of items in group with help of $length function.

You can use function as argument for $add directive:

    // group by country and add cities list for each group 
    var groups = shops.groupBy({
        country: true,
        $addfunction (item) {
            var cities = Qstore.getList(item._g, 'city');
            return {cities: cities.join('')};
        }
    });

Result of groups.rows:

 
[
    {
        _g: Array[2],
        cities: "London, York",
        country: "UK",
        idx: 1
    },
    {
        _g: Array[2],
        cities: "Paris",
        country: "France",
        idx: 2
    },
    {
        _g: Array[3],
        cities: "Dresden, Berlin, Munchen",
        country: "Germany",
        idx: 3
    },
    {
        _g: Array[1],
        cities: "Vladivostok",
        country: "Russia",
        idx: 4
    }
]
 

Also you can use function as value for additional field, and write previous example at another manner:

    var groups = shops.groupBy({
        country: true,
        $add: {
            citiesfunction (item) {
                var cities = Qstore.getList(item._g, 'city');
                return cities.join('');
            }
        }
    });

###Data manipulation

####.add (rows [,soft=false])

add new items to collection

  • row {Object|Array}
  • soft soft add. See soft mode.

Examples:

   //add one new fruit 
   fruits.add({type: 'carrot', color: 'red', weight: 0.3, price: 0.3});
   
   //add few new fruits 
   fruits.add([
       {type: 'carrot', color: 'red', weight: 0.3, price: 0.3},
       {type: 'orange', color: 'orange', weight: 0.4, price: 0.5}
   ]);

####.update ([searchQuery,] updateQuery [,soft=false]) Update items in collection.

  • [searchQuery] {Object|Function} if option is set then will be updated only finded items
  • updateQuery {Object|Function} patch or function returned patch
  • [soft=false] soft update. See soft mode

Examples:

    //all fruits will be apples 
    fruits.update({type: 'apple'});
    
    //make all green fruits red 
    fruits.update({color: 'green'}, {color: 'red'});
    
    //The price of all pears will increase by 1 $ 
    fruits.update(function (item) {
        if (item.type == 'pear') {
            return {price: item.price + 1}
        }
    });

####.patch (values [,key='idx'] [,soft=false]) Update current collection by using update-collection.

  • values array of patches
  • [key='idx'] key field
  • [soft=false] soft patch. See soft mode.
    var patch = [
        {id: 21, connected: true},
        {id: 22, connected: false},
        {id: 33, name: 'unknown'}
    ];
    
    users.patch(patch, 'id');

####.remove (expr [,soft=false])

Delete items from collection and returns count of deleted items.

    // delete messages that do not have author 
    messages.remove({author: undefined});

####.addFields (fields) Add new fields in collection.

  • fields {Array|Object} array of new fields settings

Fields with default values:

    messages.addFields([
        {name: 'author', default: 'unknown'},
        {name: 'rating', default: 0}
    ]);
    
    messages.add({text: 'hello world'});
    messages.findOne({text: 'hello world'}); // {text: 'hello world', author: 'unknown', rating: 0} 

Computed fields:

    fruits.addFields({name: 'pricePerKg', computefunction (fruit) {
        return fruit.price / fruit.weight;
    });

Forced recalculate computed fields.
Computed fields automatically recalculeted whan collection was changed. Use this method if you need recalculate computed fields manualy.


####.removeFields (fields) remove fields from collection

  • fields {String|Array} field name or array of field names to delete
    // delete one field 
    fruits.removeFields('weight');
    
    // delete few fields 
    fruits.removeFields(['price', 'color']);

####.sort (fields [,zeroIsLast=false]) another variant: sort (fn) where fn is sort function

Sort collection.

Examples:

    // sort by idx 
    fruits.sort();
    
    // sort by type (asc) 
    fruits.sort({fieldName: 'type', order: 'asc'});
    
    // sort by type (asc) when by price (desc) 
    fruits.sort([
        {fieldName: 'type', order: 'asc'},
        {fieldName: 'price', order: 'desc'},
    ]);
    
    // sort by price, zero values will be in the end of collection 
    fruits.sort({fieldName: 'price', zeroIsLast: true});
    
    // use sort function 
    fruits.sort(function (fruit1fruit2) {
        return fruit1.price - fruit2.price;
    });
    

###Work with changes By default, your collections keep changes until you call the method commit or rollback. If you do not need this functionality, see soft mode.

####.getChanges () returns collection of changes

Examples:

    // we need get the list of idx of removed items 
    fruits.remove({type: 'apple'});
    fruits.getChanges().search({action: 'remove'}).getList('source.idx');// [1, 4, 9] 
    
    // do some changes 
    fruits.update({type: 'pear'}, {color: 'blue', price: 0.5});
    fruits.add({type: 'apple', color: 'green'});
    fruits.remove({type: 'pineapple'});
    
    // we need to create patch for database 
    var changes = fruits.getChanges();
    var patch = {};
    patch.add = changes.find({action: 'add'}, ['values:']);
    patch.remove = changes.search({action: 'remove'}).getList('source.idx');
    patch.update = changes.find(true, ['source.idx:id', 'values:']);
    

An easier way to get the map of changes - use getChangesMap method.


####.getChangesMap ([keyField='idx'])

Use this method to get map of changes group by action like:

{
    add: [array of added items]
    remove: [array of removed keys]
    update: [array of updated values]
}   

####.commit () Commit changes.


####.rollback () Revert all changes.


####softMode Some actions may be called with soft mode. This means that they do not add information about the change in the list of changes.

If you want that your collection always work in soft mode use .setSoftMode method.

    fruits.setSoftMode(true)

If you want that anyone new collection will be work in soft mode use Qstore.setSoftMode method


###Utilites

####.size () Returns rows count.


####.pack ([query] [,fields]) Returs reduced collection.

    // create fruits collection 
    var fruits = new Qstore([
        {type: 'apple', color: 'red', weight: 0.25, price: 1.5},
        {type: 'pear', color: 'green', weight: 0.4, price: 2},
        {type: 'pear', color: 'red', weight: 0.3, price: 1.8},
        {type: 'apple', color: 'yellow', weight: 0.26, price: 1.2}
    ]);
    
    var apples =  fruits.pack({type: 'apple'}, ['idx', 'weight', 'price']);
    
    //now apples contains: 
    {
        columns: ['idx', 'weight', 'price'],
        rows: [
            [1, 0.25, 1.5],
            [4, 0.26, 1.2]
        ]
    }
    

You can use this method if you whant to send collection or part of collection by network, because it will reduce the outgoing traffic.


####.getCopy () Returns a new independent collection, which will be copy of current collection.


###Events

  • change
  • commit
  • sort

Use setListener method to react on changes.


  • fn {Function} listener function

Example:

// We want to add messages to the log, if any apple will change its color. 
var listener = function (namedatacollection) {
 
    // We are interested only in the event "change" with the action "update" 
    if (name != 'change' || data.action != 'update') return;
    
    // get operations changes 
    var changes = data.changes;
    
    // find apples with changed color 
    var applePainting = changes.find({'source.type': 'apple', 'patch.color': {$ne: undefined} });
    
    // write to log 
    for (var i = 0; i < applePainting.length; i++) {
        var change = applePainting[i];
        console.log('Some apple change color from ' + change.source.color + ' to ' + change.patch.color);
    }
};
 
fruits.setListener(listener);
 
fruits.update({color: 'blue'}); // it will write to log: 
// Some apple change color from red to blue 
// Some apple change color from yellow to blue 
// Some apple change color from green to blue 
 

###Examples of collections

In many examples of the API docs using various collections. You can explore their in this section.

####fruits

 
    var fruits = new Qstore({
        columns: ['type', 'color', 'weight', 'price'],
        rows: [
            ['apple', 'red', 0.25, 1.5],
            ['pear', 'green', 0.4, 2],
            ['pear', 'red', 0.3, 1.8],
            ['apple', 'yellow', 0.26, 1.2],
            ['pineapple', 'yellow', 1, 4],
            ['banana', 'yellow', 0.3, 1.5],
            ['melon', 'yellow', 3, 3],
            ['watermelon', 'green', 10, 5],
            ['apple', 'green', 0.24, 1],
            ['strawberries', 'red', 0.1, 0.2]
        ]
    });
 

####usersMessages

 
    var usersMessages = new Qstore ({
        columns: ['text', 'subject', 'user'],
        rows: [
            ['Hi', 'new year', {id: 1, name: 'Bob', company: {name: 'IBM', phone: '+9999'} }],
            ['Happy new year!', 'new year', {id: 2, name: 'Kate', company: {name: 'Microsoft', phone: '+8888'}}],
            ['How to learn javascript?', 'programming', {id: 2, name: 'Stan'}],
            ['Anyone want to dance?', 'new year', {id: 2, name: 'James'}]
        ]
    });
 

####messages

 
    var messages = new Qstore ({
        columns: ['text', 'subject', 'user'],
        rows: [
            ['Hello world!', 'programming', {id: 1, name: 'Bob'}],
            ['Happy new year!', 'new year', {id: 2, name: 'Kate'}],
            ['How to learn javascript?', 'programming', {id: 2, name: 'Stan'}],
            ['Anyone want to dance?', 'new year', {id: 2, name: 'James'}]
        ]
    });
 
 

####diet

 
    var diet = new Qstore ({
        columns: ['month', 'breakfast', 'dinner'],
        rows: [
            ['april', {calories: 400, food: 'egg'}, {calories: 300, food: 'soup'}],
            ['may', {calories: 300, food: 'bacon'}, {calories: 500, food: 'soup'}],
            ['june', {calories: 350, food: 'porridge'}, {calories: 300, food: 'chicken'}]
        ]
    });
 

####users

 
    var users = new Qstore ([
        {id: 12, name: 'Bob', friends: ['Mike', 'Sam']},
        {id: 4, name: 'Martin', friends: ['Bob']},
        {id: 5, name: 'Mike', friends: ['Bob', 'Martin', 'Sam']},
        {id: 10, name: 'Sam', friends: []},
        {id: 15, name: 'Sam', friends: ['Mike']}
    ]);
 

####costumes

 
    var costumes = new Qstore([
        {name: 'policeman', items: [ {name: 'tie', color: 'black'}, {name: 'cap', color: 'blue'}]},
        {name: 'fireman', items: [{name: 'helmet', color: 'yellow'}]},
        {name: 'solder', items: [{name: 'helmet', color: 'green'}]},
        {name: 'zombie', items: [{name: 'skin', color: 'green'}, {name: 'brain', color: 'pink'}]}
    ]);
 

####clothes

 
    var clothes =  new Qstore([
        {name: 'skirt', sizes: [42, 48, 50]},
        {name: 'jeans', sizes: [48, 54]},
        {name: 'skirt', sizes: [42, 45, 48]}
    ]);
 

####usersChanges

 
    var usersChanges = new Qstore ({
        columns: ['source', 'patch'],
        rows: [
            [{id: 2, name: 'Bob', age: 23}, {name: 'Mike'}],
            [{id: 4, name: 'Stan', age: 30}, {age: 31}]
        ]
    });
 
 

####shops

    var shops = new Qstore ({
        columns: ['country', 'city', 'address'],
        rows: [
            ['UK', 'London', 'mace st. 5'],
            ['UK', 'York', 'temple ave. 10'],
            ['France', 'Paris', 'de rivoli st. 20'],
            ['France', 'Paris', 'pelleport st. 3'],
            ['Germany', 'Dresden', 'haydn st. 2'],
            ['Germany', 'Berlin', 'bornitz st. 50'],
            ['Germany', 'Munchen', 'eva st. 12'],
            ['Russia', 'Vladivostok', 'stroiteley st. 9']
        ]
    });
 

####contacts

 
    var contacts = new Qstore({
        columns: ['name', 'phone'],
        rows: [
            ['Leonardo Da Vinci', '23090533'],
            ['Elvis Presley', '247543'],
            ['Christopher Columbus', '85321443'],
            ['Pablo Piccaso', '2512567'],
            ['Walt Disney', '123456464'],
            ['Albert Einstein', '0865443'],
            ['Aristotle', '23090533'],
            ['William Shakespeare', '235667'],
            ['Ludwig van Beethoven', '245433'],
            ['Cleopatra', '346422'],
            ['Paul McCartney', '5532173'],
        ]
    });
 
 

####meetings

 
    var meetings = new Qstore({
        columns: ['day','month', 'year', 'details'],
        rows: [
            [2, 'feb', 2012, 'Meeting with Albert Einstein'],
            [14, 'feb', 2012,'Meeting with Elvis Presley'],
            [20, 'feb', 2013, 'Meeting with Christopher Columbus'],
            [3, 'mar', 2013, 'Meeting with Pablo Piccaso'],
            [2, 'apr', 2013, 'Meeting with Walt Disney'],
            [10, 'apr', 2013,'Meeting with Aristotle'],
            [11, 'may', 2013, 'Meeting with William Shakespeare'],
            [13, 'may', 2013, 'Meeting with Cleopatra']
        ]
    });