Skip to content

Commit 7863c0f

Browse files
committed
Feature: allow to exclude relations on object get
1 parent ab600c3 commit 7863c0f

File tree

11 files changed

+117
-47
lines changed

11 files changed

+117
-47
lines changed

mwdb/model/object.py

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from sqlalchemy import and_, cast, distinct, exists, func, or_, select
88
from sqlalchemy.dialects.postgresql import JSONB
99
from sqlalchemy.exc import IntegrityError
10-
from sqlalchemy.orm import aliased, column_property, contains_eager
10+
from sqlalchemy.orm import column_property
1111
from sqlalchemy.sql.expression import true
1212

1313
from mwdb.core.capabilities import Capabilities
@@ -668,6 +668,21 @@ def _get_or_create(
668668

669669
return created_object, is_new
670670

671+
def query_visible_parents(self, requestor=None):
672+
"""
673+
Queries for parents visible by specified requestor.
674+
"""
675+
if requestor is None:
676+
requestor = g.auth_user
677+
678+
return (
679+
db.session.query(Object)
680+
.join(relation, relation.c.parent_id == Object.id)
681+
.filter(relation.c.child_id == self.id)
682+
.order_by(relation.c.creation_time.desc())
683+
.filter(requestor.has_access_to_object(Object.id))
684+
)
685+
671686
@classmethod
672687
def access(cls, identifier, requestor=None):
673688
"""
@@ -686,35 +701,12 @@ def access(cls, identifier, requestor=None):
686701
if requestor is None:
687702
requestor = g.auth_user
688703

689-
obj = cls.get(identifier)
704+
obj_query = cls.get(identifier)
705+
obj = obj_query.first()
690706
# If object doesn't exist - it doesn't exist
691-
if obj.first() is None:
707+
if obj is None:
692708
return None
693709

694-
# In that case we want only those parents to which requestor has access.
695-
stmtp = (
696-
db.session.query(Object)
697-
.join(relation, relation.c.parent_id == Object.id)
698-
.filter(
699-
Object.id.in_(
700-
db.session.query(relation.c.parent_id).filter(
701-
relation.c.child_id == obj.first().id
702-
)
703-
)
704-
)
705-
.order_by(relation.c.creation_time.desc())
706-
.filter(requestor.has_access_to_object(Object.id))
707-
)
708-
stmtp = stmtp.subquery()
709-
710-
parent = aliased(Object, stmtp)
711-
712-
obj = (
713-
obj.outerjoin(parent, Object.parents)
714-
.options(contains_eager(Object.parents, alias=parent))
715-
.all()[0]
716-
)
717-
718710
# Ok, now let's check whether requestor has explicit access
719711
if obj.has_explicit_access(requestor):
720712
return obj

mwdb/resources/blob.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from mwdb.model.object import ObjectTypeConflictError
99
from mwdb.schema.blob import (
1010
BlobCreateRequestSchema,
11+
BlobItemAndRelationsResponseSchema,
1112
BlobItemResponseSchema,
1213
BlobLegacyCreateRequestSchema,
1314
BlobListResponseSchema,
@@ -186,6 +187,7 @@ def post(self):
186187
class TextBlobItemResource(ObjectItemResource, TextBlobUploader):
187188
ObjectType = TextBlob
188189
ItemResponseSchema = BlobItemResponseSchema
190+
ItemAndRelationsResponseSchema = BlobItemAndRelationsResponseSchema
189191
CreateRequestSchema = BlobLegacyCreateRequestSchema
190192

191193
def call_specialised_remove_hook(self, text_blob):

mwdb/resources/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from mwdb.schema.blob import BlobCreateSpecSchema
1414
from mwdb.schema.config import (
1515
ConfigCreateRequestSchema,
16+
ConfigItemAndRelationsResponseSchema,
1617
ConfigItemResponseSchema,
1718
ConfigLegacyCreateRequestSchema,
1819
ConfigListResponseSchema,
@@ -166,6 +167,7 @@ class ConfigResource(ObjectResource, ConfigUploader):
166167
ObjectType = Config
167168
ListResponseSchema = ConfigListResponseSchema
168169
ItemResponseSchema = ConfigItemResponseSchema
170+
ItemAndRelationsResponseSchema = ConfigItemAndRelationsResponseSchema
169171

170172
@requires_authorization
171173
def get(self):
@@ -309,6 +311,7 @@ class ConfigItemResource(ObjectItemResource, ConfigUploader):
309311

310312
ObjectType = Config
311313
ItemResponseSchema = ConfigItemResponseSchema
314+
ItemAndRelationsResponseSchema = ConfigItemAndRelationsResponseSchema
312315
CreateRequestSchema = ConfigLegacyCreateRequestSchema
313316

314317
def call_specialised_remove_hook(self, config):

mwdb/resources/file.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from mwdb.schema.file import (
1212
FileCreateRequestSchema,
1313
FileDownloadTokenResponseSchema,
14+
FileItemAndRelationsResponseSchema,
1415
FileItemResponseSchema,
1516
FileLegacyCreateRequestSchema,
1617
FileListResponseSchema,
@@ -203,6 +204,7 @@ def post(self):
203204
class FileItemResource(ObjectItemResource, FileUploader):
204205
ObjectType = File
205206
ItemResponseSchema = FileItemResponseSchema
207+
ItemAndRelationsResponseSchema = FileItemAndRelationsResponseSchema
206208
CreateRequestSchema = FileLegacyCreateRequestSchema
207209

208210
def call_specialised_remove_hook(self, file):
@@ -225,6 +227,15 @@ def get(self, identifier):
225227
schema:
226228
type: string
227229
description: File identifier (SHA256/SHA512/SHA1/MD5)
230+
- in: query
231+
name: exclude_relations
232+
schema:
233+
type: integer
234+
description: |
235+
If set, results doesn't include relations
236+
which will be default behavior on next major release of MWDB
237+
required: false
238+
default: 0
228239
responses:
229240
200:
230241
description: Information about file

mwdb/resources/object.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from mwdb.schema.object import (
1717
ObjectCountRequestSchema,
1818
ObjectCountResponseSchema,
19+
ObjectItemAndRelationsResponseSchema,
20+
ObjectItemRequestSchema,
1921
ObjectItemResponseSchema,
2022
ObjectListRequestSchema,
2123
ObjectListResponseSchema,
@@ -262,6 +264,7 @@ def get(self):
262264
class ObjectItemResource(Resource, ObjectUploader):
263265
ObjectType = Object
264266
ItemResponseSchema = ObjectItemResponseSchema
267+
ItemAndRelationsResponseSchema = ObjectItemAndRelationsResponseSchema
265268
CreateRequestSchema = None
266269

267270
def call_specialised_remove_hook(self, obj):
@@ -284,6 +287,15 @@ def get(self, identifier):
284287
schema:
285288
type: string
286289
description: Object identifier
290+
- in: query
291+
name: exclude_relations
292+
schema:
293+
type: integer
294+
description: |
295+
If set, results doesn't include relations
296+
which will be default behavior on next major release of MWDB
297+
required: false
298+
default: 0
287299
responses:
288300
200:
289301
description: Information about object
@@ -298,10 +310,15 @@ def get(self, identifier):
298310
description: |
299311
Request canceled due to database statement timeout.
300312
"""
313+
args = load_schema(request.args, ObjectItemRequestSchema())
301314
obj = self.ObjectType.access(identifier)
302315
if obj is None:
303316
raise NotFound("Object not found")
304-
schema = self.ItemResponseSchema()
317+
schema = (
318+
self.ItemResponseSchema()
319+
if args["exclude_relations"]
320+
else self.ItemAndRelationsResponseSchema()
321+
)
305322
return schema.dump(obj)
306323

307324
def _get_upload_args(self, parent_identifier):

mwdb/resources/relations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from mwdb.core.plugins import hooks
66
from mwdb.core.rate_limit import rate_limited_resource
77
from mwdb.model import Object, db
8-
from mwdb.schema.relations import RelationsResponseSchema
8+
from mwdb.schema.object import RelationsResponseSchema
99

1010
from . import access_object, logger, requires_authorization, requires_capabilities
1111

mwdb/schema/blob.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
ObjectLegacyMetakeysMixin,
88
ObjectListItemResponseSchema,
99
ObjectListResponseSchemaBase,
10+
RelationsResponseSchema,
1011
)
1112
from .utils import UTCDateTime
1213

@@ -47,3 +48,12 @@ class BlobItemResponseSchema(ObjectItemResponseSchema):
4748
latest_config = fields.Nested(
4849
ConfigItemResponseSchema, required=True, allow_none=True
4950
)
51+
52+
53+
class BlobItemAndRelationsResponseSchema(
54+
BlobItemResponseSchema, RelationsResponseSchema
55+
):
56+
"""
57+
This is legacy schema that returns object item along with relations
58+
It is awfully slow when object is bound with lots of relatives
59+
"""

mwdb/schema/config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
ObjectLegacyMetakeysMixin,
77
ObjectListItemResponseSchema,
88
ObjectListResponseSchemaBase,
9+
RelationsResponseSchema,
910
)
1011

1112

@@ -47,6 +48,15 @@ class ConfigItemResponseSchema(ObjectItemResponseSchema):
4748
cfg = fields.Dict(required=True, allow_none=False)
4849

4950

51+
class ConfigItemAndRelationsResponseSchema(
52+
ConfigItemResponseSchema, RelationsResponseSchema
53+
):
54+
"""
55+
This is legacy schema that returns object item along with relations
56+
It is awfully slow when object is bound with lots of relatives
57+
"""
58+
59+
5060
class ConfigStatsItemResponseSchema(Schema):
5161
family = fields.Str(required=True, allow_none=False)
5262
last_upload = fields.Date(required=True, allow_none=False)

mwdb/schema/file.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
ObjectLegacyMetakeysMixin,
1010
ObjectListItemResponseSchema,
1111
ObjectListResponseSchemaBase,
12+
RelationsResponseSchema,
1213
)
1314

1415

@@ -71,5 +72,14 @@ class FileItemResponseSchema(ObjectItemResponseSchema):
7172
)
7273

7374

75+
class FileItemAndRelationsResponseSchema(
76+
FileItemResponseSchema, RelationsResponseSchema
77+
):
78+
"""
79+
This is legacy schema that returns object item along with relations
80+
It is awfully slow when object is bound with lots of relatives
81+
"""
82+
83+
7484
class FileDownloadTokenResponseSchema(Schema):
7585
token = fields.Str(required=True, allow_none=False)

mwdb/schema/object.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ def validate_key(self, data, **kwargs):
3030
)
3131

3232

33+
class ObjectItemRequestSchema(Schema):
34+
exclude_relations = fields.Boolean(
35+
truthy={
36+
"1",
37+
"",
38+
},
39+
falsy={"0"},
40+
missing=False,
41+
allow_none=False,
42+
)
43+
44+
3345
class ObjectCountRequestSchema(Schema):
3446
query = fields.Str(missing=None)
3547

@@ -103,12 +115,6 @@ class ObjectItemResponseSchema(Schema):
103115
upload_time = UTCDateTime(required=True, allow_none=False)
104116
favorite = fields.Boolean(required=True, allow_none=False)
105117

106-
parents = fields.Nested(
107-
ObjectListItemResponseSchema, many=True, required=True, allow_none=False
108-
)
109-
children = fields.Nested(
110-
ObjectListItemResponseSchema, many=True, required=True, allow_none=False
111-
)
112118
attributes = fields.Nested(
113119
AttributeItemResponseSchema, many=True, required=True, allow_none=False
114120
)
@@ -125,5 +131,26 @@ def get_accessible_attributes(self, data, object, **kwargs):
125131
return {**data, "attributes": attributes_serialized}
126132

127133

134+
class RelationsResponseSchema(Schema):
135+
parents = fields.Method("get_parents")
136+
children = fields.Nested(
137+
ObjectListItemResponseSchema, many=True, required=True, allow_none=False
138+
)
139+
140+
def get_parents(self, obj):
141+
parents = obj.query_visible_parents().all()
142+
schema = ObjectListItemResponseSchema()
143+
return schema.dump(parents, many=True)
144+
145+
146+
class ObjectItemAndRelationsResponseSchema(
147+
ObjectItemResponseSchema, RelationsResponseSchema
148+
):
149+
"""
150+
This is legacy schema that returns object item along with relations
151+
It is awfully slow when object is bound with lots of relatives
152+
"""
153+
154+
128155
class ObjectCountResponseSchema(Schema):
129156
count = fields.Int()

mwdb/schema/relations.py

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)