Skip to content

Commit e2d28b6

Browse files
committed
Use lookup aggregations when querying groups
1 parent a400eb9 commit e2d28b6

File tree

2 files changed

+44
-45
lines changed

2 files changed

+44
-45
lines changed

pydatalab/src/pydatalab/routes/v0_1/admin.py

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import datetime
2-
import json
32

43
import pymongo.errors
54
from bson import ObjectId
@@ -284,52 +283,49 @@ def list_access_tokens():
284283

285284
@ADMIN.route("/groups", methods=["GET"])
286285
def get_groups():
287-
groups_data = []
288-
for group_doc in flask_mongo.db.groups.find():
289-
group_doc["immutable_id"] = str(group_doc["_id"])
290-
291-
group_data = json.loads(Group(**group_doc).json())
292-
293-
# TODO: remove or refactor into $lookup
294-
group_members = list(
295-
flask_mongo.db.users.find(
296-
{"group_ids": group_doc["_id"]}, {"_id": 1, "display_name": 1, "contact_email": 1}
297-
)
298-
)
299-
group_data["members"] = [
286+
# Lookup members from users collection: find all those that refer to this group ID in their groups->immutable_id field
287+
members_lookup = {
288+
"from": "users",
289+
"let": {"group_id": "$_id"},
290+
"pipeline": [
300291
{
301-
"immutable_id": str(member["_id"]),
302-
"display_name": member.get("display_name", ""),
303-
"contact_email": member.get("contact_email", ""),
304-
}
305-
for member in group_members
306-
]
292+
"$match": {
293+
"$expr": {"$in": ["$$group_id", {"$ifNull": ["$groups.immutable_id", []]}]}
294+
}
295+
},
296+
{
297+
"$project": {
298+
"_id": 1,
299+
"display_name": 1,
300+
"contact_email": 1,
301+
}
302+
},
303+
],
304+
"as": "members",
305+
}
307306

308-
# TODO: remove or refactor into $lookup
309-
if group_doc.get("managers"):
310-
admin_ids = [ObjectId(admin_id) for admin_id in group_doc["managers"]]
311-
managers = list(
312-
flask_mongo.db.users.find(
313-
{"_id": {"$in": admin_ids}}, {"_id": 1, "display_name": 1, "contact_email": 1}
314-
)
315-
)
316-
group_data["managers"] = [
317-
{
318-
"immutable_id": str(admin["_id"]),
319-
"display_name": admin.get("display_name", ""),
320-
"contact_email": admin.get("contact_email", ""),
307+
# Lookup managers from users collection: find all those referred to by ID in this group under the managers field
308+
managers_lookup = {
309+
"from": "users",
310+
"let": {"manager_ids": "$managers"},
311+
"pipeline": [
312+
{"$match": {"$expr": {"$in": ["$_id", {"$ifNull": ["$$manager_ids", []]}]}}},
313+
{
314+
"$project": {
315+
"_id": 1,
316+
"display_name": 1,
317+
"contact_email": 1,
321318
}
322-
for admin in managers
323-
]
319+
},
320+
],
321+
"as": "managers",
322+
}
324323

325-
groups_data.append(group_data)
324+
group_docs = flask_mongo.db.groups.aggregate(
325+
[{"$match": {}}, {"$lookup": members_lookup}, {"$lookup": managers_lookup}]
326+
)
326327

327-
return jsonify(
328-
{
329-
"status": "success",
330-
"data": groups_data,
331-
}
332-
), 200
328+
return jsonify({"status": "success", "data": [Group(**d).dict() for d in group_docs]}), 200
333329

334330

335331
@ADMIN.route("/groups", methods=["PUT"])
@@ -399,6 +395,9 @@ def update_group(group_immutable_id: str):
399395
if "description" in request_json:
400396
update_data["description"] = request_json["description"]
401397

398+
if "group_id" in request_json:
399+
update_data["group_id"] = request_json["group_id"]
400+
402401
if "managers" in request_json:
403402
update_data["managers"] = [ObjectId(u) for u in request_json["managers"]]
404403
bad_managers = [

pydatalab/tests/server/test_users.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def test_user_update_admin(admin_client, database, user_id):
151151

152152

153153
def test_groups(
154-
admin_client, client, unauthenticated_client, database, admin_id, user_id, another_user_id
154+
admin_client, client, unauthenticated_client, database, admin_user_id, user_id, another_user_id
155155
):
156156
from bson import ObjectId
157157

@@ -181,7 +181,7 @@ def test_groups(
181181
# Request must come from admin
182182
# Make ID unique so that this would otherwise pass
183183
good_group["group_id"] = "my-new-group-2"
184-
resp = unauthenticated_client.patch("/groups", json=good_group)
184+
resp = unauthenticated_client.put("/groups", json=good_group)
185185
assert resp.status_code == 401
186186
assert database.groups.find_one({"group_id": good_group["group_id"]}) is None
187187

@@ -215,7 +215,7 @@ def test_groups(
215215
"display_name": "My Newly Named Group",
216216
"group_id": "my-new-group-renamed",
217217
"description": "A group for testing the group update mechanism",
218-
"managers": [admin_id, user_id, another_user_id],
218+
"managers": [admin_user_id, user_id, another_user_id],
219219
}
220220

221221
resp = admin_client.patch(f"/groups/{group_immutable_id}", json=new_details)

0 commit comments

Comments
 (0)