Writing Apps the Google-y Way

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

description

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

Transcript of Writing Apps the Google-y Way

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.