【问题标题】:join two json in Google Cloud Platform with dataflow使用数据流在 Google Cloud Platform 中加入两个 json
【发布时间】:2017-07-24 18:24:28
【问题描述】:

我想从两个不同的 JSON 文件中只找出女性员工,只选择我们感兴趣的字段并将输出写入另一个 JSON。

我也在尝试使用 Dataflow 在 Google 的云平台中实现它。有人可以提供任何可以实现以获得结果的示例 Java 代码。

员工 JSON

{"emp_id":"OrgEmp#1","emp_name":"Adam","emp_dept":"OrgDept#1","emp_country":"USA","emp_gender":"female","emp_birth_year":"1980","emp_salary":"$100000"}
{"emp_id":"OrgEmp#1","emp_name":"Scott","emp_dept":"OrgDept#3","emp_country":"USA","emp_gender":"male","emp_birth_year":"1985","emp_salary":"$105000"}

部门 JSON

{"dept_id":"OrgDept#1","dept_name":"Account","dept_start_year":"1950"}
{"dept_id":"OrgDept#2","dept_name":"IT","dept_start_year":"1990"}
{"dept_id":"OrgDept#3","dept_name":"HR","dept_start_year":"1950"}

预期的输出 JSON 文件应该是这样的

{"emp_id":"OrgEmp#1","emp_name":"Adam","dept_name":"Account","emp_salary":"$100000"}

【问题讨论】:

    标签: python json google-cloud-dataflow apache-beam


    【解决方案1】:

    如果您的部门集合明显较小,您可以使用CoGroupByKey(将使用随机播放)或使用边输入来执行此操作。

    我会给你 Python 代码,但你可以在 Java 中使用相同的管道。


    使用侧面输入,您将:

    1. 将您的部门 PCollection 转换为映射 dept_id 到部门 JSON 字典。

    2. 然后你拿 employees PCollection 作为主要输入,您可以在其中使用 dept_id 获取部门 PCollection 中每个部门的 JSON。

    像这样:

    departments = (p | LoadDepts()
                     | 'key_dept' >> beam.Map(lambda dept: (dept['dept_id'], dept)))
    
    deps_si = beam.pvalue.AsDict(departments)
    
    employees = (p | LoadEmps())
    
    def join_emp_dept(employee, dept_dict):
      return employee.update(dept_dict[employee['dept_id']])
    
    joined_dicts = employees | beam.Map(join_dicts, dept_dict=deps_si)
    

    使用CoGroupByKey,您可以使用dept_id 作为键来对两个集合进行分组。这将产生一个键值对的 PCollection,其中键是 dept_id,值是部门和该部门员工的两个可迭代对象。

    departments = (p | LoadDepts()
                   | 'key_dept' >> beam.Map(lambda dept: (dept['dept_id'], dept)))
    
    employees = (p | LoadEmps()
                   | 'key_emp' >> beam.Map(lambda emp: (emp['dept_id'], emp)))
    
    def join_lists((k, v)):
      itertools.product(v['employees'], v['departments'])
    
    joined_dicts = (
        {'employees': employees, 'departments': departments} 
        | beam.CoGroupByKey()    
        | beam.FlatMap(join_lists)
        | 'mergedicts' >> beam.Map(lambda (emp_dict, dept_dict): emp_dict.update(dept_dict))
        | 'filterfields'>> beam.Map(filter_fields)
    )
    

    【讨论】:

    • 请注意,使用侧输入可能是更好的选择,因为 Pablo 提到部门集合可能小于员工集合。
    • 巴勃罗 - 感谢您的回复。您能否对您提到的步骤提供一个简单的解释。因为我是这个领域的新手,所以一个小的解释会有所帮助。
    • 我已经添加了解释。另外,我将侧输入解决方案上移了,所以你会先考虑那个。
    • 如果答案有用,可以选择Koushik,这样上面的每个人都可以使用。
    • @Pablo 非常感谢你,一直坚持加入,但阅读你的示例后,我想出了更多关于如何在 5 分钟内解决它的问题,而不是我昨天在 5 小时内所做的。你应该得到“解决”、点赞和爱
    【解决方案2】:

    有人要求为这个问题提供基于 Java 的解决方案。这是用于此的 Java 代码。它更冗长,但本质上是相同的。

    // First we want to load all departments, and put them into a PCollection
    // of key-value pairs, where the Key is their identifier. We assume that it is String-type.
    PCollection<KV<String, Department>> departments = 
        p.apply(new LoadDepts())
         .apply("getKey", MapElements.via((Department dept) -> KV.of(dept.getId(), dept)));
    
    // We then convert this PCollection into a map-type PCollectionView.
    // We can access this map directly within a ParDo.
    PCollectionView<Map<String, Department>> departmentSideInput = 
        departments.apply("ToMapSideInput", View.<String, Department>asMap());
    
    // We load the PCollection of employees
    PCollection<Employee> employees = p.apply(new LoadEmployees());
    
    // Let us suppose that we will *extend* an employee information with their
    // Department information. I have assumed the existence of an ExtendedEmployee
    // class to represent an employee extended with department information.
    class JoinDeptEmployeeDoFn extends DoFn<Employee, ExtendedEmployee> {
    
      @ProcessElement
      public void processElement(ProcessContext c) {
        // We obtain the Map-type side input with department information.
        Map<String, Department> departmentMap = c.sideInput(departmentSideInput);
        Employee empl = c.element();
        Department dept = departmentMap.get(empl.getDepartmentId(), null);
        if (department == null) return;
    
        ExtendedEmployee result = empl.extendWith(dept);
        c.output(result);
      }
    
    }
    
    // We apply the ParDo to extend the employee with department information
    // and specify that it takes in a departmentSideInput.
    PCollection<ExtendedEmployee> extendedEmployees = 
        employees.apply(
            ParDo.of(new JoinDeptEmployeeDoFn()).withSideInput(departmentSideInput));
    

    使用 CoGroupByKey,您可以使用 dept_id 作为键来对两个集合进行分组。这在 Beam Java SDK 中的外观是 CoGbkResult

    // We load the departments, and make them a key-value collection, to Join them
    // later with employees.
    PCollection<KV<String, Department>> departments = 
        p.apply(new LoadDepts())
         .apply("getKey", MapElements.via((Department dept) -> KV.of(dept.getId(), dept)));
    
    // Because we will perform a join, employees also need to be put into
    // key-value pairs, where their key is their *department id*.
    PCollection<KV<String, Employee>> employees = 
        p.apply(new LoadEmployees())
         .apply("getKey", MapElements.via((Employee empl) -> KV.of(empl.getDepartmentId(), empl)));
    
    // We define a DoFn that is able to join a single department with multiple
    // employees.
    class JoinEmployeesWithDepartments extends DoFn<KV<String, CoGbkResult>, ExtendedEmployee> {
      @ProcessElement
      public void processElement(ProcessContext c) {
        KV<...> elm = c.element();
        // We assume one department with the same ID, and assume that
        // employees always have a department available.
        Department dept = elm.getValue().getOnly(departmentsTag);
        Iterable<Employee> employees = elm.getValue().getAll(employeesTag);
    
        for (Employee empl : employees) {
          ExtendedEmployee result = empl.extendWith(dept);
          c.output(result);
        }
      }
    }
    
    // The syntax for a CoGroupByKey operation is a bit verbose.
    // In this step we define a TupleTag, which serves as identifier for a
    // PCollection.
    final TupleTag<String> employeesTag = new TupleTag<>();
    final TupleTag<String> departmentsTag = new TupleTag<>();
    
    // We use the PCollection tuple-tags to join the two PCollections.
    PCollection<KV<String, CoGbkResult>> results =
        KeyedPCollectionTuple.of(departmentsTag, departments)
            .and(employeesTag, employees)
            .apply(CoGroupByKey.create());
    
    // Finally, we convert the joined PCollections into a kind that
    // we can use: ExtendedEmployee.
    PCollection<ExtendedEmployee> extendedEmployees =
        results.apply("ExtendInformation", ParDo.of(new JoinEmployeesWithDepartments()));
    

    【讨论】:

    • 如果您希望我进一步澄清这一点,请随时提出问题。
    猜你喜欢
    • 2019-06-02
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    • 2020-07-23
    • 1970-01-01
    相关资源
    最近更新 更多