stores

Lance Pollard edited this page Sep 25, 2012 · 4 revisions
Clone this wiki locally

Finders

  • Store#find
  • Store#findOne
  • Store#count
  • Store#exists

Store#find

Returns an array of models. The database is free to perform more fine-grained optimizations, such as making a collection.findOne call in mongodb if there's only one id we're searching by.

store.find { "id" : { "$in": [1, 2, 3] } }
store.find { "id" : { "$nin": [1, 2, 3] } }
store.find { "tags" : { "$all": ["ruby", "javascript"] } }
store.find { "tags" : { "$in": ["ruby", "javascript"] } }
store.find { "$or": [ { "tags" : { "$in": ["ruby", "javascript"] } }, { "id" : { "$in": [1, 2, 3] } } ] }

Persistence

Each Tower.Store method requires exact parameters (there's no argument overloading).

The store is used by the Tower.Model internally.

  • Store#create
  • Store#update
  • Store#destroy

Store#create

Creates one model.

store.create { "tags" : ["ruby", "javascript"] }

Store#update

Updates any models matching the query.

store.update { "$set": { "tags" : ["ruby", "javascript"] } }, { "id" : { "$in": [1, 2, 3] } }

Store#destroy

Deletes any models matching the query.

store.destroy { "id" : { "$in": [1, 2, 3] } }

Querying

Operators

$eq

store.find { "status" : "active" }
store.find { "status" : "$eq" : "active" }

$ne

store.find { "status" : "$ne" : "inactive" }

<, <=, >, >=

store.find { "likeCount" : ">=" : 10 }
store.find { "likeCount" : "$gte" : 10 }

$all

store.find { "tags" : { "$all" : ["ruby", "javascript"] } }

$in

store.find { "tags" : { "$in" : ["ruby", "javascript"] } }

$nin

store.find { "tags" : { "$nin" : ["java", "asp"] } }

$match

store.find { "name" : /acme.*corp/i }

$notMatch

store.find { "name" : /acme.*corp/i }

$or

store.find { "$or": [ { "likeCount" : 1000 }, { "likeCount" : { "$gte": 1, "$lte": 100 } } ] }

$nor

store.find { "$nor": [ { "likeCount" : 1000 }, { "likeCount" : { "$gte": 1, "$lte": 100 } } ] }

$and

store.find { "$and" : [ { "a" : 1 }, { "a" : { $gt: 5 } } ] }

Sorting

sort

store.find { "tags" : { "$in" : ["ruby", "javascript"] }, "sort": [["title", "asc"]] }

Paginating

limit

store.find { "tags" : { "$in" : ["ruby", "javascript"] }, "limit": 20 }

offset

store.find { "tags" : { "$in" : ["ruby", "javascript"] }, "offset": 10 }

Selecting Specific Fields

fields

Memory Store

The memory story stores all the records in a JavaScript object. It can perform all the same advanced queries as Tower.Store.MongoDB, making it really easy to reuse server-side model code on the client.

MongoDB Store

(todo) Neo4j Store

This is the Tower adapter to Neo4j, an awesome graph database. Neo4j has several features that the other databases don't have.

Database Transactions

Tower models have something like database transactions.

On the client, there is one global transaction. By default, this transaction commits every time you create/save/update/destroy a record. You can set Tower.Store.Transaction.commitInterval = n to some n millisecond value, and it will make sure the transaction is committed every n milliseconds. If it's 0, it will commit automatically. You can also set it to null and it will never commit, you have to manually call Tower.Store.instance().commit().

On the server, there is no global transaction; that would create a huge memory management problem. Instead, every create/save/update/destroy creates its own transaction and automatically commits. If you want group multiple operations into a single transaction, you can do that like this:

class App.TransactionsController extends Tower.Controller
  create: ->
    Tower.Store.transaction (transaction) =>
      user = App.User.with(transaction).create()
      post = App.Post.with(transaction).create()
      transaction.commit (error) =>
        if error
          @render json: success: "All operations saved"
        else
          @render json: failure: error.message

The model instances themselves will always show their new/updated attribute values, but they won't necessarily get their id immediately due to the async database operations.

Creating Records

App.User.where(firstName: "Lance").create()
  cursor.create() # pass in transaction if cursor had transaction
    record.save()
      transaction.create()
        if transaction.autocommit
          store.create()

user = new App.User

user.save()
  transaction.create()
    store.create()

App.User.create({}, {}, {})
  cursor.create()
    for record in records
      record.save()
        transaction.create()
          store.create()

App.User.transaction().create({}, {}, {})
  cursor.create()
    for record in records
      record.save()
        transaction.create()
    transaction.commit()
      store.create()

Finding Records (bindable scopes)

scope.all returns a cursor, so if you want the records you have to add a callback scope.all (error, records). This makes it so you can do both/or: get the records or set up a binding to that record set.

# this returns a cursor that acts like an array of records.
App.usersByFirstName = App.User.asc("firstName").limit(20).all()
# this returns a cursor that acts as an individual record.
# this way you can have something like a "featured post"
# that will automatically update. Maybe we don't want to do this.
App.topPost = App.Post.desc("likes").first()
<ul>
{{#each App.usersByFirstName}}
  <li>{{fullName}}</li>
{{/each}}
</ul>

Ideally you'd be able to not have to call all, so you could do this:

class App.User extends Tower.Model
  @field "firstName"

  @scope "byFirstName", @asc("firstName").limit(20)
<ul>
{{#each App.User.byFirstName}}
  <li>{{fullName}}</li>
{{/each}}
</ul>

List of "checkins within the last minute"

Since the >= is a Date, Tower will generate a callback which will update the query with the latest records. By default it will update it every 5 seconds, but you can change this.

App.latestCheckins = App.Checkin.where(createdAt: ">=": _(1).minute().ago()).all()

App.Checkin.create()
# wait 0:50
App.Checkin.create()
# wait 0:11, 1 checkin disappears.
<ul>
{{#each App.lastCheckins}}
  <li>{{user.name}} checked into {{place.name}} at {{createdAtAgo}}</li>
{{/each}}
</ul>
  • If Date, refresh every x interval.

List of "closest"

currentLocation   = [40.741404,  -73.988135]
App.closestDeals  = App.Deal.near(currentLocation).within(1, "mile").all()
<ul>
{{#each App.closestDeals}}
  <li>{{description}}</li>
{{/each}}
</ul>

That currentLocation will be a bindable array, which when updated will update the scope, so the deals always reflect the ones closest the current location!

  • If location, refresh if location changes
  • If array, refresh if array changes.

CoffeeKup to generate handlebar templates

Potential idea!

ul ->
  each "App.closestDeals", ->
    li "{{description}}"

"Transports"

Ajax Transport

Client-side RESTful Batch-Processing

Resources