Skip to content

Latest commit

 

History

History
363 lines (251 loc) · 9.35 KB

05.3.vefthjonustur.md

File metadata and controls

363 lines (251 loc) · 9.35 KB
title
Fyrirlestur 5.3 — Vefþjónustur

Fyrirlestur 5.3 — Vefþjónustur

Vefforritun 2 — HBV403G

Ólafur Sverrir Kjartansson, osk@hi.is


Hönnun á vefþjónustum

  • Töluvert ólíkt því að hanna vefi með útliti
  • Neytendur okkar eru aðrir forritarar og þeirra forrit
    • Mun minna rými til að túlka eitthvað eins og villu á vef
    • Getum sparað öðrum ótrúlega mikinn tíma með því að vanda okkur

  • Þurfum að hugsa vel um samræmi
    • Samræmi á heitum (ekki username, userName og user-name)
    • Samræmi á URI (ekki /get-users, /products og /cats)
    • Samræmi á villuskilaboðum

  • Hugsum heildstætt, hvað gerist í hverju tilfelli?
    • Hvað ef beðið er um eitthvað sem er ekki til
    • Hvað ef villa kemur upp
    • o.s.fr.
  • Oft gott að aðskilja virknina okkar frá vefþjónustulaginu
    • Vefþjónustan kemur sem „þunnt lag“ ofan á virkni

Dæmi


Dýnamískar fyrirspurnir

  • Stundum þurfum við að grípa til þess að búa til fyrirspurnir með strengjameðhöndlun 🙈
  • Ekki allt getur farið í prepared statement, t.d. ef við viljum dýnamískt breyta ORDER BY
  • Eða gera PATCH köll
  • VERÐUM AÐ FARA VARLEGA, svona kóði er einstaklega viðkvæmur fyrir SQL injection

async function list(req, res) {
  const asc = req.query.order === 'ASC';

  // röðum e. id í ascending (ASC) eða
  // descending (DESC) röð eftir því
  // hvort boolean gildi sé satt eða ekki
  // Notum ekkert frá notanda í dýnamískrí
  // fyrirsp. GETUM AÐEINS HAFT TVÖ GILDI!
  const order = asc ? 'id ASC' : 'id DESC';

  const q = `
    SELECT * FROM items ORDER BY ${order}`;

  const result = await query(q);
}

Færslur búnar til

  • Getum notað RETURNING syntax í postgres til að fá til baka færslu sem búin var til
  • Þurfum ekki aðra fyrirspurn til að fletta upp reitum eins og id eða created
  • INSERT INTO items (text) VALUES ('foo') RETURNING id, text, created;

Færslum eytt

  • Getum reynt að eyða færslu sem er ekki til
    • Þurfum samt að aðskilja á milli færslu sem var til og eytt
    • og færslu sem var ekki til...
  • Þegar átt var við raðir mun rowCount skila fjölda raða sem átt var við

async function deleteRow(id) {
  const q = `
    DELETE FROM todos WHERE id = $1`;

  const result = await query(q, [id]);

  // true ef færslu eytt, annars false
  return result.rowCount === 1;
}

Paging

  • Þegar við erum að vinna með mikið af gögnum þurfum við oft á tíðum að takmarka hversu miklu er skilað
  • Ekki vænlegt að skila öllum miljón færslum til notanda
  • Yfirleitt útfært með því að skila síðum

Síður

  • Síður takmarkast af fjölda færslna per síðu (limit) og hve mörgum við sleppum (offset)
    • limit=10, offset=10 birtir færslur 11-20
  • Einnig hægt að einfalda í page, höfum þá skilgreindan fjölda per síðu og page er margföldun á honum
    • page=1 eru færslur 1-10, page=4 eru færslur 31-40

Upplýsingar um síðu

  • Getum skilað upplýsingum um síðu í svari eða header
  • RFC 5988 skilgreinir web linking og hvernig nota megi í hausum
    • Link: <http://api.example.com/?page=1>; rel="previous", <http://api.example.com/?page=3>; rel="next",

  • Getur verið einfaldara að skila í svari, hægt að nota _link sem er skilgreint í HAL

{
  "_links": {
    "self": {
      "href": "http://api.example.com/?page=2"
    },
    "previous": {
      "href": "http://api.example.com/?page=1"
    },
    "next": {
      "href": "http://api.example.com/?page=3"
    }
  },
  "items": []
}

Síður og postgres

  • Getum sent offset og limit í postgres query
  • SELECT * FROM foo OFFSET 0 LIMIT 10
    • Sendum sem parameterized gildi

Dæmi


Leit

  • Með stórum gagnasettum fylgir of einnig þörf til að geta leitað í þeim
  • Nokkrar leiðir mögulegar

Table scan

  • Fyrsta lausn sem manni gæti dottið í hug væri að nota LIKE leit í gagnagrunni
  • Veljum dálka til að leita í og setjum leitarskilyrði inn með %
    • ... WHERE description LIKE '%foo%'
  • Afskaplega slæm lausn þar sem fyrir hvert skilyrði þarf að skoða alla dálka í töflu

Graf sem sýnir hvernig allt sé hratt fyrir lítið n


Index

  • Getum þá útbúið index fyrir töflu, veljum einhverja dálka og geymum þá sérstaklega
  • Til sérstök lausn á þessu með full-text index, allur texti settur í index og leitað sérstaklega í honum
    • Getur þurft sérstaka uppsetningu í gagnagrunni

Leitarþjónusta

  • Önnur algeng lausn er algjörlega aðskilinn leitarþjónusta
  • Tengjum gögnin í okkar gagnagrunn við leitarþjónustu, t.d. með reglulegum keyrslum eða triggers
  • Bíður upp á sérhæfða leit með setningarfræði og álíka
  • T.d. elasticsearch eða algolia

Leit í postgres


  • Skilgreinum í hvaða dálk við leitum og eftir hverju við leitum
    • to_tsvector og to_tsquery
  • to_tsquery getur tekið við breytum og syntax, getum lent í að fá villur
    • plainto_tsquery leitar bara

SELECT title
FROM pgweb
WHERE
  to_tsvector('english', body)
  @@
  to_tsquery('english', 'friend');

Dæmi


Gagnamódel

  • Þegar við setjum upp töflur í gagnagrunn þurfum við að hugsa um hvaða verkefni við erum að leysa
  • Hvernig gagnamódelið (e. data model) okkar lítur út
    • Erum að útbúa einfaldaða birtingarmynd af raunveruleikanum
  • Þurfum að útbúa tengingar á milli taflna

One-to-one

  • One-to-one tengingar eiga við þegar eining á tengingu við nákvæmlega eina aðra einingu
  • Sú eining á tengingu til baka í aðeins þá einingu
  • T.d. höfuðborg ↔ land, íslensk kennitala ↔ íslenskur ríkisborgari

One-to-many

  • One-to-many á við þegar eining á tengingu við nákvæmlega eina aðra einingu
  • Sú eining getur átt tengingar við margar einingar
  • T.d. bók á margar blaðsíður, en blaðsíðurnar eiga aðeins eina bók
  • Einnig hægt að snúa við og módela sem many-to-one

  • Í gagnagrunni notum við vísun úr einni töflu í aðra til að módela þessar tenginar
  • Þó getur one-to-one verið „brotið“ á þennan máta þar sem við getum myndað one-to-many tengingu
    • Oft eru gögnin hreinlega saman í einni töflu, t.d. nafn og kennitala

  • Við tryggjum tenginguna með því að skilgreina foreign key á milli taflanna
  • Getum ekki bætt við færslu ef vísun er ekki til
  • Getum ekki hent færslu ef önnur vísar í hana
  • Mikill kostur og tryggir öryggi gagnanna okkar

CREATE TABLE categories (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255)
);
CREATE TABLE books (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255),
  category INTEGER NOT NULL,
  CONSTRAINT category FOREIGN KEY (category)
    REFERENCES categories (id)
);

Many-to-many

  • Many-to-many á við þegar eining á tenginar í margar aðrar einingar
  • Þær einingar eiga einnig tengingar í margar aðrar einingar
  • T.d. nemandi getur verið skráður í margar áfanga, áfangar hafa marga nemendur

  • Many-to-many tenginar þurfum við að skilgreina í sérstökum tengitöflum (e. join table)
  • Hver færsla í tengitöflu tengir tvær raðir í öðrum töflum
  • Getum hugsanlega bætt við auka lýsigögnum um sambandið

CREATE TABLE users_books (
  id SERIAL PRIMARY KEY,
  "user" INTEGER NOT NULL,
  book INTEGER NOT NULL,
  rating INTEGER, -- Lýsigögn um sambandið!
  CONSTRAINT book FOREIGN KEY (book)
    REFERENCES books (id),
  CONSTRAINT "user" FOREIGN KEY ("user")
    REFERENCES users (id)
);

join

  • Þegar við viljum fá gögn úr þessum tengdu töflum þurfum við að nota join
  • Sameinar sett úr fleiri en einni töflu (eða úr sömu töflu) miðað við ákveðin skilyrði
    • „Gefðu mér id og nafn á bókum úr books og þann flokk sem hún tilheyrir í categories

SELECT
  books.id AS id, books.name AS name,
  categories.name AS category
FROM
  books
JOIN
  -- Taflan sem við viljum tengja
  categories ON
    -- Skilyrðin á tengingu
    books.category = categories.id

  • Verðum að vísa í dálka per töflu (books.id) þar sem töflurnar geta átt dálka með sama heiti
  • AS leyfir okkur að gefa nýtt nafn í niðurstöðum
  • JOIN geta orðið flóknari (left, right, cross), förum ekki nánar í það hér

n+1 fyrirspurnir

  • Þegar við erum að vinna með tengd gögn getum við dottið í það „anti-pattern“ að framkvæma n+1 fyrirspurnir
  • 1 fyrirspurn fyrir lista af n færslum, n fyrirspurnir til að fá gögn um hverja færslu
  • Ættum alltaf að getað notað join í staðinn, eða módelað gögnin þannig að það þurfi ekki

Dæmi