spark:计算订单中所有商品是否属于套包

需求

计算订单中,所包含的商品(单品)是否可以组成套包,且得出这个套包在这个订单中的个数。

即:假设某套包的商品为(a,b,c),某订单中购买了(a,b,c,d,a,b,c)五个商品,则这个订单中包含2个这个套包。

之前是将套包维度表和订单事实表进行笛卡尔积,然后用hive的UDF函数计算,但是在实际使用过程中发现速度很慢。了解到spark 2.0之后的第二代钨丝计划对spark内部函数进行了大量优化,可能性能会比hive的UDF函数好点。考虑进行重构,使用spark的UDF函数进行计算。再此之前先使用DF的map算子进行计算。

思路

经过处理的订单表内容包含订单ID字段和购买商品字段,购买商品字段包含该订单所买的所有商品,以逗号隔开,内容大致如下:

Order_ID           Order_Items

     1                  a,b,c,d,a,b,c,d
     2                  q,w,e,r
     3                  a,a,s,d,f

套包维度表与订单表相同,包含套包ID和套包内商品:

 Head_ID         Head_items    

     1                  a,b,c,d
     2                  q,w,e
     3                  q,a,s,d

总的来说,这个需求就是求数组A是否包含数组B,并求出包含几个数组B的问题。

  1. 将订单表和套包维度表笛卡尔积。
  2. 对笛卡尔积的每条数据进行计算。
    1. 将两张表的items已逗号分隔,转换成ArrayBuffer。
    2. 判断Order的items个数是否大于Head的items个数,如果大于或等于,进入3;否则不可能包含Head的套包,结束。
    3. 循环判断Head的每个item是否存在Order的items中,如果存在,则删除Order中的该item,并继续判断,直到Head的最后一个Item也存在Order中,则count加1,并返回2;如果不存在,则说明Order中不可能包含Head的所有商品,结束。

实现

1.定义样例类。

package com.zixuan.spark.bean

case class HD(hid:String,hitem:String)
case class Order(oid:String,oitem:String)
case class Result(hid:String,hitem:String,oid:String,oitem:String,result: Int)

2.spark代码

package com.zixuan.spark.test

import com.zixuan.spark.bean.{HD, Order, Result}
import org.apache.spark.sql.SparkSession
import org.apache.spark.{SparkConf, SparkContext}

object BomCount {
  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("test").setMaster("local[1]")
    val context = new SparkContext(conf)
    //屏蔽spark的info warn信息
    context.setLogLevel("ERROR")
    val session = SparkSession.builder().config(conf).getOrCreate()
    //导入转换DF的隐式转换
    import session.implicits._

    val hd =
      context.textFile("C:\\Users\\zixuan.wu\\Desktop\\sparkTest\\hd.txt")
        .map(s=>s.split(" "))
        .map(strings=>HD(strings(0),strings(1)))
        .toDF()

    val order =
      context.textFile("C:\\Users\\zixuan.wu\\Desktop\\sparkTest\\order.txt")
        .map(s=>s.split(" "))
        .map(strings=>Order(strings(0),strings(1)))
        .toDF()
    //val hd = session.sql("select * from xxx")
    //val order = session.sql("select * from xxx")
    //val order_product = order.groupBy("order_id","product_id")

    //笛卡尔积
    val frame = hd.crossJoin(order)
    frame.show()

    //主要计算逻辑
    val result = frame.map(
      row => {
        //将items以逗号切割,并转换成buffer
        var hitems = row.getAs[String]("hitem").split(",").toBuffer
        var oitems = row.getAs[String]("oitem").split(",").toBuffer
        //初始化count
        var count = 0
        //while循环控制标记
        var continueFlag = 1
        //导入scala的break包
        import scala.util.control._
        
        //如果oitem的个数大于等于hitem且continueFlag标记为1,则进入循环
        while (oitems.length >= hitems.length && continueFlag==1){
          //默认不继续while循环,如果oitem中包含hitem再开启循环
          continueFlag = 0
          //break
          val loop = new Breaks
          loop.breakable(
            //遍历hitems,如果oitems中包含hitem,则移除oitems中的这个item,并将continueFlag置为1;且如果这个item时hitems中的最后一个,则count+1
            //否则退出循环;
            for(hitem <- hitems){
              if (oitems.contains(hitem)){
                oitems.remove(oitems.indexOf(hitem))
                continueFlag=1
                if (hitem.equals(hitems.last)){
                  count+=1
                }
              }
              else {
                loop.break()
              }
            }
          )
        }
        //返回result
        Result(row.getString(0), row.getString(1), row.getString(2), row.getString(3), count)
      }
    )
    result.show()
  }
}

3.结果

笛卡尔积的结果:

计算后的结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值