Skip to content

Commit c870962

Browse files
committed
this will be come the final one.
1 parent 8f24419 commit c870962

File tree

10 files changed

+467
-0
lines changed

10 files changed

+467
-0
lines changed
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from nosql import mongo_setup
2+
from nosql.car import Car
3+
from nosql.owner import Owner
4+
5+
6+
def main():
7+
mongo_setup.init()
8+
9+
print("Computing stats, this WILL take awhile...", flush=True)
10+
11+
cars = list(Car.objects())
12+
print("There are {:,} cars.".format(len(cars)))
13+
14+
owners = list(Owner.objects())
15+
print("There are {:,} owners.".format(len(owners)))
16+
owned_cars = sum((len(o.car_ids) for o in owners))
17+
print("Each owner owns an average of {:.2f} cars.".format(owned_cars / len(owners)))
18+
19+
service_histories = sum((len(c.service_history) for c in cars))
20+
print("There are {:,} service histories.".format(service_histories))
21+
print("Each car has an average of {:.2f} service records.".format(service_histories / len(cars)))
22+
23+
24+
main()
+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import nosql.mongo_setup as mongo_setup
2+
import services.car_service as car_service
3+
from nosql.car import Car
4+
from nosql.engine import Engine
5+
from nosql.owner import Owner
6+
7+
from datetime import datetime
8+
import random
9+
from faker import Faker
10+
11+
from nosql.service_record import ServiceRecord
12+
13+
14+
def main():
15+
# large data DB example
16+
car_count = 250_000
17+
owner_count = 100_000
18+
19+
# simple DB example
20+
# car_count = 200
21+
# owner_count = 100
22+
23+
mongo_setup.init()
24+
clear_db()
25+
26+
t0 = datetime.now()
27+
28+
fake = create_faker_and_seed()
29+
owners = create_owners(fake, count=owner_count)
30+
print("Created {:,.0f} owners".format(len(owners)))
31+
cars = create_cars(count=car_count)
32+
print("Created {:,.0f} cars".format(len(cars)))
33+
if cars and owners:
34+
add_cars_to_owners(owners, cars)
35+
create_service_records(cars, fake)
36+
37+
dt = datetime.now() - t0
38+
print("Done in {} sec".format(dt.total_seconds()))
39+
40+
41+
models = [
42+
'Ferrari 488 GTB',
43+
'Ferrari 360 modena',
44+
'F430',
45+
'599 GTB Fiorano',
46+
'458 Italia',
47+
'LaFerrari',
48+
'Testarossa',
49+
'F12 Berlinetta',
50+
'308 GTB/GTS',
51+
'F355',
52+
'California',
53+
'575M Maranello',
54+
'F50',
55+
'F40',
56+
'Enzo Ferrari',
57+
]
58+
59+
service_operations = [
60+
('Oil change', 200),
61+
('New tires', 1000),
62+
('New engine', 15000),
63+
('Body repair', 4000),
64+
('New seat', 5000),
65+
('Tune up', 1500),
66+
('Air filter', 100),
67+
('Flat tire', 200),
68+
]
69+
70+
71+
def create_faker_and_seed():
72+
fake = Faker()
73+
fake.seed(42)
74+
random.seed(42)
75+
return fake
76+
77+
78+
def clear_db():
79+
Car.drop_collection()
80+
Owner.drop_collection()
81+
82+
83+
def create_owners(fake, count=100):
84+
current_owner_count = Owner.objects().count()
85+
if current_owner_count >= count:
86+
print("There are currently {:,} owners. Skipping create.")
87+
return []
88+
89+
count = count - current_owner_count
90+
91+
datetime_start = datetime(year=2000, month=1, day=1)
92+
datetime_end = datetime(year=datetime.now().year, month=1, day=1)
93+
94+
owners = []
95+
print("Building owners")
96+
for _ in range(0, count):
97+
owner = Owner()
98+
owner.name = fake.name()
99+
owner.created = fake.date_time_between_dates(datetime_start=datetime_start,
100+
datetime_end=datetime_end,
101+
tzinfo=None)
102+
owners.append(owner)
103+
104+
print("Saving owners")
105+
Owner.objects().insert(owners, load_bulk=True)
106+
107+
return list(Owner.objects())
108+
109+
110+
def create_cars(count=200):
111+
current_car_count = Car.objects().count()
112+
if current_car_count >= count:
113+
print("There are currently {:,} cars. Skipping create.")
114+
return []
115+
116+
count = count - current_car_count
117+
118+
hp_factor = 660
119+
mpg_factor = 21
120+
liters_factor = 4
121+
122+
cars = []
123+
print("Building cars...")
124+
for _ in range(0, count):
125+
model = random.choice(models)
126+
make = 'Ferrari'
127+
year = random.randint(1985, datetime.now().year)
128+
mileage = random.randint(0, 150000)
129+
130+
mpg = int((mpg_factor + mpg_factor * random.random() / 4) * 10) / 10.0
131+
horsepower = int(hp_factor + hp_factor * random.random() / 2)
132+
liters = int((liters_factor + liters_factor * random.random() / 2) * 100) / 100.0
133+
134+
engine = Engine(horsepower=horsepower, liters=liters, mpg=mpg)
135+
car = Car(model=model, make=make, year=year, engine=engine, mileage=mileage)
136+
cars.append(car)
137+
138+
print("Saving cars...")
139+
Car.objects().insert(cars)
140+
141+
return list(Car.objects())
142+
143+
144+
def add_cars_to_owners(owners: list, cars: list):
145+
for o in owners:
146+
counter = random.randint(0, 5)
147+
for _ in range(0, counter):
148+
car = random.choice(cars)
149+
car_service.add_owner(o.id, car.id)
150+
151+
152+
def create_service_records(cars, fake):
153+
datetime_start = datetime(year=2000, month=1, day=1)
154+
datetime_end = datetime(year=datetime.now().year, month=1, day=1)
155+
156+
for car in cars:
157+
counter = random.randint(0, 10)
158+
is_positive = random.randint(0, 1) == 1
159+
for _ in range(0, counter):
160+
s = random.choice(service_operations)
161+
sr = ServiceRecord()
162+
sr.description = s[0]
163+
sr.date = fake.date_time_between_dates(datetime_start=datetime_start,
164+
datetime_end=datetime_end,
165+
tzinfo=None)
166+
sr.price = int(s[1] + (random.random() - .5) * s[1] / 4)
167+
if is_positive:
168+
sr.customer_rating = random.randint(4, 5)
169+
else:
170+
sr.customer_rating = random.randint(1, 3)
171+
car.service_history.append(sr)
172+
car.save()
173+
174+
175+
if __name__ == '__main__':
176+
main()
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import uuid
2+
import mongoengine
3+
4+
from nosql.engine import Engine
5+
from nosql.service_record import ServiceRecord
6+
7+
8+
class Car(mongoengine.Document):
9+
model = mongoengine.StringField(required=True)
10+
make = mongoengine.StringField(required=True)
11+
year = mongoengine.IntField(required=True)
12+
mileage = mongoengine.IntField(default=0)
13+
vi_number = mongoengine.StringField(default=lambda: str(uuid.uuid4()).replace("-", ''))
14+
15+
engine = mongoengine.EmbeddedDocumentField(Engine, required=True)
16+
service_history = mongoengine.EmbeddedDocumentListField(ServiceRecord)
17+
18+
# no need to reference owners here, that is entirely contained in owner class
19+
20+
meta = {
21+
'db_alias': 'core',
22+
'collection': 'cars',
23+
'indexes': [
24+
]
25+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import uuid
2+
3+
import mongoengine
4+
5+
6+
class Engine(mongoengine.EmbeddedDocument):
7+
horsepower = mongoengine.IntField(required=True)
8+
liters = mongoengine.FloatField(required=True)
9+
mpg = mongoengine.FloatField(required=True)
10+
serial_number = mongoengine.StringField(default=lambda: str(uuid.uuid4()))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import mongoengine
2+
3+
alias_core = 'core'
4+
5+
6+
def init():
7+
db = 'dealership'
8+
# Other connection options here (server, port, username, etc.)
9+
mongoengine.register_connection(alias=alias_core, name=db)
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from datetime import datetime
2+
3+
import mongoengine
4+
5+
6+
class Owner(mongoengine.Document):
7+
# show off required (not available in mongo or pymongo directly)
8+
name = mongoengine.StringField(required=True)
9+
10+
# show off default
11+
created = mongoengine.DateTimeField(default=datetime.now)
12+
13+
# allows us to use $set and $inc
14+
number_of_visits = mongoengine.IntField(default=0)
15+
16+
# show off many-to-many modeling with one sided list field
17+
# cars can have multiple owners and an owner can own multiple cares
18+
car_ids = mongoengine.ListField(mongoengine.ObjectIdField())
19+
20+
meta = {
21+
'db_alias': 'core',
22+
'collection': 'owners',
23+
'indexes': [
24+
]
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import datetime
2+
import mongoengine
3+
4+
5+
class ServiceRecord(mongoengine.EmbeddedDocument):
6+
date = mongoengine.DateTimeField(default=datetime.datetime.now)
7+
description = mongoengine.StringField()
8+
price = mongoengine.FloatField(required=True)
9+
customer_rating = mongoengine.IntField(required=True) # 1 - 5 satisfaction level.

src/08_perf/big_dealership/q_and_a.py

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
from nosql.car import Car
2+
from nosql.owner import Owner
3+
from datetime import datetime
4+
import nosql.mongo_setup as mongo_setup
5+
6+
7+
def timed(msg, func):
8+
t0 = datetime.now()
9+
10+
func()
11+
12+
dt = datetime.now() - t0
13+
print("{} Time: {:,.3f} ms".format(msg, dt.total_seconds() * 1000.0), flush=True)
14+
15+
16+
mongo_setup.init()
17+
18+
print("Time to ask some questions")
19+
20+
timed(
21+
'How many owners?',
22+
lambda: Owner.objects().filter().count()
23+
)
24+
timed(
25+
'How many cars?',
26+
lambda: Owner.objects().filter().count()
27+
)
28+
29+
timed(
30+
'Find the 10,000th owner?',
31+
lambda: Owner.objects().order_by('name')[10000:10001][0]
32+
)
33+
34+
owner = Owner.objects().order_by('name')[10000:10001][0]
35+
36+
37+
def find_cars_by_owner(owner_id):
38+
the_owner = Owner.objects(id=owner_id).first()
39+
cars = Car.objects().filter(id__in=the_owner.car_ids)
40+
return list(cars)
41+
42+
43+
timed(
44+
'How many cars are owned by the 10,000th owner?',
45+
lambda: find_cars_by_owner(owner.id)
46+
)
47+
48+
49+
def find_owners_by_car(car_id):
50+
owners = Owner.objects(car_ids=car_id)
51+
return list(owners)
52+
53+
54+
car = Car.objects()[10000:10001][0]
55+
timed(
56+
'How many owners own the 10,000th car?',
57+
lambda: find_owners_by_car(car.id)
58+
)
59+
60+
owner50k = Owner.objects()[50000:50001][0]
61+
timed(
62+
'Find owner 50,000 by name?',
63+
lambda: Owner.objects(name=owner50k.name).first()
64+
)
65+
66+
timed(
67+
'Cars with expensive service?',
68+
lambda: Car.objects(service_history__price__gt=16800).count()
69+
)
70+
71+
timed(
72+
'Cars with expensive service and spark plugs?',
73+
lambda: Car.objects(service_history__price__gt=16800, service_history__description='Spark plugs').count()
74+
)
75+
76+
timed(
77+
'Load cars with expensive service and spark plugs?',
78+
lambda: list(Car.objects(service_history__price__gt=16800, service_history__description='Spark plugs'))
79+
)
80+
81+
timed(
82+
'Load car name and ids with expensive service and spark plugs?',
83+
lambda: list(Car.objects(service_history__price__gt=16800, service_history__description='Spark plugs')
84+
.only('make', 'model', 'id'))
85+
)
86+
87+
timed(
88+
'Highly rated, high price service events?',
89+
lambda: Car.objects(service_history__customer_rating=5, service_history__price__gt=16800).count()
90+
)
91+
92+
timed(
93+
'Low rated, low price service events?',
94+
lambda: Car.objects(service_history__customer_rating=1, service_history__price__lt=50).count()
95+
)
96+
97+
timed(
98+
'How many high mileage cars?',
99+
lambda: Car.objects(mileage__gt=140000).count()
100+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mongoengine
2+
faker
3+
python-dateutil # for DateTimeField parsing

0 commit comments

Comments
 (0)