# Racer's Model

***StartupJS*** takes advantage of ***Racer's*** capabilities to provide reactivity. [The Racer repository](https://github.com/derbyjs/racer) provides this module definition:

***Racer*** is a real-time model synchronization mechanism for ***Node.js***. With ***ShareDB***, multiple users can interact with the same data in real time using Operational Transformation, a complex conflict-resolving algorithm that works in real time and with offline clients. ShareDB also supports PubSub on multiple servers for horizontal scaling. Clients can subscribe and fetch data in terms of queries and specific documents, so different clients can subscribe to different overlapping datasets. Apart from this complex backend, Racer provides a simple model and event interface for writing application logic.

## The main advantages are:

-	***Real-Time Updates*** – model methods automatically propagate changes to browser clients and Node servers in real time. The racer-browserchannel adapter is recommended for browser connection in real time.
-	***Real Time Query Subscriptions*** - clients can subscribe to a limited set of information related to the current session. Both document subscriptions and real-time queries are supported. Mongo ad-hoc queries are currently supported as well.
-	***Conflict Resolution*** - using ShareDB's JSON Operational Transformation algorithm, Racer will generate events that bring conflicting client states into a complete coherence. In addition to the synchronous API, model methods have callbacks to handle the resolved state after the server responds.
-	***Immediate interaction*** - model methods come into effect immediately. Meanwhile, Racer sends updates to the server and checks for conflicts. If the updates are successful, they are saved and broadcast to other clients.
-	***Offline Mode*** - because the model methods are applied immediately, clients continue working offline. Any changes to the local client or global state are automatically synchronized on reconnection.
-	***Single server and client interface*** - the same model interface can be used on the server for initial page rendering and on the client interface for interaction with user. Racer supports combining models, created on the server, as well as reinitializing them in the same state in the browser.
-	***Persistent Storage*** - Racer uses ShareDB for logging all data operations, publishing operations on multiple external servers and automatic document saving. It currently supports MongoDB and can be easily adjusted to support other document storages.

You have already worked with model in the chapter on creating the Todo List app. Model is a core concept in Startupjs. With its help, we can perform any of the CRUD operations very easily. The startupjs for racer implements [orm](https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping) (Object-Relational Mapping) to associate the database with the code. In simple terms, ORM is a system that provides functionality for working with a database in the form of object methods. That is, using ORM you have an object responsible for a specific document or collection.

## In simple words

Imagine the structure of MongoDB. There are collections, each collection contains documents, each document has fields with some values.

Let's look at this as a city. A city is a database, a block is a collection, houses are documents, and residents of houses are fields. To understand how the database works, let's imagine a postman. How does he find the person who needs to receive the letter? He has an address where this person lives. For example, Johnny Stone lives at 10, Independence Block. Let's transform this into an object-oriented view: 'Independence.10'. This is his address and you can find him at it.

Now let's try to understand what a model is. There will be a bit of abstraction. Conceive an imaginary mark that you can put on the desired object in the city. For example, you want to tag a block or a specific house in a block, or even a specific person. With this tag you can quickly find the object you want. The model plays a role of such a label. To put such a label is called "subscription".

Now when we have a model of an object, we can interact with it. The type of interaction depends on the type of this object. For example, you cannot add a new house to the human model, this is absurd. But you can add it to a block.

Now let's look at this from a programming point of view. A model is an object that has methods for interacting with the entity it refers to. Methods that modify the document are called setters (from to set), methods that return values are called getters (from to get). All available [setters](https://derbyjs.com/docs/derby-0.10/models/setters) and [getters](https://derbyjs.com/docs/derby-0.10/models/getters) are in the Racer documentation. The important point is that the collection in racer is an object, so the methods of the object are available to it.

There is one important exception.

If you used aggregation in `model.query` or queried `{$count: true}`, then in order to get the `value`, you need to use `model.getExtra`, not the usual model.get.

In Startupjs, the model should be named starting with the `$` symbol. For example, `$todos`, `$todo`, etc.

## Algorithm for obtaining a model

In this example, we are referring to `todos`, which basically means we are referring to the `todos` collection in `mongodb`.

1. Take the selected scope for the model (called scope).

```js
  const $todos = model.scope('todos')
```

2. Now you need to subscribe, note that this subscription returns a Promise. (At the current step, it is important to understand that we just got a pointer to what we need. That is, there is no data from `mongodb` yet. But this is a necessary action, for example, to get data using `get()`)

```js
  await $todos.subscribe()
  // or you can use
  await model.subscribe($todos)
```

3. After subscribing, you can perform the necessary interactions with the model.

```js
  // Add new document to collection
  $todos.add({
    title: 'understand the third step of the algorithm',
    completed: false
  })

  // Get all data from collection
  $todos.get()
```

4. After all the actions are performed, you need to unsubscribe the model.

```js
  $todos.unsubscribe()
```

## Collection model

Let's take a look at some basic examples. Startupjs uses MongoDB as its database. Therefore, all examples use the syntax of this database.

```jsx
const [todos, $todos] = useQuery('todos', {})

<Content padding>
  <H1>TODOList</H1>
    {todos.map(todo => (
      <Card key={todo.id}>
        <Row align='between' vAlign='center'>
          <Span>{todo.title}</Span>
            <Row align='between' vAlign='center'>
              <Checkbox
                value={todo.completed}
                onChange={value => changeStatus(todo.id, value)}
              />
              <Button
                icon={faTimes}
                iconColor='error'
                size='s'
                onPress={() => removeTodo(todo.id)}
              />
            </Row>
        </Row>
      </Card>
    ))}
    <Row align='between' vAlign='center'>
      <TextInput />
      <Button>Add</Button>
    </Row>
</Content>
```

An example you already know. Here `$todos` is the model of the `todos` collection, since `useQuery` returns the queried collection model as its second value. For example, to add a document to a collection, use the `add` method.

```jsx
$todos.add({
  title: 'New todo',
  completed: false
})
```

There is no need to specify id, since we are working with MongoDB, which will do it for us. This tells the `$todos` model to add a new document with the specified fields.

## Document Model

Now let's recall the `useDoc` hook. It allows you to get a document by `id`. Let's say we have a `Todo` component that accepts an `id` of a specific task.

```jsx
function Todo({id}) {
  const [todo, $todo] = useDoc('todos', id)

  return(
    <Card key={todo.id}>
      <Row align='between' vAlign='center'>
        <Span>{todo.title}</Span>
          <Row align='between' vAlign='center'>
            <Checkbox
              value={todo.completed}
              onChange={value => changeStatus(todo.id, value)}
            />
            <Button
              icon={faTimes}
              iconColor='error'
              size='s'
              onPress={() => removeTodo(todo.id)}
            />
          </Row>
      </Row>
    </Card>
  )
}
```

Here `$todo` is a model for a specific document of the `todos` collection. You can use it to modify this document. For example, alter a specific field.

```js
  $todo.set('title', 'New title')
```

You can add a new field to your document. `setEach` will add each object field to the document, and if such a field already exists, then it will be overwritten with a new value.

```js
  $todo.setEach({ newField: 'NewField' })
```

Imagine this method iterates over each field of the passed object and executes a `set` method, where the first parameter is the element's key and the second is the element's value. I.e:

```js
  const newFields = { newField: 'NewField' }
  $todo.setEach(newFilds)
  // The same as:
  for (let key in newFields) {
    $todo.set(key, newFilds[key])
  }
```

Or to remove it:

```js
  $todo.del()
```

## Let's practice (model)

1. **DO NOT USE CUSTOM HOOKS IN THIS TASK!** We need to use the `model` from startupjs, which grants us access to the global model. You have a variable `userId` that contains the user `id` from the users collection. Subscribe to it, fill in the `name` and `age` fields, and then add a new field `hobby=football`.

  ```jsx
    const [user, setUser] = useState()
    const userId = '123123123'

    function getUser() {
      const $user = model.at('users.'+userId)
      await $user.subscribe()
      const user = $user.get()
      console.log({...user})
      $user.set('hobby', 'football')
      $user.unsubscribe()
      setUser(user)
    }

    return pug`
      Div
        Button(onPress=getUser) Get User
        Div
          Span= user.name
          Span= user.age
    `
  ```

1. There is a `users` collection. Implement the `addUser`, `removeUser` functions.

  ```jsx
    const [name, setName] = useState('')
    const [age, setAge] = useState('')
    const [users, $users] = useQuery('users', {})

    function addUser() {
      // write here
    }

    function removeUser() {
      // write here
    }

    return pug`
      Div
        each user in users
          Div
          Span= user.name
          Span= user.age
          Button(onPress=removeUser) Remove user
        Div
          TextInput(value=name label='name' onChangeText=setName)
          TextInput(value=age label='age' onChangeText=setAge)
          Button(onPress=addUser) Add user
    `
  ```

1. Now let's implement a simple counter example to practice the number-related model methods. Write functions addCounter, reduceCounter. Since this is the 3rd task, let's also implement the addAsync function, which will increment the counter by 1 in 2 seconds after clicking.

  ```jsx
    const [counter, $counter] = useValue()

    function addCounter() {
      // write here
    }

    function reduceCounter() {
      // write here
    }

    function addAsync() {
      // write here
    }

    return pug`
      Div
        Span= counter
        Row
          Button(onPress=addCounter) Add
          Button(onPress=reduceCounter) Reduce
          Button(onPress=addUser) Add async
    `
  ```
