From eaeb89762f1ec3cb6f7e586d263ddec04f9df2e9 Mon Sep 17 00:00:00 2001 From: MohammadMahdi Khalaj Date: Mon, 6 Nov 2023 08:16:22 +0330 Subject: [PATCH] Update README.md --- README.md | 224 +++++++++++++++++++++++++++++------------------ aggify/aggify.py | 38 ++++---- 2 files changed, 160 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index ab8ddc0..d695dc4 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ Here's a code snippet that demonstrates how to use Aggify to construct a MongoDB ```python from mongoengine import Document, fields - class AccountDocument(Document): username = fields.StringField() display_name = fields.StringField() @@ -56,105 +55,164 @@ class AccountDocument(Document): deleted_at = fields.LongField() banned_at = fields.LongField() -class PostDocument(Document): - owner = fields.ReferenceField('AccountDocument', db_field='owner_id') - caption = fields.StringField() - location = fields.StringField() - comment_disabled = fields.BooleanField() - stat_disabled = fields.BooleanField() - hashtags = fields.ListField() - archived_at = fields.LongField() - deleted_at = fields.LongField() +class FollowAccountEdge(Document): + start = fields.ReferenceField("AccountDocument") + end = fields.ReferenceField("AccountDocument") + accepted = fields.BooleanField() + meta = { + "collection": "edge.follow.account", + } + +class BlockEdge(Document): + start = fields.ObjectIdField() + end = fields.ObjectIdField() + meta = { + "collection": "edge.block", + } ``` Aggify query: ```python -from aggify import Aggify, Q, F - -query = Aggify(PostDocument) - -query.filter(deleted_at=None, caption__contains='Aggify').order_by('-_id').lookup( - AccountDocument, query=[ - Q(_id__exact='owner') & Q(deleted_at=None), - Q(is_verified__exact=True) - ], let=['owner'], as_name='owner' - ).filter(owner__ne=[]).add_fields({ - "aggify": "Aggify is lovely", - } - ).project(caption=0).out("post").pipelines +from models import * +from aggify import Aggify, F, Q +from bson import ObjectId + +aggify = Aggify(AccountDocument) + +pipelines = list( + ( + aggify.filter( + phone__in=[], + id__ne=ObjectId(), + disabled_at=None, + banned_at=None, + deleted_at=None, + network_id=ObjectId(), + ) + .lookup( + FollowAccountEdge, + let=["id"], + query=[Q(start__exact=ObjectId()) & Q(end__exact="id")], + as_name="followed", + ) + .lookup( + BlockEdge, + let=["id"], + as_name="blocked", + query=[ + (Q(start__exact=ObjectId()) & Q(end__exact="id")) + | (Q(end__exact=ObjectId()) & Q(start__exact="id")) + ], + ) + .filter(followed=[], blocked=[]) + .group("username") + .annotate(annotate_name="phone", accumulator="first", f=F("phone") + 10) + .redact( + value1="phone", + condition="==", + value2="132", + then_value="keep", + else_value="prune", + ) + .project(username=0)[5:10] + .out(coll="account") + ) +) ``` Mongoengine equivalent query: ```python [ - { - '$match': { - 'caption': { - '$options': 'i', - '$regex': '.*Aggify.*' - }, - 'deleted_at': None - } - }, - { - '$sort': { - '_id': -1 - } - }, - { - '$lookup': { - 'as': 'owner', - 'from': 'account', - 'let': { - 'owner': '$owner_id' - }, - 'pipeline': [ - { - '$match': { - '$expr': { - '$and': [ - { - '$eq': ['$_id', '$$owner'] - }, - { - 'deleted_at': None - } - ] - } + { + "$match": { + "phone": {"$in": []}, + "_id": {"$ne": ObjectId("65486eae04cce43c5469e0f1")}, + "disabled_at": None, + "banned_at": None, + "deleted_at": None, + "network_id": ObjectId("65486eae04cce43c5469e0f2"), + } + }, + { + "$lookup": { + "from": "edge.follow.account", + "let": {"id": "$_id"}, + "pipeline": [ + { + "$match": { + "$expr": { + "$and": [ + { + "$eq": [ + "$start", + ObjectId("65486eae04cce43c5469e0f3"), + ] + }, + {"$eq": ["$end", "$$id"]}, + ] } - }, - { - '$match': { - '$expr': { - '$eq': ['$is_verified', True] - } + } + } + ], + "as": "followed", + } + }, + { + "$lookup": { + "from": "edge.block", + "let": {"id": "$_id"}, + "pipeline": [ + { + "$match": { + "$expr": { + "$or": [ + { + "$and": [ + { + "$eq": [ + "$start", + ObjectId("65486eae04cce43c5469e0f4"), + ] + }, + {"$eq": ["$end", "$$id"]}, + ] + }, + { + "$and": [ + { + "$eq": [ + "$end", + ObjectId("65486eae04cce43c5469e0f5"), + ] + }, + {"$eq": ["$start", "$$id"]}, + ] + }, + ] } } - ] - } - }, - { - '$match': { - 'owner': {'$ne': []} - } - }, - { - '$addFields': { - 'aggify': { - '$literal': 'Aggify is lovely' } + ], + "as": "blocked", + } + }, + {"$match": {"followed": [], "blocked": []}}, + {"$group": {"_id": "$username", "phone": {"$first": {"$add": ["$phone", 10]}}}}, + { + "$redact": { + "$cond": { + "if": {"$eq": ["phone", "132"]}, + "then": "$$KEEP", + "else": "$$PRUNE", } - }, - { - '$project': { - 'caption': 0 - } - }, - { - '$out': 'post' } + }, + {"$project": {"username": 0}}, + {"$skip": 5}, + {"$limit": 5}, + {"$out": "account"}, ] ``` diff --git a/aggify/aggify.py b/aggify/aggify.py index a2623c0..28db214 100644 --- a/aggify/aggify.py +++ b/aggify/aggify.py @@ -252,8 +252,8 @@ def __to_aggregate(self, query: Dict[str, Any]) -> None: or "document_type_obj" not in join_field.__dict__ # Check whether this field is a join field or not. or issubclass( - join_field.document_type, EmbeddedDocument # noqa - ) # Check whether this field is embedded field or not + join_field.document_type, EmbeddedDocument # noqa + ) # Check whether this field is embedded field or not or len(split_query) == 1 or (len(split_query) == 2 and split_query[1] in Operators.ALL_OPERATORS) ): @@ -555,23 +555,23 @@ def lookup( foreign_field: Union[str, None] = None, ) -> "Aggify": """ - Generates a MongoDB lookup pipeline stage. - - Args: - from_collection (Document): The document representing the collection to perform the lookup on. - as_name (str): The name of the new field to create. - query (list[Q] | Union[Q, None], optional): List of desired queries with Q function or a single query. - let (Union[List[str], None], optional): The local field(s) to join on. If provided, - localField and foreignField are not used. - local_field (Union[str, None], optional): The local field to join on when `let` is not provided. - foreign_field (Union[str, None], optional): The foreign field to join on when `let` is not provided. - let (Union[List[str], None], optional): The local field(s) to join on. If provided, - localField and foreignField are not used. - local_field (Union[str, None], optional): The local field to join on when let not provided. - foreign_field (Union[str, None], optional): The foreign field to join on when let not provided. - - Returns: - Aggify: An instance of the Aggify class representing a MongoDB lookup pipeline stage. + Generates a MongoDB lookup pipeline stage. + + Args: + from_collection (Document): The document representing the collection to perform the lookup on. + as_name (str): The name of the new field to create. + query (list[Q] | Union[Q, None], optional): List of desired queries with Q function or a single query. + let (Union[List[str], None], optional): The local field(s) to join on. If provided, + localField and foreignField are not used. + local_field (Union[str, None], optional): The local field to join on when `let` is not provided. + foreign_field (Union[str, None], optional): The foreign field to join on when `let` is not provided. + let (Union[List[str], None], optional): The local field(s) to join on. If provided, + localField and foreignField are not used. + local_field (Union[str, None], optional): The local field to join on when let not provided. + foreign_field (Union[str, None], optional): The foreign field to join on when let not provided. + + Returns: + Aggify: An instance of the Aggify class representing a MongoDB lookup pipeline stage. """ lookup_stages = []