多字段连接
简介
在本教程中,您可以学习如何使用 PyMongo 构建聚合管道,对集合执行聚合,并通过完成和运行示例应用来打印结果。
此聚合执行多字段联接。 当您用来将文档匹配在一起的两个collection的文档中有多个相应字段时,就会发生多字段连接。聚合将这些文档的字段值进行匹配,并将两者的信息合并到一个文档中。
提示
一对多连接
一对多联接是多字段联接的一种变体。 执行一对多联接时,您可以从一个文档中选择一个字段,该字段与联接另一端的多个文档中的字段值匹配。 要学习;了解有关这些数据关系的详情,请参阅有关 一对多(数据模型) 和多对多(数据模型模型)的维基百科条目。
聚合任务摘要
本教程演示如何将描述产品信息的集合与描述客户订单的另一个集合中的数据组合起来。 结果显示 2020 年订购的产品列表,其中还包含每个订单的详细信息。
此示例使用两个集合:
products
,其中包含描述商店销售的产品的文档orders
,其中包含描述商店中产品的单个订单的文档
一份订单只能包含一种产品,因此聚合使用多字段联接将产品文档与表示该产品订单的文档进行匹配。 这些collection由products
collection中的文档中的name
和variation
字段连接,对应于orders
collection中的文档中的product_name
和product_variation
字段。
开始之前
在开始学习聚合教程之前,您必须设置一个新的 Python 应用。 您可以使用此应用连接到 MongoDB 部署,将示例数据插入 MongoDB,并运行每个教程中的聚合管道。
提示
要学习;了解如何安装驾驶员并连接到MongoDB,请参阅 PyMongo入门
安装驱动程序后,创建一个名为agg_tutorial.py
的文件。 将以下代码粘贴到此文件中,为聚合教程创建应用模板:
from pymongo import MongoClient # Replace the placeholder with your connection string. uri = "<connection string>" client = MongoClient(uri) try: agg_db = client["agg_tutorials_db"] # Get a reference to relevant collections. # ... some_coll = # ... another_coll = # Delete any existing documents in collections. # ... some_coll.delete_many({}) # Insert sample data into the collection or collections. # ... some_data = [...] # ... some_coll.insert_many(some_data) # Create an empty pipeline array. pipeline = [] # Add code to create pipeline stages. # ... pipeline.append({...}) # Run the aggregation. # ... aggregation_result = ... # Print the aggregation results. for document in aggregation_result: print(document) finally: client.close()
重要
在前面的代码中,请阅读代码注释,找到您必须根据教程进行修改的代码部分。
如果您尝试在不进行任何更改的情况下运行代码,则会遇到连接错误。
对于每个教程,您都必须将 连接string占位符替换为部署的 连接string 。 要学习;了解如何查找部署的连接string ,请参阅创建连接string 。
例如,如果连接字符串为 "mongodb+srv://mongodb-example:27017"
,则其赋值应如下所示:
uri = "mongodb+srv://mongodb-example:27017";
要在修改教程模板后运行完成的文件,请在 shell 中运行以下命令:
python3 agg_tutorial.py
设置应用后,通过将以下代码添加到应用程序来访问products
和orders
collection:
products_coll = agg_db["products"] orders_coll = agg_db["orders"]
删除所有现有数据,并将示例数据插入 products
集合,如以下代码所示。选择 Synchronous 或 Asynchronous标签页以查看相应的代码:
products_coll.delete_many({}) products_data = [ { "name": "Asus Laptop", "variation": "Ultra HD", "category": "ELECTRONICS", "description": "Great for watching movies" }, { "name": "Asus Laptop", "variation": "Standard Display", "category": "ELECTRONICS", "description": "Good value laptop for students" }, { "name": "The Day Of The Triffids", "variation": "1st Edition", "category": "BOOKS", "description": "Classic post-apocalyptic novel" }, { "name": "The Day Of The Triffids", "variation": "2nd Edition", "category": "BOOKS", "description": "Classic post-apocalyptic novel" }, { "name": "Morphy Richards Food Mixer", "variation": "Deluxe", "category": "KITCHENWARE", "description": "Luxury mixer turning good cakes into great" } ] products_coll.insert_many(products_data)
await products_coll.delete_many({}) products_data = [ { "name": "Asus Laptop", "variation": "Ultra HD", "category": "ELECTRONICS", "description": "Great for watching movies" }, { "name": "Asus Laptop", "variation": "Standard Display", "category": "ELECTRONICS", "description": "Good value laptop for students" }, { "name": "The Day Of The Triffids", "variation": "1st Edition", "category": "BOOKS", "description": "Classic post-apocalyptic novel" }, { "name": "The Day Of The Triffids", "variation": "2nd Edition", "category": "BOOKS", "description": "Classic post-apocalyptic novel" }, { "name": "Morphy Richards Food Mixer", "variation": "Deluxe", "category": "KITCHENWARE", "description": "Luxury mixer turning good cakes into great" } ] await products_coll.insert_many(products_data)
删除所有现有数据,并将样本数据插入orders
collection,如以下代码所示:
orders_coll.delete_many({}) order_data = [ { "customer_id": "[email protected]", "orderdate": datetime(2020, 5, 30, 8, 35, 52), "product_name": "Asus Laptop", "product_variation": "Standard Display", "value": 431.43 }, { "customer_id": "[email protected]", "orderdate": datetime(2019, 5, 28, 19, 13, 32), "product_name": "The Day Of The Triffids", "product_variation": "2nd Edition", "value": 5.01 }, { "customer_id": "[email protected]", "orderdate": datetime(2020, 1, 1, 8, 25, 37), "product_name": "Morphy Richards Food Mixer", "product_variation": "Deluxe", "value": 63.13 }, { "customer_id": "[email protected]", "orderdate": datetime(2020, 12, 26, 8, 55, 46), "product_name": "Asus Laptop", "product_variation": "Standard Display", "value": 429.65 } ] orders_coll.insert_many(order_data)
await orders_coll.delete_many({}) order_data = [ { "customer_id": "[email protected]", "orderdate": datetime(2020, 5, 30, 8, 35, 52), "product_name": "Asus Laptop", "product_variation": "Standard Display", "value": 431.43 }, { "customer_id": "[email protected]", "orderdate": datetime(2019, 5, 28, 19, 13, 32), "product_name": "The Day Of The Triffids", "product_variation": "2nd Edition", "value": 5.01 }, { "customer_id": "[email protected]", "orderdate": datetime(2020, 1, 1, 8, 25, 37), "product_name": "Morphy Richards Food Mixer", "product_variation": "Deluxe", "value": 63.13 }, { "customer_id": "[email protected]", "orderdate": datetime(2020, 12, 26, 8, 55, 46), "product_name": "Asus Laptop", "product_variation": "Standard Display", "value": 429.65 } ] await orders_coll.insert_many(order_data)
Tutorial
添加查找阶段以链接collection和导入字段
管道的第一个阶段是$lookup阶段,用于通过每个集合中的两个字段将orders
集合连接到products
集合。 查找阶段包含一个用于配置联接的嵌入式管道。
在嵌入式管道中,添加$match阶段以匹配联接两侧的两个字段的值。 请注意,以下代码使用创建$lookup
阶段时设置的name
和variation
字段的别名:
embedded_pl = [ { "$match": { "$expr": { "$and": [ {"$eq": ["$product_name", "$$prdname"]}, {"$eq": ["$product_variation", "$$prdvartn"]} ] } } } ]
在嵌入式管道中,添加另一个$match阶段以匹配2020中的订单:
embedded_pl.append({ "$match": { "orderdate": { "$gte": datetime(2020, 1, 1, 0, 0, 0), "$lt": datetime(2021, 1, 1, 0, 0, 0) } } })
在嵌入式管道中,添加$unset阶段以从联接的orders
集合端删除不需要的字段:
embedded_pl.append({ "$unset": ["_id", "product_name", "product_variation"] })
嵌入式管道完成后,将$lookup
阶段添加到主聚合管道。 配置此阶段以将处理后的查找字段存储在名为orders
的数组字段中:
pipeline.append({ "$lookup": { "from": "orders", "let": { "prdname": "$name", "prdvartn": "$variation" }, "pipeline": embedded_pl, "as": "orders" } })
为 2020 年订购的产品添加匹配阶段
接下来,添加一个$match阶段,以仅显示基于上一步中计算的orders
数组在2020中至少有一个订单的产品:
pipeline.append({ "$match": { "orders": {"$ne": []} } })
添加未设置阶段以删除不需要的字段
最后,添加一个$unset阶段。 $unset
阶段从结果文档中删除_id
和description
字段:
pipeline.append({ "$unset": ["_id", "description"] })
解释结果
聚合结果包含两个文档。 这些文档代表 2020 年有订单的产品。 每个文档都包含一个orders
数组字段,其中列出了该产品的每个订单的详细信息:
{ 'name': 'Asus Laptop', 'variation': 'Standard Display', 'category': 'ELECTRONICS', 'orders': [ { 'customer_id': '[email protected]', 'orderdate': datetime.datetime(2020, 5, 30, 8, 35, 52), 'value': 431.43 }, { 'customer_id': '[email protected]', 'orderdate': datetime.datetime(2020, 12, 26, 8, 55, 46), 'value': 429.65 } ] } { 'name': 'Morphy Richards Food Mixer', 'variation': 'Deluxe', 'category': 'KITCHENWARE', 'orders': [ { 'customer_id': '[email protected]', 'orderdate': datetime.datetime(2020, 1, 1, 8, 25, 37), 'value': 63.13 } ] }
结果文档包含orders
集合和products
集合中文档的详细信息,并由产品名称和变体连接起来。
要查看本教程的完整代码,请参阅 Completed Multi-field Join App 在Github 上。