67
WRITING APPS THE GOOGLE-Y WAY Pamela Fox, YOW! Australia 2010

Writing Apps the Google-y Way

Embed Size (px)

DESCRIPTION

Talk by Pamela Fox at YOW 2010 about App Engine, the datastore, and how to implement common features for your webapp.

Citation preview

Page 1: Writing Apps the Google-y Way

WRITING APPS THE GOOGLE-Y WAY

Pamela Fox, YOW! Australia 2010

Page 2: Writing Apps the Google-y Way

Who am I?

twitter.com/pamelafox

[email protected]

pamelafox.org

you get the idea...

Page 3: Writing Apps the Google-y Way

Who am I?

USC Google

Amazon, Flickr, Maps

Google Maps API Google Wave API

Spreadsheets, Blogger, Youtube, Picasa, Gadgets, App Engine

Page 4: Writing Apps the Google-y Way

Who am I?

Java pYthon

Page 5: Writing Apps the Google-y Way

What is App Engine?

“Google App Engine enables you to build and host web apps on the same systems that power Google applications.”

http://code.google.com/appengine

Page 6: Writing Apps the Google-y Way

What is a “web app”?

Page 7: Writing Apps the Google-y Way

Static vs. Dynamic

Page 8: Writing Apps the Google-y Way

Anonymous vs. Users

Page 9: Writing Apps the Google-y Way

Intranet vs. Internet

~2 billionHundreds - Thousands

Page 10: Writing Apps the Google-y Way

What is a “web app”?

Page 11: Writing Apps the Google-y Way

Some Google web apps

Page 12: Writing Apps the Google-y Way

Some Google App Engine web apps

www.gifttag.comwww.buddypoke.com

Page 13: Writing Apps the Google-y Way

Google apps on App Engine

panoramio.com pubsubhubbub.appspot.com

Page 14: Writing Apps the Google-y Way

How does App Engine work?

1. You upload application code & resources to Google.

2. Google serves your application from scalable infrastructure.

3. You pay for only the resources that Google used in serving the application.

Page 15: Writing Apps the Google-y Way

Example app: YOW!*

Page 16: Writing Apps the Google-y Way

App Engine architecture

user or task

Page 17: Writing Apps the Google-y Way

App Engine architecture

Page 18: Writing Apps the Google-y Way

App Engine architecture

LIMIT!

Page 19: Writing Apps the Google-y Way

The tricky bits

LIMIT!

Page 20: Writing Apps the Google-y Way

Datastore

Entity

PropertiesKey

Entity

Entity

Entity

Entity

Path Kind Name/ID

Page 21: Writing Apps the Google-y Way

Example: Speaker Entities

Key Path

Kind ID First Name

Last Name

Speaker1

- Speaker 1 Rod Johnson

Key Path

Kind ID First Name

Last Name

Middle Name Suffix

Speaker1 - Speaker

2 Guy Steele L Jr.

Page 22: Writing Apps the Google-y Way

Modeling Speaker Entities

class Speaker(db.model):

firstname = db.StringProperty(required=True)

lastname = db.StringProperty(required=True)

middlename = db.StringProperty()

namesuffix = db.StringProperty()

website = db.StringProperty()

keynote = db.BooleanProperty(default=False)

Page 23: Writing Apps the Google-y Way

Saving Speaker Entities

ron = Speaker(firstname="Ron", lastname="Johnson")

guy = Speaker(firstname="Guy", lastname="Steele",

middlename="L", namesuffix="Jr.")

ron.put()

guy.put()

Page 24: Writing Apps the Google-y Way

Updating Speaker Entities

ron = Speaker.get_by_id(1)

guy = Speaker.get_by_id(2)

ron.website = "http://www.ronjohnson.com"

ron.keynote = True

guy.website = "http://www.guysteele.com"

guy.keynote = True

db.put(ron, guy)

LIMIT!

Page 25: Writing Apps the Google-y Way

How Updates Happen

commitjournal apply entities

apply indexes

A B

Page 26: Writing Apps the Google-y Way

Queries & Indexes

Query Index

Index

Index

Index

Query

Query

Query

Query

Query

Page 27: Writing Apps the Google-y Way

Queries & Indexes

SELECT * from Speaker ORDER BY lastname

key lastname

Speaker3 Fox

Speaker4 Hohpe

Speaker1 Johnson

Speaker2 Steele

LIMIT!

Page 28: Writing Apps the Google-y Way

Queries & Indexes

SELECT * from Speaker ORDER by middlename

key middlename

Speaker2 L

Page 29: Writing Apps the Google-y Way

Queries & Indexes

SELECT * from Speaker WHERE keynote = True

key keynote

Speaker1 True

Speaker2 True

Speaker3 False

Speaker4 False

Page 30: Writing Apps the Google-y Way

Queries & Indexes

SELECT * from Speaker WHERE keynote = False

key keynote

Speaker1 True

Speaker2 True

Speaker3 False

Speaker4 False

Page 31: Writing Apps the Google-y Way

Queries

allspeakers = Speaker.all().order('lastname)

for speaker in allspeakers:

print speaker.firstname + '' + speaker.lastname + '' + speaker.website

keynotespeakers = Speaker.all().filter('keynote = ', True)

notspecialspeakers = Speaker.all().filter('keynote = ', False)

LIMIT!

Page 32: Writing Apps the Google-y Way

Custom Indexes

SELECT * from Speaker ORDER BY lastname, keynote

key lastname keynote

Speaker3 Fox false

Speaker4 Hohpe false

Speaker1 Johnson true

Speaker2 Steele true

speakers = Speaker.all().order('lastname')

.order('keynote')

Page 33: Writing Apps the Google-y Way

Custom Indexes

SELECT * from Speaker WHERE lastname > 'Johnson' and keynote = true

key lastname keynote

Speaker3 Fox false

Speaker4 Hohpe false

Speaker1 Johnson true

Speaker2 Steele true

speakers = Speaker.all().order('lastname')

.filter('keynote =', True)

Page 34: Writing Apps the Google-y Way

Impossible Indexes

SELECT * from Speaker WHERE lastname < 'Steele' and firstname > 'Gregory'

key lastname firstname

Speaker3 Fox Pamela

Speaker4 Hohpe Gregory

Speaker1 Johnson Ron

Speaker2 Steele Guy

...not in subsequent rows!

Page 35: Writing Apps the Google-y Way

Impossible Indexes

SELECT * from Speaker WHERE lastname > 'Fox' ORDER BY firstname

key lastname firstname

Speaker3 Fox Pamela

Speaker4 Hohpe Gregory

Speaker1 Johnson Ron

Speaker2 Steele Guy

...not in the correct order!

Page 36: Writing Apps the Google-y Way

Queries with Offset

SELECT * from Speaker LIMIT 2 OFFSET 2

key lastname

Speaker3 Fox

Speaker4 Hohpe

Speaker1 Johnson

Speaker2 Steele

speakers = Speaker.all().fetch(2, 2)

1

2

...slow! LIMIT!

Page 37: Writing Apps the Google-y Way

Queries with Cursors

query = db.Query(Speaker)

speakers = q.fetch(1000)

cursor = q.cursor()

memcache.set('speaker_cursor', cursor)

...

last_cursor = memcache.get('speaker_cursor')

q.with_cursor(last_cursor)

speakers = q.fetch(1000)

Page 38: Writing Apps the Google-y Way

More Properties

class Talk(db.Model):

title = db.StringProperty(required=True)

abstract = db.TextProperty(required=True)

speaker = db.ReferenceProperty(Speaker)

tags = db.StringListProperty()

pamela = Speaker.all().filter('firstname = ', 'Pamela').get()

talk = Talk('Writing Apps the Googley Way', 'Bla bla bla',

pamela, ['App Engine', 'Python'])

talk.put()

talk = Talk('Wonders of the Onesie', 'Bluh bluh bluh',

pamela, ['Pajamas', 'Onesies'])

talk.put()

Page 39: Writing Apps the Google-y Way

Back-References

pamela = Speaker.all().filter('firstname = ', 'Pamela').get()

for talk in pamela.talk_set:

print talk.title

key speaker

Talk6 Speaker2

Talk1 Speaker3

Talk2 Speaker3

Talk5 Speaker4

SELECT * from Talk WHERE speaker = Speaker3

Page 40: Writing Apps the Google-y Way

Searching List Properties

talks = Talk.all().filter('tags = ', 'python').fetch(10)

SELECT * from Talk WHERE tags = 'Python'

key lastname

Talk1 App Engine

Talk2 Pajamas

Talk1 Python

Talk2 Onesies

LIMIT!

Page 41: Writing Apps the Google-y Way

Entity Groups

pamela = Speaker.all().filter('firstname = ', 'Pamela').get()

talk1 = Talk('Writing Apps the Googley Way', 'Bla bla bla',

pamela, ['App Engine', 'Python'],

parent=pamela)

talk2 = Talk('Wonders of the Onesie', 'Bluh bluh bluh',

pamela, ['Pajamas', 'Onesies'],

parent=pamela)

db.put(talk1, talk2)

def update_talks():

talk1.title = 'Writing Apps the Microsoft Way'

talk2.title = 'Wonders of the Windows'

db.put(talk1, talk2)

db.run_in_transaction(update_talks)

Page 42: Writing Apps the Google-y Way

Common Features

Page 43: Writing Apps the Google-y Way

Counters

1 2 3 4 5people have done something.

Page 44: Writing Apps the Google-y Way

RageTube: Global Stats

Page 45: Writing Apps the Google-y Way

RageTube: Global Stats

SongStat

yaycountviewcount

title artist

Key

Path KindName(song)

naycount

mehcount

Page 46: Writing Apps the Google-y Way

RageTube: Global Stats

viewcount viewcount viewcount

datastore memcache

Page 47: Writing Apps the Google-y Way

RageTube: Global Stats

class Song(db.Model): viewcount = db.IntegerProperty(default=0) title = db.StringProperty() artist = db.StringProperty()

def get_viewcount(self): viewcount = self.viewcount cached_viewcount = memcache.get('viewcount-' + self.key().name(), self.key().kind()) if cached_viewcount: viewcount += cached_viewcount return viewcount

@classmethod def flush_viewcount(cls, name): song = cls.get_by_key_name(name) value = memcache.get('viewcount-' + name, cls.kind()) memcache.decr('viewcount-' + name, value, cls.kind()) song.viewcount += value song.put()

@classmethod def incr_viewcount(cls, name, interval=5, value=1): memcache.incr('viewcount-' + name, value, cls.kind()) interval_num = get_interval_number(datetime.now(), interval) task_name = '-'.join([cls.kind(), name.replace(' ', '-'), 'viewcount', str(interval), str(interval_num)]) deferred.defer(cls.flush_viewcount, name, _name=task_name) LIMIT!

Page 48: Writing Apps the Google-y Way

Ratings

Rated by 500 users.

Page 49: Writing Apps the Google-y Way

App Gallery: Ratings

Page 50: Writing Apps the Google-y Way

App Gallery: Ratings

Comment Application

total_ratings

sum_ratings

avg_ratingrated_inde

x

comment_count

rating

Page 51: Writing Apps the Google-y Way

App Gallery: Ratings

def UpdateAppCommentData(self, rating, operation): def UpdateCommentData(self, rating, operation): self.comment_count += 1 * operation self.sum_ratings += rating * operation self.total_ratings += 1 * operation self.avg_rating = int(round(self.sum_ratings / self.total_ratings)) self.rated_index = '%d:%d:%d' % (self.avg_rating, self.total_ratings, self.index) self.put()

db.run_in_transaction(UpdateCommentData, self, rating, operation)

app.UpdateAppCommentData(rating, db_models.Comment.ADD)comment = db_models.Comment()comment.application = appcomment.rating = ratingcomment.put()

query.order('-avg_rating').order('-rated_index')

Page 52: Writing Apps the Google-y Way

Geospatial Queries

Page 53: Writing Apps the Google-y Way

City-Go-Round: Agencies

citygoround.org

https://github.com/walkscore/City-Go-Round

Page 54: Writing Apps the Google-y Way

City-Go-Round: Geo Queries

AgencyGeoModel

location (GeoPt)

location_geocells (StringListProper

ty)

Page 55: Writing Apps the Google-y Way

City-Go-Round: Geo Queries

def fetch_agencies_near(lat, long, bbox_side_in_miles): query = Agency.all() bbox = bbox_centered_at(lat, long, bbox_side_in_miles) return Agency.bounding_box_fetch(query, bbox, max_results = 50)

def bounding_box_fetch(query, bbox, max_results=1000,): results = [] query_geocells = geocell.best_bbox_search_cells(bbox)

for entity in query.filter('location_geocells IN', query_geocells): if len(results) == max_results: break if (entity.location.lat >= bbox.south and entity.location.lat <= bbox.north and entity.location.lon >= bbox.west and entity.location.lon <= bbox.east): results.append(entity) return results

Page 56: Writing Apps the Google-y Way

City-Go-Round: Apps

citygoround.org

Page 57: Writing Apps the Google-y Way

City-Go-Round: Geo Queries

TransitAppLocation

GeoModel

location (GeoPt)

location_geocells (StringListProper

ty)

TransitApp

Page 58: Writing Apps the Google-y Way

City-Go-Round: Geo Queries

class TransitAppLocation(GeoModel): transit_app = db.ReferenceProperty(TransitApp) def fetch_transit_app_locations_near(lat, longi): query = TransitAppLocation.all() bbox = bbox_centered_at(lat, long, bbox_side_in_miles) return TransitAppLocation.bounding_box_fetch(query, bounding_box, max_results = 500)

def fetch_transit_apps_near(lat, long):

transit_app_locations = TransitAppLocation.fetch_transit_app_locations_near(lat, long)]

transit_apps = [transit_app_location.transit_app for transit_app_location in transit_app_locations] return transit_apps

Page 59: Writing Apps the Google-y Way

Full Text Search

pizza Search

ThingyIt's like pizza, but in the cloud.

Other ThingyThis will make you smell as delicious as pizza.

Page 60: Writing Apps the Google-y Way

Disclosed.ca: Search

Page 61: Writing Apps the Google-y Way

Disclosed.ca: Search

Contract

agency_name vendor_name

description comments

uri

Page 62: Writing Apps the Google-y Way

Disclosed.ca: Search

from search.core import SearchIndexProperty, porter_stemmer

class Contract(db.Model): uri = db.StringProperty(required=True) agency_name = db.StringProperty(required=True) vendor_name = db.StringProperty(required=True) description = db.StringProperty() comments = db.TextProperty() search_index = SearchIndexProperty(('agency_name', 'vendor_name', 'description', 'comments'), indexer=porter_stemmer)

results = Contract.search_index.search(sheep').fetch(20)

Page 63: Writing Apps the Google-y Way

Disclosed.ca: Search

Contract

agency_name vendor_name

description comments

uri

search_index(StringListProper

ty)

SearchIndex

Page 64: Writing Apps the Google-y Way

Disclosed.ca: Search

key search_index

ContractSearch1 charter

ContractSearch1 june

ContractSearch1 sheep

ContractSearch2 sheep

ContractSearch1 wood

SELECT FROM ContractSearch WHERE search_index = "sheep"

Page 65: Writing Apps the Google-y Way

More Learning

http://ae-book.appspot.com

http://code.google.com/appengine

http://blog.notdot.net/

Page 66: Writing Apps the Google-y Way

AppEngine: Now & Later

"Run your web apps on Google's infrastructure.Easy to build, easy to maintain, easy to scale."

Roadmap:•Background processes > 30s•MapReduce•Bulk Import/Export•Channel API

App Engine for Business:•SQL• Other stuff..

Page 67: Writing Apps the Google-y Way

Thanks for coming!

*I am never very good at conclusions, so this slide is a subtle notice to all of you that I am done talking now.