diff --git a/mpcontribs-api/mpcontribs/api/contributions/document.py b/mpcontribs-api/mpcontribs/api/contributions/document.py index 55c9e204b..c502d673c 100644 --- a/mpcontribs-api/mpcontribs/api/contributions/document.py +++ b/mpcontribs-api/mpcontribs/api/contributions/document.py @@ -163,7 +163,32 @@ class Contributions(DynamicDocument): ReferenceField("Attachments", null=True), default=list, max_length=10 ) notebook = ReferenceField("Notebooks") - atlas = AtlasManager("formula_autocomplete") + atlas = AtlasManager( + "formula_autocomplete", + definition={ + "analyzer": "lucene.whitespace", + "searchAnalyzer": "lucene.whitespace", + "mappings": { + "dynamic": False, + "fields": { + "formula": {"type": "string"}, + "identifier": {"type": "string"}, + "is_public": {"type": "boolean"}, + "project": [{"type": "stringFacet"}, {"type": "string"}], + }, + }, + "storedSource": { + "include": [ + "formula", + "identifier", + "is_public", + "last_modified", + "needs_build", + "project", + ] + }, + }, + ) meta = { "collection": "contributions", "indexes": [ @@ -183,39 +208,8 @@ class Contributions(DynamicDocument): @queryset_manager def objects(doc_cls, queryset): - return queryset.no_dereference().only( - "project", - "identifier", - "formula", - "is_public", - "last_modified", - "needs_build", - ) - - @classmethod - def atlas_filter(cls, term): - try: - comp = Composition(term) - except Exception: - raise ValueError(f"{term} is not a valid composition") - - try: - for element in comp.elements: - Element(element) - except Exception: - raise ValueError(f"{element} not a valid element") - - ind_str = [] - - if len(comp) == 1: - d = comp.get_integer_formula_and_factor() - ind_str.append(d[0] + str(int(d[1])) if d[1] != 1 else d[0]) - else: - for i, j in comp.reduced_composition.items(): - ind_str.append(i.name + str(int(j)) if j != 1 else i.name) - - final_terms = ["".join(entry) for entry in permutations(ind_str)] - return AtlasQ(formula=final_terms[0]) # TODO formula__in=final_terms + only = doc_cls.atlas.definition["storedSource"]["include"] + return queryset.no_dereference().only(*only) @classmethod def post_init(cls, sender, document, **kwargs): diff --git a/mpcontribs-api/mpcontribs/api/contributions/views.py b/mpcontribs-api/mpcontribs/api/contributions/views.py index fe4e941b5..2d113228c 100644 --- a/mpcontribs-api/mpcontribs/api/contributions/views.py +++ b/mpcontribs-api/mpcontribs/api/contributions/views.py @@ -169,53 +169,3 @@ def has_add_permission(self, req, obj): raise Unauthorized(f"{obj.identifier} already added for {obj.project.id}") return True - - -@contributions.route("/search") -def search(): - formula = request.args.get("formula") - if not formula: - abort(404, description="Missing formula param.") - - try: - comp = Composition(formula) - except (CompositionError, ValueError): - abort(400, description="Invalid formula provided.") - - ind_str = [] - - if len(comp) == 1: - d = comp.get_integer_formula_and_factor() - ind_str.append(d[0] + str(int(d[1])) if d[1] != 1 else d[0]) - else: - for i, j in comp.reduced_composition.items(): - ind_str.append(i.name + str(int(j)) if j != 1 else i.name) - - final_terms = ["".join(entry) for entry in permutations(ind_str)] - limit = request.args.get("limit", ContributionsResource.default_limit) - - pipeline = [ - { - "$search": { - "index": "formula_autocomplete", - "text": {"path": "formula", "query": final_terms}, - } - }, - {"$project": {"formula": 1, "length": {"$strLenCP": "$formula"}, "project": 1}}, - {"$match": {"length": {"$gte": len(final_terms[0])}}}, - {"$limit": limit}, - {"$sort": {"length": 1}}, - ] - - results = [] - - for contrib in Contributions.objects().aggregate(pipeline): - results.append( - { - "id": str(contrib["_id"]), - "formula": contrib["formula"], - "project": contrib["project"], - } - ) - - return jsonify(results) diff --git a/mpcontribs-api/mpcontribs/api/core.py b/mpcontribs-api/mpcontribs/api/core.py index 6524d099f..1808b7900 100644 --- a/mpcontribs-api/mpcontribs/api/core.py +++ b/mpcontribs-api/mpcontribs/api/core.py @@ -581,8 +581,18 @@ def has_read_permission(self, request, qs): return qs.none() else: names = None - if q and "project" in q and "$in" in q["project"]: - names = q.pop("project").pop("$in") + if hasattr(qs._query_obj, "children"): + children = deepcopy(qs._query_obj.children) + else: + children = [deepcopy(qs._query_obj)] + + qs._query_obj = Q() + for node in children: + for field, value in node.query.items(): + if field == "project__in": + names = value + else: + qs = qs.filter(**{field: value}) qfilter = self.get_projects_filter( username, groups, filter_names=names @@ -610,15 +620,20 @@ def has_read_permission(self, request, qs): qfilter = self.get_projects_filter(username, groups) component = component[:-1] if component == "notebooks" else component qfilter &= Q(**{f"{component}__in": ids}) - contribs = Contributions.objects(qfilter).only(component).limit(len(ids)) + contribs = ( + Contributions.objects(qfilter).only(component).limit(len(ids)) + ) # return new queryset using "ids__in" - readable_ids = [ - getattr(contrib, component).id for contrib in contribs - ] if component == "notebook" else [ - dbref.id for contrib in contribs - for dbref in getattr(contrib, component) - if dbref.id in ids - ] + readable_ids = ( + [getattr(contrib, component).id for contrib in contribs] + if component == "notebook" + else [ + dbref.id + for contrib in contribs + for dbref in getattr(contrib, component) + if dbref.id in ids + ] + ) if not readable_ids: return qs.none()