通过推荐过滤功能实现Feed流个性化过滤

本文将向您介绍如何使用推荐过滤功能,实现推荐场景的按需定制,满足用户的特定浏览喜好。

一、背景介绍

在某些猜你喜欢场景当中,用户可能会出于需求或自身喜好与经验,只想要看到属于特定品牌等拥有特定属性的物品,此时可以使用推荐过滤功能,只要在客户端埋点对应选择逻辑,在服务端SDK请求推荐结果时加上要筛选的条件,即可获取到希望返回的指定类型(比如指定类别、标签、城市、作者等等)的物品。

实现场景:

以下为使用场景

例1:

当用户在浏览民宿、酒店时,对地点等属性有硬性需求,此时就可以选择对应需求的tag来筛选推荐结果,让用户可以更加方便快捷的找到心仪的物品。(此功能同样也可适用于行为数据较少的新用户,通过用户自身选择来快速锁定用户喜好。)

推荐过滤1

例2:

新品活动页面筛选用户喜好品牌的活动产品:

推荐过滤2

用户在浏览新品推荐的场景时,想要对自己感兴趣的品牌进行过滤筛选。

可以看到,该功能可在部分已有场景(如新品、活动商品)的基础上,叠加用户根据自己喜好选择的过滤条件,形成更加灵活、细分、可自由定制的个性化推荐结果。

二、使用方法

推荐过滤功能目前没有控制台设置页面,因为是在已有推荐场景的基础上增加对物品属性的过滤, 因此需要在请求推荐结果时,以添加参数的形式调用请求推荐结果的接口使用,具体方式为:

将表示过滤规则的JSON字符串经过Base64编码后,将编码结果中的”+”、”/” 、”=”分别替换为”-”、”_” 、”.”,然后将此结果作为filter参数,请求推荐接口即可。

注意

除了要求符合基本格式外,过滤规则中还要求:

  1. 总的单值过滤规则不超过10个

  2. 组合深度不超过3层(即第3层的所有过滤规则必须都是单值过滤规则)

  3. 阿里云对URL的长度有限制,因此编码之后的字符串总长度不能过长,否则可能会引起SDK抛错;

    一般来说,只要遵循上述1、2要求,这个长度都不会超过限制。

此处JDK示例位于文章末尾

三、功能详解

推荐过滤功能支持对物品的单个属性字段的单值过滤,也支持对多个属性字段的组合过滤。

单值过滤:

支持的过滤条件有三种类型:

  1. 包含/不包含(contain/not_contain),仅支持多值字段(多值字段是指可以用逗号分隔的字符串字段,比如tags, author)

  2. 相等/不相等(equal/not_equal),仅支持单值字段(比如item_type, title, city)

  3. 类目匹配/类目不匹配(category_match/category_not_match),仅支持类目字段(只有category_path),类目匹配只支持从头开始,比如category_path=1_2_3_4,则只能匹配1、1_2、1_2_3、1_2_3_4,而不能匹配2_3、 3_4、2_3_4)

组合过滤:

对单值过滤或其他组合过滤的结果进行“且/或”的逻辑组合。 目前不同行业支持的推荐过滤字段有所差异,具体支持推荐过滤的字段如下:

  1. 电商行业:

    单值字段:

    item_type,category_level,category_path,weight,cur_price,brand_id,shop_id,source_id

    多值字段:

    tags

  1. 内容行业: 单值字段:

    item_type,category_level,category_path,weight,pub_id,source_id,country,city

    多值字段:

    tags,channel,organization,author

  2. 新闻行业: 单值字段:

    item_type,category_level,category_path,weight,pub_id,source_id,country,city

    多值字段:

    tags,channel,organization,author

过滤规则通过一个JSON字符串来表示,JSON格式的过滤规则的具体语法如下: 单值过滤:

{
                "cond": "contain" | "not_contain" | "equal" | "not_equal"| "category_match" | "category_not_match", // 要进行的比较操作
                "field": "<field_name>", // 要筛选的字段名,如"tag"、"channel"等
                "value": "<compare_value>" // 要筛选的目标值,如"军事"、"周星驰"等
}

组合过滤:

{
                "join": "and" | "or",
                "filters": [
                                { 组合过滤规则 | 单值过滤规则 },
                                { 组合过滤规则 | 单值过滤规则 },
                                ...
                ]
}

四、应用实例:

例3:

如图,假设当前产品为旅游类电商,场景设置有有“出行”、“酒店”等场景,其中“出行”场景中设置了“机票”、“火车票”、“汽车/船票”、“接送/租车”四个类目(category_path)的物品(item)。
用户想要查看“出行”页面下,“接送/租车”类目里的“租车”物品,
share2
用户有额外需求是:能送车上门、是京牌、有倒车雷达,
share
根据需求,此处需要获取推荐商品的请求为:在“出行_接送/租车”类目下,属于“租车”类(假设用brand_id区分)的商品,但因用户有额外需求,则需要使用到组合过滤功能,来满足用户一定的定制需求。
假设后面三项需求是在物品的tags字段中作为属性存放,则此处的推荐过滤表达式为:
( category_path 类目匹配 “出行_接送/租车 )AND ( brand_id = “租车”) AND
((tag 包含 “送车上门”) OR (tag 包含 “京牌”) OR (tag 包含 “倒车雷达”))
可用如下JSON格式的过滤条件查询:
{
  "join": "and",
  "filters": [
  {
    "cond": "contain",
    "field": "brand_id",
    "value": "租车"
  },
  {
    "cond": "category_match",
    "field": "category_path",
    "value": "出行_接送/租车"
  },
  {
    "join": "or",
    "filters": [{
      "cond": "contain",
      "field": "tag",
      "value": "送车上门"
    },
    {
      "cond": "contain",
      "field": "tag",
      "value": "京牌"
    },
    {
      "cond": "contain",
      "field": "tag",
      "value": "倒车雷达"
    }]
  }]
}
在发送请求时,使用如上的过滤条件即可

SDK调用代码样例

SDK调用代码示例:
@Data
private static abstract class BaseFilterRule {}

@EqualsAndHashCode(callSuper = true)
@Data
private static final class JoinFilterRule extends BaseFilterRule {
    String join;
    List<BaseFilterRule> filters = new ArrayList<>();
}

@EqualsAndHashCode(callSuper = true)
@Data
private static final class SingleFilterRule extends BaseFilterRule {
    String cond;
    String field;
    String value;
}

public void testRecommendWithFilterDemo() throws ClientException {
    JoinFilterRule rootRule = new JoinFilterRule();
    rootRule.setJoin("and");
    {
        JoinFilterRule tagsRule = new JoinFilterRule();
        tagsRule.setJoin("or");
        {
            SingleFilterRule rule = new SingleFilterRule();
            rule.setCond("contain");
            rule.setField("tags");
            rule.setValue("小红书");
            tagsRule.getFilters().add(rule);
        }
        {
            SingleFilterRule rule = new SingleFilterRule();
            rule.setCond("contain");
            rule.setField("tags");
            rule.setValue("爆品");
            tagsRule.getFilters().add(rule);
        }
        rootRule.getFilters().add(tagsRule);
    }
    {
        SingleFilterRule rule = new SingleFilterRule();
        rule.setCond("equal");
        rule.setField("channel");
        rule.setValue("美妆");
        rootRule.getFilters().add(rule);
    }
    {
        SingleFilterRule rule = new SingleFilterRule();
        rule.setCond("not_equal");
        rule.setField("category");
        rule.setValue("促销品");
        rootRule.getFilters().add(rule);
    }
    {
        SingleFilterRule rule = new SingleFilterRule();
        rule.setCond("category_match");
        rule.setField("category_path");
        rule.setValue("女装_裙子");
        rootRule.getFilters().add(rule);
    }
    String filterRuleString = new Gson().toJson(rootRule);
    filterRuleString = new String(Base64.getEncoder().encode(filterRuleString.getBytes()));
    filterRuleString = filterRuleString.replaceAll("\\+", "-");
    filterRuleString = filterRuleString.replaceAll("/", "_");
    filterRuleString = filterRuleString.replaceAll("=", ".");
    DefaultAcsClient client = createClient();
    RecommendRequest request = new RecommendRequest();
    request.setInstanceId(instanceId);
    request.setSceneId(sceneId);
    request.setUserId(userId);
    request.putQueryParameter("filter", filterRuleString);  //  添加filter参数 
    request.setReturnCount(2);
    request.setAcceptFormat(FormatType.JSON);
    RecommendResponse response = client.getAcsResponse(request);
    System.out.println(String.format("got %d results", response.getResult().size()));
}