Skip to content

Commit 263a74e

Browse files
Arun S KumarArun S Kumar
authored andcommitted
Query efficiency and performance - By retrieving only the queried fields from the database - Test Passed
1 parent 1553e71 commit 263a74e

File tree

3 files changed

+67
-7
lines changed

3 files changed

+67
-7
lines changed

graphene_mongo/converter.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from mongoengine.base import get_document
88
from . import advanced_types
99
from .utils import import_single_dispatch, get_field_description, get_query_fields
10+
from concurrent.futures import ThreadPoolExecutor, wait, as_completed
1011

1112
singledispatch = import_single_dispatch()
1213

@@ -104,6 +105,46 @@ def convert_file_to_field(field, registry=None):
104105
def convert_field_to_list(field, registry=None):
105106
base_type = convert_mongoengine_field(field.field, registry=registry)
106107
if isinstance(base_type, graphene.Field):
108+
if isinstance(field.field, mongoengine.GenericReferenceField):
109+
def get_reference_objects(*args, **kwargs):
110+
if args[0][1]:
111+
document = get_document(args[0][0])
112+
document_field = mongoengine.ReferenceField(document)
113+
document_field = convert_mongoengine_field(document_field, registry)
114+
document_field_type = document_field.get_type().type._meta.name
115+
only_fields = [to_snake_case(i) for i in get_query_fields(args[0][3])[document_field_type].keys()]
116+
return document.objects().no_dereference().only(*only_fields).filter(pk__in=args[0][1])
117+
else:
118+
return []
119+
120+
def reference_resolver(root, *args, **kwargs):
121+
choice_to_resolve = dict()
122+
to_resolve = getattr(root, field.name or field.db_name)
123+
for each in to_resolve:
124+
if each['_cls'] not in choice_to_resolve:
125+
choice_to_resolve[each['_cls']] = list()
126+
choice_to_resolve[each['_cls']].append(each["_ref"].id)
127+
128+
pool = ThreadPoolExecutor(5)
129+
futures = list()
130+
for model, object_id_list in choice_to_resolve.items():
131+
futures.append(pool.submit(get_reference_objects, (model, object_id_list, registry, *args)))
132+
result = list()
133+
for x in as_completed(futures):
134+
result += x.result()
135+
to_resolve_object_ids = [each["_ref"].id for each in to_resolve]
136+
result_to_resolve_object_ids = [each.id for each in result]
137+
ordered_result = list()
138+
for each in to_resolve_object_ids:
139+
ordered_result.append(result[result_to_resolve_object_ids.index(each)])
140+
return ordered_result
141+
142+
return graphene.List(
143+
base_type._type,
144+
description=get_field_description(field, registry),
145+
required=field.required,
146+
resolver=reference_resolver
147+
)
107148
return graphene.List(
108149
base_type._type,
109150
description=get_field_description(field, registry),
@@ -128,7 +169,7 @@ def convert_field_to_list(field, registry=None):
128169
return graphene.List(
129170
base_type,
130171
description=get_field_description(field, registry),
131-
required=field.required
172+
required=field.required,
132173
)
133174

134175

@@ -172,7 +213,8 @@ def reference_resolver(root, *args, **kwargs):
172213
return document.objects().no_dereference().only(*only_fields).get(pk=dereferenced["_ref"].id)
173214

174215
if isinstance(field, mongoengine.GenericReferenceField):
175-
return graphene.Field(_union, resolver=reference_resolver)
216+
return graphene.Field(_union, resolver=reference_resolver,
217+
description=get_field_description(field, registry))
176218

177219
return graphene.Field(_union)
178220

graphene_mongo/fields.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import graphene
77
import mongoengine
8+
from bson import DBRef
89
from graphene import Context
910
from graphene.utils.str_converters import to_snake_case
1011
from promise import Promise
@@ -61,6 +62,12 @@ def model(self):
6162
def order_by(self):
6263
return self.node_type._meta.order_by
6364

65+
@property
66+
def only_fields(self):
67+
if isinstance(self.node_type._meta.only_fields, str):
68+
return self.node_type._meta.only_fields.split(",")
69+
return list()
70+
6471
@property
6572
def registry(self):
6673
return getattr(self.node_type._meta, "registry", get_global_registry())
@@ -206,8 +213,10 @@ def get_queryset(self, model, info, only_fields=list(), **args):
206213
def default_resolver(self, _root, info, only_fields=list(), **args):
207214
args = args or {}
208215

209-
if _root is not None and getattr(_root, info.field_name, []) is not None:
210-
args["pk__in"] = [r.id for r in getattr(_root, info.field_name, [])]
216+
if _root is not None:
217+
field_name = to_snake_case(info.field_name)
218+
if getattr(_root, field_name, []) is not None:
219+
args["pk__in"] = [r.id for r in getattr(_root, field_name, [])]
211220

212221
connection_args = {
213222
"first": args.pop("first", None),
@@ -243,10 +252,13 @@ def default_resolver(self, _root, info, only_fields=list(), **args):
243252

244253
def chained_resolver(self, resolver, is_partial, root, info, **args):
245254
only_fields = list()
255+
for field in self.only_fields:
256+
if field in self.model._fields_ordered:
257+
only_fields.append(field)
246258
for field in get_query_fields(info):
247259
if to_snake_case(field) in self.model._fields_ordered:
248260
only_fields.append(to_snake_case(field))
249-
if root is None and (not bool(args) or not is_partial):
261+
if not bool(args) or not is_partial:
250262
if isinstance(self.model, mongoengine.Document) or isinstance(self.model,
251263
mongoengine.base.metaclasses.TopLevelDocumentMetaclass):
252264
args_copy = args.copy()
@@ -259,7 +271,13 @@ def chained_resolver(self, resolver, is_partial, root, info, **args):
259271
# XXX: Filter nested args
260272
resolved = resolver(root, info, **args)
261273
if resolved is not None:
262-
return resolved
274+
if isinstance(resolved, list):
275+
if resolved == list():
276+
return resolved
277+
elif not isinstance(resolved[0], DBRef):
278+
return resolved
279+
else:
280+
return resolved
263281
return self.default_resolver(root, info, only_fields, **args)
264282

265283
@classmethod

graphene_mongo/tests/test_relay_query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Query(graphene.ObjectType):
1717
reporter = graphene.Field(nodes.ReporterNode)
1818

1919
def resolve_reporter(self, *args, **kwargs):
20-
return models.Reporter.objects.first()
20+
return models.Reporter.objects.no_dereference().first()
2121

2222
query = """
2323
query ReporterQuery {

0 commit comments

Comments
 (0)