{"id":345,"date":"2023-06-01T10:32:55","date_gmt":"2023-06-01T03:32:55","guid":{"rendered":"https:\/\/thoaicoder.com\/?p=345"},"modified":"2024-01-07T23:07:52","modified_gmt":"2024-01-07T16:07:52","slug":"data-unification-using-pyspark","status":"publish","type":"post","link":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/","title":{"rendered":"Data unification using PySpark"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">1. The need of data unification<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Data unification refers to the process of combining and merging data <strong>from multiple sources into a single, unified view<\/strong>. This process involves identifying and resolving inconsistencies and errors in the data and mapping and transforming data from different sources to ensure that it is consistent and usable. Data unification is a critical process of CRM, ERP, and CDP systems.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Imagine an organization, which would process customer information from multiple sources to generate insights (or <strong>customer 360-degree view<\/strong>), which would help their customers to take better business decisions and act on them, for example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Contact data source: store customer information on an on-premise database.<\/li>\n\n\n\n<li>Customer activity data source: store customers&#8217; behaviors and activities in an e-commerce platform.<\/li>\n\n\n\n<li>Payment data source: store customers&#8217; purchase history and sale funnel status in the Salesforce CRM platform.<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-4-1024x719.png\" alt=\"\" class=\"wp-image-398\" width=\"542\" height=\"380\" srcset=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-4-1024x719.png 1024w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-4-300x211.png 300w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-4-768x539.png 768w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-4.png 1028w\" sizes=\"auto, (max-width: 542px) 100vw, 542px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Data unification aims to <strong>create a single source of truth<\/strong> for an organization&#8217;s data, which can improve data quality, enable better decision-making, and reduce the time and effort needed to access and analyze data. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Data unification is definitely a Big Data task. There are a lot of challenges you will have to deal with in order to implement a unified data platform. For example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>When you deal with customer data, you must ensure security and compliance.<\/li>\n\n\n\n<li>When calculating the total amount that your customer has purchased, you must query your purchase transaction stored in a MySQL table containing more than 100 million records.<\/li>\n\n\n\n<li>The user probably does not have the time to wait for the computation to finish, they even prefer real-time data unification.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">These challenges are difficult to handle using usual programming techniques such as creating a Java service to query data from multiple data sources and unify them via application logic. That is why we should choose a unique platform like Apache Spark that supports integration with a large number of systems and has the power to process large volumes of data so you can unify a massive amount of business data. Let dive dig into Spark in the next chapter.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">2. Spark introduction<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">2.1. What is Apache Spark?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The major problem of Big data is about dealing with <strong>large computations<\/strong> and <strong>large volumes of data<\/strong>. One particularly challenging area is <strong>data processing<\/strong>. Single machines do not have enough power and resources to perform computations on huge amounts of information (or the user probably does not have the time to wait for the computation to finish). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>cluster<\/strong> (or group, of computers) pools the resources of many machines together, allowing us<br>to use all the cumulative resources as if they were a single computer. Now, a group of machines alone<br>is not powerful, you need a framework to coordinate work across them. <strong>Spark does just that,<br>managing and coordinating the execution of tasks on data across a cluster of computers (or nodes).<\/strong><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"434\" height=\"317\" src=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-3.png\" alt=\"\" class=\"wp-image-386\" srcset=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-3.png 434w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-3-300x219.png 300w\" sizes=\"auto, (max-width: 434px) 100vw, 434px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Apache Spark is a unified computing engine and a set of libraries for <strong>parallel data processing<\/strong> on<br>computer clusters. Spark supports multiple widely used programming languages (Python, Java, Scala, and R), and includes libraries for diverse tasks ranging from SQL to streaming and machine learning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Spark is a huge part of companies solving massive-scale data challenges, from<br>technology companies like Uber and Netflix using Spark\u2019s streaming and machine-learning tools, to<br>institutions like NASA, CERN, and the Broad Institute of MIT and Harvard applying Spark to<br>scientific data analysis<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2.2. Spark concepts<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">2.2.1. RDD<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Resilient Distributed Dataset (RDD) is the fundamental data structure of Spark (<strong>or<\/strong> <strong>the backbone of PySpark<\/strong>). They are immutable Distributed collections of objects of any type and <strong>can reside on multiple nodes<\/strong> which Spark uses to achieve Faster and more efficient&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/MapReduce\" target=\"_blank\" rel=\"noreferrer noopener\">MapReduce<\/a>&nbsp;operations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.2.2. DataFrame<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Spark has several core abstractions<strong>: Datasets, DataFrames, SQL Tables, and Resilient Distributed Datasets (RDDs)<\/strong>. These different abstractions all represent distributed collections of data. <strong>The easiest and most efficient are DataFrames, which are available in all languages.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A DataFrame is the most common Structured API and <strong>represents a table of data with rows and<br>columns<\/strong>. The list that defines the columns and the types within those columns is called the schema.<br><strong>You can think of a DataFrame as a spreadsheet with named columns.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The image below illustrates the fundamental difference: a spreadsheet sits on one computer in one specific location, whereas a Spark DataFrame can span thousands of computers. The reason for putting the data on more than one computer should be intuitive: either <strong>the data is too large to fit on one machine<\/strong> or it would simply take too long to perform that computation on one machine.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"789\" height=\"286\" src=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-5.png\" alt=\"\" class=\"wp-image-417\" srcset=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-5.png 789w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-5-300x109.png 300w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-5-768x278.png 768w\" sizes=\"auto, (max-width: 789px) 100vw, 789px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">2.2.3. Spark Session<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Since we control your Spark Application through <strong>a driver process<\/strong> called the SparkSession. The SparkSession instance is the way Spark executes user-defined manipulations across the cluster. There is a one-to-one correspondence between a SparkSession and a Spark Application. In Python, the variable is available as Spark when you start the console as below &#8211; a first Spark example locally.<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-tangaroa-background-color has-text-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>from pyspark import SparkConf\nfrom pyspark.sql import SparkSession\n\nconf = SparkConf().setMaster(\"spark:\/\/localhost:7077\").setAppName(\"Hello\").set(\n                \"spark.dynamicAllocation.enabled\", False).set(\n                \"spark.driver.maxResultSize\", 0)\n\nmy_spark = SparkSession \\\n    .builder \\\n    .config(conf=conf) \\\n    .getOrCreate()\n\nrange = my_spark.range(1000).toDF(\"number\")<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-pullquote has-text-align-left\"><blockquote><p>You just ran your first Spark code! We created a DataFrame with one column containing 1,000 rows with values from 0 to 999. This range of numbers represents a distributed collection. When run on a cluster, each part of this range of numbers exists on a different executor. This is a Spark DataFrame.<\/p><\/blockquote><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">2.2.4. Overview of Structured API Execution<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">How your code is actually executed across a Spark cluster? <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To execute code, we must write code. This code is then submitted to Spark either through the console<br>or via a submitted job. This code then passes through the <strong>Catalyst Optimizer<\/strong>, which decides how the<br>code should be executed (physical plan) and lays out a plan for doing so before, finally, the code is run and the result is returned to the user.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"780\" height=\"406\" src=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-7.png\" alt=\"\" class=\"wp-image-419\" srcset=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-7.png 780w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-7-300x156.png 300w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/image-7-768x400.png 768w\" sizes=\"auto, (max-width: 780px) 100vw, 780px\" \/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">3. Data unification using PySpark<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">3.1. Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We will create a sample project to demonstrate how to implement data unification using Spark. This project simulates a Customer Data Platform with three databases:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>nht_contact<\/strong>: a MongoDB database contains two collections:\n<ul class=\"wp-block-list\">\n<li><strong>contact<\/strong>: store customer information including firstName, lastName, email, phone, gender,&#8230;<\/li>\n\n\n\n<li><strong>contact<\/strong>_<strong>activity<\/strong>: score customer activities (click an email, access a website, send a message,&#8230;)<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>nht_payment<\/strong>: a MySQL database contains two tables\n<ul class=\"wp-block-list\">\n<li><strong>product<\/strong>: store product data<\/li>\n\n\n\n<li><strong><strong>transaction<\/strong><\/strong>: store transactions between customers and products.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>nht_unified_contact<\/strong>: a MongoDB database to store unified contact<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The requirement of this project is to implement a <strong>&#8220;unify service&#8221;<\/strong> which creates a single customer 360-degree view unified from the above <strong>nht_contact <\/strong>and <strong>nht_payment <\/strong>data sources. This service requires a <strong>&#8220;primary_key&#8221;<\/strong> parameter, a set of fields in <strong>nht_contact.contact<\/strong> collection to identify which customer attributes will be used to match the data, when merging the data the latest data will be prioritized. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Below is a data unification example with 3 contacts and their activities, and payment data. After unifying with primate_key = [email, first_name], two unified contacts will be created:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"539\" src=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-3-1024x539.png\" alt=\"\" class=\"wp-image-480\" srcset=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-3-1024x539.png 1024w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-3-300x158.png 300w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-3-768x404.png 768w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-3-1536x808.png 1536w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-3-2048x1077.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">PySpark will be used to interact with Spark cluster via Spark MongoDB Connector so you need to understand the basic fundamentals of Python programming API. Due to the complexity of building up Spark locally, we will use Docker to build up the entire local environment, so you need to install Docker on your local machine and understand the basic concept of Docker containerization. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The high-level architecture of this system is as below.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"616\" src=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-1024x616.png\" alt=\"\" class=\"wp-image-457\" srcset=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-1024x616.png 1024w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-300x181.png 300w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-768x462.png 768w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-1536x924.png 1536w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image.png 1921w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">3.2. Build up Spark locally with Docker<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We will use Docker Compose to build the local environment including:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A Spark cluster with two workers<\/li>\n\n\n\n<li>nht_contact Database (MongoDB)<\/li>\n\n\n\n<li>nht_payment Database (MySQL)<\/li>\n\n\n\n<li>unified_contact Database (MongoDB)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The unify service also needs to be containerized to Docker Image in order to run the entire system in Docker. Below is the content of the Dockerfile. Note that Spark requires Java runtime environment so we need to install Java as well.<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-tangaroa-background-color has-text-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>FROM python:3.8.2-buster\n\n# Install OpenJDK-11\nRUN apt-get update &amp;&amp; \\\n    apt-get install -y openjdk-11-jre-headless &amp;&amp; \\\n    apt-get clean;\n\n# Install PYTHON requirements\nWORKDIR \/srv\nADD .\/requirements.txt \/srv\/requirements.txt\nRUN pip install -r requirements.txt\nADD . \/srv\nCMD python \/srv\/app.py<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can containerize unify service to Dockeri mage with the below command.<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-tangaroa-background-color has-text-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>docker build -t unify-service .<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The docker-compose file is as below. We use the <strong>&#8220;depend_on&#8221;<\/strong> attribute to make sure the unify-service image runs after the other services are ready. We also create sample data to demonstrate the unification including:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>nht_contact.contact<\/strong>: 100 documents<\/li>\n\n\n\n<li><strong>nht_contact.contact_acitivy<\/strong>: 1000 documents<\/li>\n\n\n\n<li><strong>nht_payment.product<\/strong>: 18 records<\/li>\n\n\n\n<li><strong>nht_payment.transaction<\/strong>: 1000 transactions<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code has-white-color has-tangaroa-background-color has-text-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code>version: '3'\nservices:\n  spark-master:\n    image: bde2020\/spark-master:3.3.0-hadoop3.3\n    container_name: spark-master\n    ports:\n      - \"8083:8080\"\n      - \"7077:7077\"\n    environment:\n      - INIT_DAEMON_STEP=setup_spark\n  spark-worker-1:\n    image: bde2020\/spark-worker:3.3.0-hadoop3.3\n    container_name: spark-worker-1\n    depends_on:\n      - spark-master\n    ports:\n      - \"8084:8081\"\n    environment:\n      - \"SPARK_MASTER=spark:\/\/spark-master:7077\"\n  spark-worker-2:\n    image: bde2020\/spark-worker:3.3.0-hadoop3.3\n    container_name: spark-worker-2\n    depends_on:\n      - spark-master\n    ports:\n      - \"8085:8081\"\n    environment:\n      - \"SPARK_MASTER=spark:\/\/spark-master:7077\"\n  contact-mongo-db:\n    ports:\n      - '27017:27017'\n    container_name: contact-mongo-db\n    restart: always\n    logging:\n      options:\n        max-size: 1g\n    environment:\n       MONGO_INITDB_ROOT_USERNAME: nht\n       MONGO_INITDB_ROOT_PASSWORD: abc123\n       MONGO_INITDB_DATABASE: nht_contact\n    image: mongo:6.0-focal\n    volumes:\n      - .\/init_mongo_contact\/mongo-init.js:\/docker-entrypoint-initdb.d\/mongo-init.js:ro\n    healthcheck:\n      test: \"exit 0\"\n  unify-mongo-db:\n    ports:\n      - '27018:27017'\n    container_name: unify-mongo-db\n    restart: always\n    logging:\n      options:\n        max-size: 1g\n    environment:\n      MONGO_INITDB_ROOT_USERNAME: nht\n      MONGO_INITDB_ROOT_PASSWORD: abc123\n      MONGO_INITDB_DATABASE: nht_unified_contact\n    image: mongo:6.0-focal\n    volumes:\n      - .\/init_mongo_unify\/mongo-init.js:\/docker-entrypoint-initdb.d\/mongo-init.js:ro\n    healthcheck:\n      test: \"exit 0\"\n  payment-mysql-db:\n    container_name: payment-mysql-db\n    platform: linux\/x86_64\n    image: mysql:5.7\n    restart: always\n    environment:\n      MYSQL_USER: 'nht'\n      MYSQL_PASSWORD: 'abc123'\n      MYSQL_ROOT_PASSWORD: 'abc123'\n    ports:\n      - '3316:3306'\n    expose:\n      - '3306'\n    volumes:\n      - .\/init_mysql_payment\/:\/docker-entrypoint-initdb.d\n    healthcheck:\n      test: \"exit 0\"\n  unify-service:\n    image: unify\n    depends_on:\n      spark-worker-1:\n        condition: service_started\n      spark-worker-2:\n        condition: service_started\n      contact-mongo-db:\n        condition: service_healthy\n      unify-mongo-db:\n        condition: service_healthy\n      payment-mysql-db:\n        condition: service_healthy<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3.3. Project structure<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The project structure is quite simple and easy to understand. <strong>app.py<\/strong> is a Python application that implements the whole logic of the unification process and will be dived deeper into in the next chapters.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"446\" height=\"302\" src=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-1.png\" alt=\"\" class=\"wp-image-471\" srcset=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-1.png 446w, https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/07\/image-1-300x203.png 300w\" sizes=\"auto, (max-width: 446px) 100vw, 446px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">3.4. Load contact data to Spark data frame<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We will create the <strong>run_unify <\/strong>method to run the unification with a parameter is <strong>primary_key<\/strong> which is a list of contact fields we want to match.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We will create Spark Session with the Spark Master built up via Docker above. This Spark Session will connect to <strong>nht_contact <\/strong>databases as the input database and <strong>nht_unfied_contact <\/strong>as the output database using Spark Mongo Connector. Finally, we will load all documents in the &#8220;contact&#8221; collection into the Spark data frame to kick off the unification process.<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-tangaroa-background-color has-text-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code> def run_unify(primary_key):\n    conf = SparkConf().setMaster(\"spark:\/\/spark-master:7077\").setAppName(\"Unify\") \\\n        .set(\"spark.dynamicAllocation.enabled\", False).set(\"spark.driver.maxResultSize\", \"3g\").set(\n        \"spark.driver.memoryOverhead\", \"3g\").set(\"spark.executor.memory\", \"3g\")\n    # initiate Spark session with input is contact database and output is unified contact database\n    spark = SparkSession.builder \\\n        .config(\"spark.jars.packages\", \"org.mongodb.spark:mongo-spark-connector_2.12:3.0.1\") \\\n        .config(\"spark.mongodb.input.uri\",\n                \"mongodb:\/\/nht:abc123@contact-mongo-db:27017\/nht_contact.contact?connectTimeoutMS=10000&amp;authSource=admin&amp;authMechanism=SCRAM-SHA-256\") \\\n        .config(\"spark.mongodb.output.uri\",\n                \"mongodb:\/\/nht:abc123@unify-mongo-db:27017\/nht_unified_contact.unified_contact?connectTimeoutMS=10000&amp;authSource=admin&amp;authMechanism=SCRAM-SHA-256\") \\\n        .config(conf=conf) \\\n        .getOrCreate()\n\n    # load contact data to data frame\n    df = spark.read.format(\"com.mongodb.spark.sql.DefaultSource\").load(schema=contact_schema)\n...<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3.5. Unify contact data<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After loading contact data to Spark data frame, we can use the <strong>groupby <\/strong>function to group these contacts by primary_key (1).  Then we will load <strong>contact_activities<\/strong> data to a data frame because this collection is in the same Spark input database (2). Payment transaction data will be loaded via Python MySQL connector (3). Finally, we will perform the merge logic to ensure the latest data will be prioritized as well as combine contact_activies data, and payment data into the newly created unified contact (4).<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-tangaroa-background-color has-text-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code># 1. group contact data frame by primary key\ncolumns = &#91;func.collect_list(x).alias(x) for x in df.columns]\nraw_unified_df = df.groupby(primary_key).agg(*columns)\nunified_contacts = &#91;]\n\n# 2. load contact activities to data frame\ncontact_activities = spark.read.format(\"com.mongodb.spark.sql.DefaultSource\") \\\n        .option(\"collection\", \"contact_activity\") \\\n        .load()\n\n# 3. load transactions from payment MySQL database\ntransactions = get_transactions();\n\n# 4. merge data to create unified contact\nfor row in raw_unified_df.rdd.collect():\n     unified_contact = {}\n     for attr, columns in row.__dict__.items():\n         for column in columns:\n             if column == \"_id\":\n                 column_values = &#91;]\n                 for items in row&#91;column]:\n                     if items&#91;0] is not None:\n                         column_values.append(items&#91;0])\n                 # store id of contacts used to create this unified contact\n                 unified_contact&#91;\"contact_ids\"] = column_values\n             else:\n                 # merge data using latest value\n                 unified_contact&#91;column] = row&#91;column]&#91;-1]\n\n    enrich_contact_activities(contact_activities, unified_contact)\n    enrich_contact_payment(transactions, unified_contact)\n    unified_contacts.append(unified_contact)<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3.6. Store unified contacts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The newly created unified contacts need to be converted to a data frame in order to utilize Spark&#8217;s powerful. We use the repartition method to distribute the workload of storing contact data across Spark workers which is a very important step.<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-tangaroa-background-color has-text-color has-background\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\"><code># store unified contact to unified_contact database\nunified_contacts_df = spark.createDataFrame(unified_contacts,   unified_contact_schema)\nunified_contacts_df = unified_contacts_df.repartition(100)\nunified_contacts_df.write.format(\"mongo\").mode(\"append\").save()\nspark.stop()<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">4. Conclusion<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">We\u2019ve walked through the fundamental concepts of data unification as well as how data unification delivers&nbsp;value to the business side by creating a unified view of data from multiple sources. We&#8217;ve also looked at Spark&#8217;s introduction including Spark&#8217;s basic components and APIs to understand how Spark&#8217;s power can enable us to implement the data unification process.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Most noteworthy, we&#8217;ve implemented a comprehensive project to implement a simple unify_service which can be run in a container environment using Docker. It is always not an easy task due to the complexity of setting up Spark, especially building up Spark in the CI\/CD pipeline which is a very interesting topic to dive deeper into. Another state-of-the-art approach is using Spark in the cloud computing platform to reduce the development effort such as <a href=\"https:\/\/aws.amazon.com\/emr\/features\/spark\/\">AWS EMR<\/a>. The demo project can be found on&nbsp;<a href=\"https:\/\/github.com\/ThoaiNguyenHuu842\/data-unification-using-spark\">GitHub<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. The need of data unification Data unification refers to the process of combining and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":504,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[46,45,30,43,44],"class_list":["post-345","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-big-data","tag-big-data","tag-data-unification","tag-docker","tag-python","tag-spark"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.14 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Data unification using PySpark - I \u2764 Code<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Data unification using PySpark - I \u2764 Code\" \/>\n<meta property=\"og:description\" content=\"1. The need of data unification Data unification refers to the process of combining and [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/\" \/>\n<meta property=\"og:site_name\" content=\"I \u2764 Code\" \/>\n<meta property=\"article:published_time\" content=\"2023-06-01T03:32:55+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-01-07T16:07:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/Apache-Spark.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"555\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Thoai\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Thoai\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/\"},\"author\":{\"name\":\"Thoai\",\"@id\":\"https:\/\/thoaicoder.com\/#\/schema\/person\/a3304c209434f63c501b05622d7d2720\"},\"headline\":\"Data unification using PySpark\",\"datePublished\":\"2023-06-01T03:32:55+00:00\",\"dateModified\":\"2024-01-07T16:07:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/\"},\"wordCount\":1744,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/thoaicoder.com\/#\/schema\/person\/a3304c209434f63c501b05622d7d2720\"},\"keywords\":[\"Big data\",\"Data unification\",\"Docker\",\"Python\",\"Spark\"],\"articleSection\":[\"Big data\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/\",\"url\":\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/\",\"name\":\"Data unification using PySpark - I \u2764 Code\",\"isPartOf\":{\"@id\":\"https:\/\/thoaicoder.com\/#website\"},\"datePublished\":\"2023-06-01T03:32:55+00:00\",\"dateModified\":\"2024-01-07T16:07:52+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/thoaicoder.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Data unification using PySpark\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/thoaicoder.com\/#website\",\"url\":\"https:\/\/thoaicoder.com\/\",\"name\":\"I \u2764 Code\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/thoaicoder.com\/#\/schema\/person\/a3304c209434f63c501b05622d7d2720\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/thoaicoder.com\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/thoaicoder.com\/#\/schema\/person\/a3304c209434f63c501b05622d7d2720\",\"name\":\"Thoai\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/thoaicoder.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/03\/photo_2023-01-05_22-53-32.jpg\",\"contentUrl\":\"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/03\/photo_2023-01-05_22-53-32.jpg\",\"width\":640,\"height\":640,\"caption\":\"Thoai\"},\"logo\":{\"@id\":\"https:\/\/thoaicoder.com\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/thoaicoder.com\",\"https:\/\/www.linkedin.com\/in\/huu-thoai-nguyen\/\"],\"url\":\"https:\/\/thoaicoder.com\/index.php\/author\/admin_cvyxq4b8\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Data unification using PySpark - I \u2764 Code","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/","og_locale":"en_US","og_type":"article","og_title":"Data unification using PySpark - I \u2764 Code","og_description":"1. The need of data unification Data unification refers to the process of combining and [&hellip;]","og_url":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/","og_site_name":"I \u2764 Code","article_published_time":"2023-06-01T03:32:55+00:00","article_modified_time":"2024-01-07T16:07:52+00:00","og_image":[{"width":1200,"height":555,"url":"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/06\/Apache-Spark.jpg","type":"image\/jpeg"}],"author":"Thoai","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Thoai","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/#article","isPartOf":{"@id":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/"},"author":{"name":"Thoai","@id":"https:\/\/thoaicoder.com\/#\/schema\/person\/a3304c209434f63c501b05622d7d2720"},"headline":"Data unification using PySpark","datePublished":"2023-06-01T03:32:55+00:00","dateModified":"2024-01-07T16:07:52+00:00","mainEntityOfPage":{"@id":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/"},"wordCount":1744,"commentCount":0,"publisher":{"@id":"https:\/\/thoaicoder.com\/#\/schema\/person\/a3304c209434f63c501b05622d7d2720"},"keywords":["Big data","Data unification","Docker","Python","Spark"],"articleSection":["Big data"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/","url":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/","name":"Data unification using PySpark - I \u2764 Code","isPartOf":{"@id":"https:\/\/thoaicoder.com\/#website"},"datePublished":"2023-06-01T03:32:55+00:00","dateModified":"2024-01-07T16:07:52+00:00","breadcrumb":{"@id":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/thoaicoder.com\/index.php\/2023\/06\/01\/data-unification-using-pyspark\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/thoaicoder.com\/"},{"@type":"ListItem","position":2,"name":"Data unification using PySpark"}]},{"@type":"WebSite","@id":"https:\/\/thoaicoder.com\/#website","url":"https:\/\/thoaicoder.com\/","name":"I \u2764 Code","description":"","publisher":{"@id":"https:\/\/thoaicoder.com\/#\/schema\/person\/a3304c209434f63c501b05622d7d2720"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/thoaicoder.com\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/thoaicoder.com\/#\/schema\/person\/a3304c209434f63c501b05622d7d2720","name":"Thoai","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/thoaicoder.com\/#\/schema\/person\/image\/","url":"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/03\/photo_2023-01-05_22-53-32.jpg","contentUrl":"https:\/\/thoaicoder.com\/wp-content\/uploads\/2023\/03\/photo_2023-01-05_22-53-32.jpg","width":640,"height":640,"caption":"Thoai"},"logo":{"@id":"https:\/\/thoaicoder.com\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/thoaicoder.com","https:\/\/www.linkedin.com\/in\/huu-thoai-nguyen\/"],"url":"https:\/\/thoaicoder.com\/index.php\/author\/admin_cvyxq4b8\/"}]}},"_links":{"self":[{"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/posts\/345","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/comments?post=345"}],"version-history":[{"count":68,"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/posts\/345\/revisions"}],"predecessor-version":[{"id":505,"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/posts\/345\/revisions\/505"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/media\/504"}],"wp:attachment":[{"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/media?parent=345"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/categories?post=345"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thoaicoder.com\/index.php\/wp-json\/wp\/v2\/tags?post=345"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}