Radosław Miernik
Open source? Embrace, understand, develop.
A co gdyby użytkownik był funkcją…?
@radekmie
Tak, tylko biblioteką.
Model | Kontroler | Widok |
---|---|---|
Dane | Logika interfejsu | Szablony |
Osobno?
<Application>
<Navigation>
<Logo />
</Navigation>
<Sidebar>
<Profile>
<Avatar of={42} />
<Bio text={bio} />
</Profile>
<Friends of={42} />
<Options of={42} />
</Sidebar>
<Timeline of={42} />
</Application>
<div class="application">
<div class="navigation">
<div class="logo">
<img src="./logo.png" alt="logo" />
</div>
</div>
<div class="sidebar">
<div class="profile">
<div class="avatar">
<img src="./avatars/42.png" alt="avatar" />
</div>
<p class="bio">
I'm a secret donut lover.
</p>
</div>
<div class="friends">...</div>
<div class="options">...</div>
</div>
<div class="timeline">...</div>
</div>
const Timeline = props => {
const events = getEvents(props.of).map(event =>
<TimelineEvent key={event.id} event={event} />
);
return (
<div className="timeline">
{events}
</div>
);
}
const Timeline = props => {
const events = getEvents(props.of).map(event =>
<TimelineEvent key={event.id} event={event} />
);
return (
<div className="timeline">
{events}
</div>
);
}
const TimelineEvent = props =>
<div className="event">
<h1>
{props.event.title}
</h1>
<Avatar of={props.event.author} />
<Schedule at={props.event.date} />
<p>
{props.event.description}
</p>
</div>
;
const Timeline = props => {
const events = getEvents(props.of).map(event =>
React.createElement(TimelineEvent, {key: event.id, event: event})
);
return (
React.createElement('div', {className: 'timeline'},
events
)
);
}
const TimelineEvent = props =>
React.createElement('div', {className: 'event'},
React.createElement('h1', null,
props.event.title
),
React.createElement(Avatar, {of: props.event.author}),
React.createElement(Schedule, {at: props.event.date}),
React.createElement('p', null,
props.event.description
)
)
;
To, co zwracają nasze komponenty, to nie fragmenty DOM, tylko opis interfejsu.
React zajmuje tylko się urzeczywistnieniem tego opisu.
Komponent rodzic przekazuje dziecku
jakieś informacje (ang. props).
Jak dziecko komunikuje się ze światem?
class Navigation extends React.Component {
onSearch = input => {
alert(`Search: ${input}`);
};
render() {
return (
<SearchBar onSearch={this.onSearch} />
);
}
}
class SearchBar extends React.Component {
onChange = event => {
this.props.onSearch(event.target.value);
};
render() {
return (
<input className="search" onChange={this.onChange} />
);
}
}
class Navigation extends React.Component {
state = {search: ''};
onSearch = input => {
this.setState({search: input});
};
render() {
return (
<SearchBar search={this.state.search} onSearch={this.onSearch} />
);
}
}
class SearchBar extends React.Component {
onChange = event => {
this.props.onSearch(event.target.value);
};
render() {
return (
<input className="search" onChange={this.onChange} />
);
}
}
Bez zmian!
Język komunikacji między serwerem a klientem.
Maciek Stasiełuk
REpresentational State Transfer
HTTP GET /book/1
{
"id": 1,
"title": "Hobbit",
"authorId": 100
}
HTTP GET /author/100
{
"id": 100,
"firstName": "John Ronald Reuel",
"lastName": "Tolkien"
}
HTTP GET /author/100/books
{
"books": [
{"id": 1, "title": "Hobbit"},
{"id": 2, "title": "Drużyna Pierścienia"},
{"id": 3, "title": "Dwie wieże"},
{"id": 4, "title": "Powrót króla"}
]
}
HTTP GET /book/1
{
"id": 1,
"title": "Hobbit",
"authorId": 100
}
HTTP GET /authorWithBooks/100
{
"id": 100,
"firstName": "John Ronald Reuel",
"lastName": "Tolkien",
"books": [
{"id": 1, "title": "Hobbit"},
{"id": 2, "title": "Drużyna pierścienia"},
{"id": 3, "title": "Dwie wieże"},
{"id": 4, "title": "Powrót króla"}
]
}
{
"id": 1,
"title": "Hobbit",
"isbn": "9780582186552",
"author": "John Ronald Reuel Tolkien"
}
{
"id": 1,
"title": "Hobbit",
"isbn": "9780582186552",
"author": "John Ronald Reuel Tolkien",
"pages": 320
}
{
"id": 1,
"title": "Hobbit",
"isbn": "9780582186552",
"author": "John Ronald Reuel Tolkien",
"pages": 279,
"publisher": "Longmans",
"year": 1937
}
{
"id": 1,
"title": "Hobbit",
"isbn": "9780582186552",
"authorId": 100
}
HTTP GET /book/1
{
"id": 1,
"title": "Hobbit",
"isbn": "9780582186552",
"author": "John Ronald Reuel Tolkien",
"pages": 279,
"publisher": "Longmans",
"year": 1937
}
{
"id": 1,
"title": "Hobbit",
"isbn": "9780582186552",
"author": "John Ronald Reuel Tolkien",
"pages": 279,
"publisher": "Longmans",
"year": 1937,
"cover": "hard"
}
{
"id": 1,
"title": "Hobbit",
"isbn": "9780582186552",
"author": "John Ronald Reuel Tolkien",
"pages": 279,
"publisher": "Longmans",
"year": 1937,
"cover": "hard",
"imageUrl": "..."
}
{
"id": 1,
"title": "Hobbit",
"isbn": "9780582186552",
"author": "John Ronald Reuel Tolkien",
"pages": 279,
"publisher": "Longmans",
"year": 1937,
"cover": "hard",
"imageUrl": "...",
"thumbnailUrl": "..."
}
Język programowania
Protokół
Framework
book (id: 1) {
id
title
}
{
"book": {
"id": 1,
"title": "Hobbit"
}
}
book(id: 1) {
id
title
author {
firstName
lastName
}
year
publisher
pages
}
{
"book": {
"id": 1,
"title": "Hobbit",
"author": {
"firstName": "John Ronald Reuel",
"lastName": "Tolkien"
},
"year": 1937,
"publisher": "Longmans",
"pages": 279
}
}
author(id: 100) {
firstName
lastName
books {
title
}
}
{
"author": {
"firstName": "John Ronald Reuel",
"lastName": "Tolkien",
"books": [
{
"title": "Hobbit"
},
{
"title": "Drużyna Pierścienia"
},
{
"title": "Dwie wieże"
},
{
"title": "Powrót króla"
}
]
}
}
book(id: 2) {
isbn
title
author {
lastName
books {
title
year
}
}
}
{
"book": {
"isbn": "9788324132614",
"title": "Drużyna Pierścienia",
"author": {
"lastName": "Tolkien",
"books": [
{
"title": "Hobbit",
"year": 1937
},
{
"title": "Drużyna Pierścienia",
"year": 2009
},
{
"title": "Dwie wieże",
"year": 1981
},
{
"title": "Powrót króla",
"year": 2002
}
]
}
}
}
mutation ($book: BookInput!) {
addNewBook(book: $book) {
id
title
pages
}
}
{
"addNewBook": {
"id": 5,
"title": "Silmarillion",
"pages": 367
}
}
const bookInput = {
title: 'Silmarillion',
publisher: 'Amber',
year: 2014,
pages: 367,
};
type Book {
id: Int!
title: String!
isbn: String
author: Author
authorId: Int
pages: Int
publisher: String
year: Int
cover: CoverType
imageUrl(size: ImageSize): String
}
type Author {
id: Int!
firstName: String!
lastName: String!
books: [Book]
}
type Query {
author(id: Int!): Author
book(id: Int!): Book
books: [Book]
}
enum CoverType {
HARD
SOFT
}
type Author {
id: Int!
firstName: String
lastName: String!
}
type DateTime
enum ImageSize {
FULL
THUMB
}
book (id: 3) {
imageUrl (size: "THUMB")
}
import {Query} from 'react-apollo';
const query = gql`{
books {
id
title
author {
lastName
}
}
}`;
const BookListing = () => (
<Query query={query}>
{({data}) => (
<ul>
{data.books.map(({id, title, author}) => (
<li key={id}>{title} - {author.lastName}</li>
))}
</ul>
)}
</Query>
);
const resolvers = {
Query: {
author: (root, args) => {
return Authors.findOne({_id: args.id})
},
book: (root, args) => {
return Books.findOne({_id: args.id})
},
books: () => {
return Books.find()
},
}
};
Baza danych w języku Internetu.
@radekmie
Grafowe |
Klucz-wartość |
Kolumnowe |
Dokumentowe |
Neo4j |
Redis |
Neo4j |
Neo4j |
Redis |
Cassandra |
Neo4j |
Redis |
Cassandra |
MongoDB |
_id
db.createCollection('example')
db.createCollection('example')
db.getCollection('example').insertOne({
key: 'value',
and: {objects: ['are', 1337]}
})
db.createCollection('example')
db.getCollection('example').insertOne({
key: 'value',
and: {objects: ['are', 1337]}
})
db.getCollection('example').insertMany([
{key: 17},
{key: 20},
{key: 42},
{key: [1, 2, 3]}
])
db.getCollection('example').findOne()
{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : "value",
"and" : {
"objects" : [
"are",
1337.0
]
}
}
db.getCollection('example').find()
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : "value",
"and" : {
"objects" : [
"are",
1337.0
]
}
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdc8"),
"key" : 17.0
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdc9"),
"key" : 20.0
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdca"),
"key" : 42.0
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdcb"),
"key" : [ 1.0, 2.0, 3.0 ]
}]
db.getCollection('example').find({key: 42})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdca"),
"key" : 42.0
}]
db.getCollection('example').find({key: 42})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdca"),
"key" : 42.0
}]
db.getCollection('example').find({key: {$gt: 17}})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc9"),
"key" : 20.0
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdca"),
"key" : 42.0
}]
db.getCollection('example').find({
key: {$in: ['value', 17]}
})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : "value",
"and" : {
"objects" : [
"are",
1337.0
]
}
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdc8"),
"key" : 17.0
}]
db.getCollection('example').find({key: /va..e/})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : "value",
"and" : {
"objects" : [
"are",
1337.0
]
}
}]
db.getCollection('example').find({key: /va..e/})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : "value",
"and" : {
"objects" : [
"are",
1337.0
]
}
}]
db.getCollection('example').find({key: /.*/})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : "value",
"and" : {
"objects" : [
"are",
1337.0
]
}
}]
db.getCollection('example')
.find({}, {key: 1})
.sort({key: -1})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : "value"
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdca"),
"key" : 42.0
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdc9"),
"key" : 20.0
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdc8"),
"key" : 17.0
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdcb"),
"key" : [
1.0,
2.0,
3.0
]
}]
db.getCollection('example')
.find()
.sort({key: -1})
.skip(2)
.limit(1)
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc9"),
"key" : 20.0
}]
$, $all, $and, $bitsAllClear, $bitsAllSet, $bitsAnyClear, $bitsAnySet, $comment, $elemMatch, $elemMatch, $eq, $exists, $expr, $geoIntersects, $geoWithin, $gt, $gte, $in, $jsonSchema, $lt, $lte, $meta, $mod, $ne, $near, $nearSphere, $nin, $nor, $not, $or, $regex, $size, $slice, $text, $type, $where
db.getCollection('example')
.updateOne({key: 'value'}, {$set: {key: 2}})
db.getCollection('example').findOne({key: 2})
{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : 2.0,
"and" : {
"objects" : [
"are",
1337.0
]
}
}
db.getCollection('example')
.updateOne({key: 2}, {$inc: {key: 10}})
db.getCollection('example').findOne({key: 12})
{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : 12.0,
"and" : {
"objects" : [
"are",
1337.0
]
}
}
db.getCollection('example')
.updateOne({key: 12}, {$addToSet: {'and.objects': 1}})
db.getCollection('example').findOne({key: 12})
{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : 12.0,
"and" : {
"objects" : [
"are",
1337.0,
1.0
]
}
}
db.getCollection('example')
.updateOne({key: 12}, {$pull: {'and.objects': 1}})
db.getCollection('example').findOne({key: 12})
{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"key" : 12.0,
"and" : {
"objects" : [
"are",
1337.0
]
}
}
db.getCollection('example')
.updateMany({}, {$set: {x: Math.PI}})
db.getCollection('example').find({}, {x: 1})
[{
"_id" : ObjectId("5adf8c4978f652fd5a50bdc7"),
"x" : 3.14159265358979
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdc8"),
"x" : 3.14159265358979
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdc9"),
"x" : 3.14159265358979
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdca"),
"x" : 3.14159265358979
}, {
"_id" : ObjectId("5adf8c4978f652fd5a50bdcb"),
"x" : 3.14159265358979
}]
$, $[<identifier>], $[], $addToSet, $bit, $currentDate, $each, $inc, $max, $min, $mul, $pop, $position, $pull, $pullAll, $push, $rename, $set, $setOnInsert, $slice, $sort, $unset
db.getCollection('example').deleteOne({})
db.getCollection('example').deleteOne({})
db.getCollection('example').deleteMany({})
db.getCollection('example').deleteOne({})
db.getCollection('example').deleteMany({})
db.getCollection('example').find()
[]
db.getCollection('example').deleteOne({})
db.getCollection('example').deleteMany({})
db.getCollection('example').find()
[]
db.getCollection('example').drop()
db.getCollection('likes').distinct('userId')
[
"vq13u8ey9d",
"tb8m0ovqrp",
"zn3ye8enyz",
"4rqmk6pg0h",
"oslff18b7u",
...
]
db.getCollection('likes').aggregate([
{$match: {date: {$gte: new Date(2016, 0, 1)}}},
{$group: {_id: {$year: '$date'}, likes: {$sum: 1}}},
{$sort: {likes: -1}}
])
db.getCollection('likes').aggregate([
{$match: {date: {$gte: new Date(2016, 0, 1)}}},
{$group: {_id: {$year: '$date'}, likes: {$sum: 1}}},
{$sort: {likes: -1}}
])
[{
_id: 2017.0,
likes: 87291.0
}, {
_id: 2016.0,
likes: 53197.0
}, {
_id: 2018.0,
likes: 17234.0
}]
$addFields, $bucket, $bucketAuto, $collStats, $count, $currentOp, $facet, $geoNear, $graphLookup, $group, $indexStats, $limit, $listLocalSessions, $listSessions, $lookup, $match, $out, $project, $redact, $replaceRoot, $sample, $skip, $sort, $sortByCount, $unwind
$abs, $add, $addToSet, $allElementsTrue, $and, $anyElementTrue, $arrayElemAt, $arrayToObject, $avg, $ceil, $cmp, $concat, $concatArrays, $cond, $dateFromParts, $dateFromString, $dateToParts, $dateToString, $dayOfMonth, $dayOfWeek, $dayOfYear, $divide, $eq, $exp, $filter, $first, $floor, $gt, $gte, $hour, $ifNull, $in, $indexOfArray, $indexOfBytes, $indexOfCP, $isArray, $isoDayOfWeek, $isoWeek, $isoWeekYear, $last, $let, $literal, $ln, $log, $log10, $lt, $lte, $map, $max, $mergeObjects, $meta, $millisecond, $min, $minute, $mod, $month, $multiply, $ne, $not, $objectToArray, $or, $pow, $push, $range, $reduce, $reverseArray, $second, $setDifference, $setEquals, $setIntersection, $setIsSubset, $setUnion, $size, $slice, $split, $sqrt, $stdDevPop, $stdDevSamp, $strLenBytes, $strLenCP, $strcasecmp, $substr, $substrBytes, $substrCP, $subtract, $sum, $switch, $toLower, $toUpper, $trunc, $type, $week, $year, $zip
By Radosław Miernik
IT PopCorn 2018