markdown Knex.js加入

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown Knex.js加入相关的知识,希望对你有一定的参考价值。

## Adding Knex to the endpoints

Now that you have a good understanding of the Knex's capabilities, let's look at adding Knex to our Express app endpoints. Quit the `nodemon drills.js` process and start the server.

Open Postman and navigate to `http://localhost:8080/restaurants`. You should get back a list of restaurants. The SELECT statement is straight forward, the only thing to point out is the addition of   in the `.then(results => res.json(results) );

### Inner Join

Let's tackle the `grade` first.

Recall INNER JOIN. If we join the restaurants and grades tables then sort the results by date. We get the list of results where to first row is the results we are looking for. Then `LIMIT` the results to 1 row and we have our output for a single restaurant.

```sql
SELECT restaurants.id, name cuisine, borough,
grades.id, grade, date as "inspectionDate", score
    FROM restaurants
    INNER JOIN grades ON grades.restaurant_id = restaurants.id
    WHERE restaurants.id = 1
    ORDER BY date DESC
    LIMIT 1;
```

Next, let's update the Knex query to accomplish the same inner join.

```js
knex.select('restaurants.id', 'name', 'cuisine', 'borough', 'grades.id', 'grade', 'date as inspectionDate', 'score')
    .from('restaurants')
    .where('restaurants.id', 1)
    .innerJoin('grades', 'restaurants.id', 'grades.restaurant_id')    
    .orderBy('date', 'desc')
    .limit(1)
    .then(results => res.json(results));
```

Finally add a new endpoint to retrieve and return a specific restaurant by ID. 

```js
.get('/restaurants/:id', (req, res) => {
    // Add query and response here...
});
```

Be sure to swap out the hard-coded id for the [req.params.id](https://expressjs.com/en/4x/api.html#req.params).

#### Concat

Now let's tackle the `address`. How do we combine the values from multiple columns in SQL/Knex?

In SQL you can combine multiple values into a single results using the `CONCAT()` function. In `psql` or your GUI tool run the following command.

```sql
SELECT CONCAT(address_building_number, ' ', address_street, ' ', address_zipcode ) as address
    FROM restaurants 
    WHERE borough = 'Manhattan';
```

Unfortunately, Knex does now support a similar syntax. It does, however, support a raw SQL using the [`knex.raw()`](http://knexjs.org/#Raw) method. Please note, `knex.raw()` passes the query as-is which can open you up to SQL injection attacks. Do not use this approach for user-entered values until to your understand SQL-injection attacks and Knex Raw Parameter Bindings.

### Try It!

Update the knex query in the .get endpoint with the following

``` js
knex.select('id', 'name', 'cuisine', 'borough')
    .select(knex.raw("CONCAT(address_building_number, ' ', address_street, ' ', address_zipcode ) as address"))
    .from('restaurants')
    .limit(10)
    .then(results => res.json(results));
```

#### Putting it all together

```js
knex.first('restaurants.id', 'name', 'cuisine', 'borough', 'grades.id', 'grade', 'date as inspectionDate', 'score')
    .select(knex.raw("CONCAT(address_building_number, ' ', address_street, ' ', address_zipcode ) as address"))
    .from('restaurants')
    .where('restaurants.id', 1)
    .innerJoin('grades', 'restaurants.id', 'grades.restaurant_id')    
    .orderBy('date', 'desc')
    .then(results => res.json(results));
```

For more information on the `.first()` methods check out the [documentation](http://knexjs.org/#Builder-first)

### Hydrate/Dehydrate

SQL query results are formatted in a flat tabular (table-like) form, but most web applications expect data to be hierarchical, known as an Object Graph. So data must be converted. This process is commonly known as hydrating and dehydrating.

### Manual Hydrate

The process of converting the tabular data to an object graph is (conceptually) straight forward. Simply create an empty object, loop over the rows creating a new property for each item or appending (pushing) data if the property already exists. In practice, however, it can be quite tricky.

Let's take a look at a simple example. Imagine we have a table of people and each person has zero or more pets. This is a one-to-many relationship. The following knex query might produce the JSON output below

Sample Knex query 
```js 
knex.select('id', 'name', 'age', 'pets.name as petName', 'pets.type as petType')
    .from('people')
    .innerJoin('pets', 'people.id', 'pets.people_id')    
    .then(results => res.json(results));
```

People and Pets output. Notice how the people are repeated for each pet.
```js
[
    {
        id: 2,
        name: 'John Doe',
        age: 34,
        petName: 'Rex',
        petType: 'dog',
    },
    {
        id: 2,
        name: 'John Doe',
        age: 34,
        petName: 'Rex',
        petType: 'dog',
    },
    {
        id: 3,
        name: 'Mary Jane',
        age: 19,
        petName: 'Mittens',
        petType: 'kitten',
    },
    {
        id: 3,
        name: 'Mary Jane',
        age: 19,
        petName: 'Fluffy',
        petType: 'cat'
    }
];
```

The desired output is an array of people objects. Each person has an array of pets.

```json
[
   {
    "2": {
        "id": 2,
        "name": "John Doe",
        "age": 34,
        "pets": [
            {
                "name": "Rex",
                "type": "dog"
            },
            {
                "name": "Rex",
                "type": "dog"
            }
        ]
    },
    "3": {
        "id": 3,
        "name": "Mary Jane",
        "age": 19,
        "pets": [
            {
                "name": "Mittens",
                "type": "kitten"
            },
            {
                "name": "Fluffy",
                "type": "cat"
            }
        ]
    }
}
]
```
To hydrate the tabular data::
- start with an empty object.
- loop over each row
  - if the 'id' property does not exist, then create it and populate it with people data
  - Then push on the pet info

```js
const hydrated = {};
people.forEach(row => {
    if ( !(row.id in hydrated) ) {
        hydrated[row.id] = {
            id: row.id,
            name: row.name,
            age: row.age,
            pets: []
        }
    }
    hydrated[row.id].pets.push({
        name: row.petName,
        type: row.petType,
    });
});
console.log(hydrated);
```

Copy the data above and method above to the `drills.js` and work thru the algorithm make sure you understand the process.

### Try it

Next, try to hydrate the tablular data results from the following restaurants query 

```js
knex.select('restaurants.id', 'name', 'cuisine', 'borough', 'grades.id as gradeId', 'grade', 'score')
    .from('restaurants')
    .innerJoin('grades', 'restaurants.id', 'grades.restaurant_id')
    .orderBy('date', 'desc')
    .limit(10)
    .then(results => console.log(results));
```

The goal is to create an array of restaurant objects that look like this:

```json
{
    "1": {
        "name": "Dj Reynolds Pub And Restaurant",
        "cuisine": "Irish",
        "borough": "Manhattan",
        "grades": [
            {
                "gradeId": 1,
                "grade": "A",
                "score": 2
            },
            {
                "gradeId": 3,
                "grade": "A",
                "score": 11
            },
            {
                "gradeId": 5,
                "grade": "A",
                "score": 12
            },
            {
                "gradeId": 7,
                "grade": "A",
                "score": 12
            }
        ]
    }
}
```

### Next Challenge

Implement the same process using [Treeize](https://github.com/kwhitley/treeize). You can find the documentation [here](https://github.com/kwhitley/treeize)

### Last Challenge

Create a `.post('/restaurants/)` which accepts a single restaurant with multiple grades, like the object below. The endpoint will need to create (insert) the restaurant and related grades. Remember to return the correct status code, see [Creating POST endpoints](https://courses.thinkful.com/node-001v5/project/1.4.3). And provide the path to the new document in the [location header](https://expressjs.com/en/4x/api.html#res.location).

```json
{
    "name": "Terra Byte Cafe",
    "cuisine": "American",
    "borough": "Brooklyn",
    "grades": [
       {
           "grade": "C",
           "score": 42
       },
       {
           "grade": "B",
           "score": 72
       },
       {
           "grade": "D",
           "score": 35
       },
       {
           "grade": "A",
           "score": 95
       }
    ]
}

```

> Hint: Is the `req.body` property undefined? Did you install and require `body-parser` and use `bodyParser.json()`?


### Example Solutions
Solutions can be found in the "solutions" branch of the [node-restaurants-app-knex](https://github.com/cklanac/node-restaurants-app-knex/tree/solutions) start repo.

以上是关于markdown Knex.js加入的主要内容,如果未能解决你的问题,请参考以下文章

Knex.js:加入 'select' 和 'where' 子句

带有 Express 的 Knex.js,如何在 knex.commit 后跟 knex.select 查询?

Knex.js 迁移问题:因`关系“knex_migrations”不存在而失败`

使用 Knex.js 进行多行插入

Knex.js 会阻止 sql 注入吗?

使用 Knex.js 和 PostgreSQL 设置 Docker