Flutter产品搜索插件visenze_productsearch_sdk的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter产品搜索插件visenze_productsearch_sdk的使用

目录

1. 概述

ViSenze发现套件通过帮助客户更容易地搜索、导航和与产品互动,为您的客户提供更好的和更直观的产品搜索和发现体验。ViSenze最新的产品搜索和推荐API包含在这个SDK中。更多信息请参阅在线文档

2. 设置

2.1 安装

运行命令:

flutter get visenze_productsearch_sdk
2.2 启动

在开始使用SDK之前,您需要设置SDK密钥。大多数这些密钥可以在您的帐户的仪表板上找到。

首先,查看下表以了解每个密钥代表什么:

密钥 重要性 描述
app_key 必需 所有SDK功能都依赖于一个有效的app_key。app_key还限制了您可以使用的API功能。
placement_id 必需 您的放置ID。
timeout 可选 API超时时间(毫秒)。默认值为15000。
uid 可选 客户用户ID。如果没有提供,将自动生成。

创建ProductSearch实例:

const psClient = await VisenzeProductSearch.create('APP_KEY', 'PLACEMENT_ID');

也可以指定用户ID和超时时间:

const psClient = await VisenzeProductSearch.create('APP_KEY', 'PLACEMENT_ID', uid: 'CUSTOMER_UID', timeout: TIMEOUT_IN_MS);

3. API

3.1 图像搜索

POST /product/search_by_image

通过图像搜索可以有三种不同的方式 - 通过URL、ID或文件。

使用图像ID:
var params = {
  im_id: 'your-image-id'
};

var response = await psClient.productSearchByImage(null, params);
使用图像URL:
var params = {
  im_url: 'your-image-url'
};

var response = await psClient.productSearchByImage(null, params);
使用相册中的图像:
var image = await psClient.uploadImage();
if (image != null) {
  var response = await psClient.productSearchByImage(image, params);
}
使用相机捕捉的图像:
var image = await psClient.captureImage();
if (image != null) {
  var response = await psClient.productSearchByImage(image, params);
}

还可以传递格式为XFile的图像。

注意:如果您正在访问iOS设备上的相册/相机进行图像搜索,请提供NSPhotoLibraryUsageDescriptionNSCameraUsageDescription值。

请求参数:有关搜索API的请求参数,请参见Viseze文档中心

3.1.1 调整大小设置

默认情况下,我们将用户上传的图像大小限制为512x512像素,以平衡搜索延迟和搜索准确性。

如果您的图像包含细小细节,如纺织品图案和纹理,可以设置更大的限制。

psClient.widthLimit = 1024;
psClient.heightLimit = 1024;

为了有效利用移动设备的内存和网络带宽,最大尺寸设置为1024x1024。任何超过此限制的图像都将被调整为该限制。

3.2 推荐

GET /product/recommendations/{product_id}

根据产品的唯一标识符搜索产品数据库中的视觉相似产品。

var productId = 'your-product-id';

// 示例参数
var parameters = {
  limit: 20 // 将结果限制为20个结果
};

var response = await psClient.productSearchById(productId, parameters);

请求参数:有关此API的请求参数,请参见Viseze文档中心

4. 高级搜索

4.1 自动对象检测

搜索API能够检测查询图像中存在的对象,并建议最佳匹配的产品类型以运行搜索。

使用detectionpoint参数来建议查询图像中的特定对象。boxpoint参数不允许出现在同一个请求中。

var params = {
  im_url: 'your-image-url',
  detection: 'Top' // 仅检测具有产品类型`Top`的图像中的对象
  detection_limit: 5 // 仅检测图像中的最多5个对象
};

var response = await psClient.productSearchByImage(null, params);

详情:有关更多详细信息,请参阅Viseze文档中心中的自动对象检测文档。

4.2 过滤器和文本过滤器

要基于产品元数据过滤搜索结果,请在filterstext_filters参数中提供元数据键值对。

参数 过滤查询行为 示例
filters 过滤查询被视为精确匹配条件。适用于字符串、整数和浮点类型字段。 filters=brand:my_brand表示搜索结果的品牌(字符串)值必须严格等于“my_brand”。filters=price:10,199表示搜索结果的价格(整数)值必须严格在10到199之间(包括10和199)。
text_filters 过滤查询被视为部分匹配过滤器。仅适用于字符串类型字段。 text_filters=brand:my_brand表示搜索结果的品牌值可以是包含“my_brand”的任何值,例如“my_brand >> sub brand”。
示例
var productId = 'your-product-id';

var parameters = {
  filters: ['brand:my_brand', 'price:50:200'] // 过滤所有来自my_brand且价格在50-200货币单位的产品
};

var response = await psClient.productSearchById(productId, parameters);

详情:有关更多过滤器语法和规则,请参阅Viseze文档中心中的过滤器部分。

4.3 层面

层面是从结果列表中过滤的值。可以通过发送一组字段来启用层面。

var productId = 'your-product-id';

var parameters = {
  facets: 'gender, brand, price', // 返回可能的过滤元数据字段,性别、品牌、价格
  facets_limit: 10 // 每个层面返回10个值
};

var response = await psClient.productSearchById(productId, parameters);

详情:有关更多详细信息,请参阅Viseze文档中心中的层面部分。

4.4 属性检索

要从API调用中检索元数据,请在attrs_to_get(字段列表)属性中提供产品元数据键列表。

var productId = 'your-product-id';

var parameters = {
  attrs_to_get: 'gender, price, brand' // 在结果中返回性别、价格和品牌元数据
};

var response = await psClient.productSearchById(productId, parameters);

注意:只有已索引的属性才能被检索。

注意:只有已索引的属性可以通过此参数检索。您可以访问编辑应用页面查看哪些属性已包含在应用索引中。

5. 搜索结果

该SDK使用http库进行API请求。响应是一个http Response

错误代码列表可以在这里找到。 对于响应属性,请参阅API文档。

var productId = 'your-product-id';
var response = await psClient.productSearchById(productId);

if (response.statusCode == 200) {
  Map<String, dynamic> successResp = jsonDecode(response.body);
  List<Map<String, dynamic>> results = successResp['result'];
  // 处理结果
} else {
  // 处理错误
}

6. 事件跟踪

为了提高搜索性能并获得有用的数据洞察力,建议发送与视觉搜索结果相关的用户交互(操作)。

目前,我们支持以下事件动作:product_clickproduct_viewadd_to_carttransaction。但是,action参数可以是任意字符串,以便发送自定义事件。

某些事件(例如product_clickproduct_view)可能需要额外的参数,如pid(产品ID)。

6.1 设置

我们将使用从您的应用密钥和放置ID生成的跟踪ID初始化事件跟踪器。

6.2 发送事件
单个事件

用户操作可以通过事件处理器发送。注册事件处理器到用户将互动的元素。

// 发送产品点击
psClient.sendEvent('product_click', {
  queryId: '<search reqid>',
  pid: '<your product id>',
  pos: 1, // 搜索结果中的产品位置,从1开始
});

// 发送产品印象
psClient.sendEvent('product_view', {
  queryId: '<search reqid>',
  pid: '<your product id>',
  pos: 1, // 搜索结果中的产品位置,从1开始
});

// 发送交易事件,例如订单购买300元
psClient.sendEvent('transaction', {
  queryId: "<search reqid>",
  transId: "<your transaction ID>",
  value: 300
});

// 发送添加到购物车事件
psClient.sendEvent('add_to_cart', {
  queryId: '<search reqid>',
  pid: '<your product id>',
  pos: 1, // 搜索结果中的产品位置,从1开始
});

// 发送自定义事件
psClient.sendEvent('favourite', {
  queryId: '<search reqid>',
  label: 'custom event label',
  cat: 'visual_search'
});

// 处理成功或错误
try {
  psClient.sendEvent('product_click', {
    queryId: '<search reqid>',
    pid: '<your product id>',
    pos: 1, // 搜索结果中的产品位置,从1开始
  });
  onRequestSuccess(); // 处理成功
} catch (errResponse) {
  onRequestError(errResponse); // 处理错误
}
批量事件

批量操作可以通过批处理事件处理器发送。

psClient.sendEvents('transaction',
  [{
    queryId: '<search request ID>',
    pid: '<product ID - 1>',
    value: 300,
  }, {
    queryId: '<search request ID>',
    pid: '<product ID - 2>',
    value: 400
  }]
);
6.3 事件参数

有关所有事件参数及其解释的列表,请参阅此文档

6.3.1 搜索查询ID

所有发送到Viseze分析服务器的事件都需要在搜索结果响应中找到的搜索查询ID(reqid)作为请求参数的一部分。

var productId = 'your-product-id';
var parameters = {
  ...
};
var response = await psClient.productSearchById(productId, parameters);
var responseBody = jsonDecode(response.body);
var queryId = responseBody['reqid']; // <- 这是您的搜索查询ID

您也可以直接获取客户端最后一次成功搜索请求的查询ID。

var queryId = psClient.lastSuccessQueryId;
6.3.2 会话ID
var sessionId = psClient.sessionId;
6.3.3 用户ID
var userId = psClient.userId;

示例代码

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:visenze_productsearch_sdk/visenze_productsearch.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  [@override](/user/override)
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  String _recRequestResult = '<unknown>';
  String _searchRequestResult = '<unknown>';
  String _sid = '<unknown>';
  String _uid = '<unknown>';
  String _trackingRequestResult = '<unknown>';
  String? _lastQueryId = '<unknown>';

  final TextEditingController _pidController = TextEditingController();
  final TextEditingController _imgUrlController = TextEditingController();
  final TextEditingController _imgIdController = TextEditingController();
  final TextEditingController _queryParamController = TextEditingController();
  final TextEditingController _eventController = TextEditingController(text: 'transaction');
  final TextEditingController _paramsController = TextEditingController(text: '{"pid": "PID_1", "queryId": "1234", "value": 50}');
  final TextEditingController _paramsListController = TextEditingController(text: '[{"pid": "PID_1", "queryId": "1234", "value": 50}, {"pid": "PID_2", "queryId": "1234", "value": 100}]');

  late VisenzeProductSearch psSearchClient;
  late VisenzeProductSearch psRecClient;
  String? _fileName;

  [@override](/user/override)
  void initState() {
    super.initState();
    initPS();
  }

  // 工厂方法是异步的,所以我们将其放在异步方法中。
  void initPS() async {
    psSearchClient = await VisenzeProductSearch.create('APP_KEY', 'PLACEMENT_ID');
    psRecClient = await VisenzeProductSearch.create('APP_KEY', 'PLACEMENT_ID');
    setState(() {
      _sid = psSearchClient.sessionId;
      _uid = psSearchClient.userId;

      // 调整大小限制 --------
      // psSearchClient.widthLimit = 2048;
      // psSearchClient.heightLimit = 2048;
    });
  }

  // ------------- 公共工具函数 ---------- 开始 --------------
  void _resetSession() async {
    psSearchClient.resetSession();
    setState(() {
      _sid = psSearchClient.sessionId;
    });
  }

  void _onRequestSuccess() {
    setState(() {
      _trackingRequestResult = 'Request success';
    });
  }

  void _onRequestError(dynamic err) {
    setState(() {
      _trackingRequestResult = 'Request fail: $err';
    });
  }

  void _resetMsg() {
    _searchRequestResult = "";
    _trackingRequestResult = "";
    _fileName = "";
    _recRequestResult = "";
  }

  Map<String, dynamic> _getSearchQueryParam() {
    Map<String, dynamic> params = {};
    if(_queryParamController.text.isNotEmpty) {
      params = jsonDecode(_queryParamController.text);
    }
    return params;
  }

  // ------------- 推荐 ---------- 开始 --------------
  void _searchById() async {
    _resetMsg();
    try {
      Map<String, dynamic> params = _getSearchQueryParam();
      var response = await psRecClient.productSearchById(_pidController.text, params=params);
      setState(() {
        _recRequestResult = response.body;
        _lastQueryId = psRecClient.lastSuccessQueryId;
      });
    } catch (err) {
      _onRequestError(err);
    }
  }

  // ------------- 搜索 ---------- 开始 --------------
  void _searchByImgUrl() async {
    _resetMsg();
    try {
      Map<String, dynamic> params = {'im_url': _imgUrlController.text};
      params.addAll(_getSearchQueryParam());
      var response = await psSearchClient.productSearchByImage(null, params);
      setState(() {
        _searchRequestResult = response.body;
        _lastQueryId = psSearchClient.lastSuccessQueryId;
      });
    } catch(err) {
      _onRequestError(err);
    }

  }

  void _searchByImgId() async {
    _resetMsg();
    try {
      Map<String, dynamic> params = {'im_id': _imgIdController.text};
      params.addAll(_getSearchQueryParam());
      var response = await psSearchClient.productSearchByImage(null, params);
      setState(() {
        _searchRequestResult = response.body;
        _lastQueryId = psSearchClient.lastSuccessQueryId;
      });
    } catch(err) {
      _onRequestError(err);
    }
  }

  void _searchByCamera() async {
    _resetMsg();
    try {
      var file = await psSearchClient.captureImage();
      setState(() {
        _fileName = file?.name;
      });
      if (file != null) {
        _searchByImg(file);
      }
    } catch(err) {
      _onRequestError(err);
    }
  }

  void _searchByImageUpload() async {
    _resetMsg();
    var file;
    try {
      file = await psSearchClient.uploadImage();
    } catch(err) {
      _onRequestError(err);
      return;
    }
    setState(() {
      _fileName = file?.name;
    });
    if (file != null) {
      _searchByImg(file);
    }
  }

  void _searchByImg(file) async {
    _resetMsg();
    try {
      if (_fileName != null) {
        var response = await psSearchClient.productSearchByImage(file, _getSearchQueryParam());
        setState(() {
          _searchRequestResult = response.body;
          _lastQueryId = psSearchClient.lastSuccessQueryId;
        });
      }
    } catch(err) {
      _onRequestError(err);
    }
  }

  // ------------- 跟踪 ---------- 开始 --------------

  Future<void> _sendEvent() async {
    try {
      Map<String, dynamic> params = jsonDecode(_paramsController.text);
      params["queryId"] = _lastQueryId;
      await psSearchClient.sendEvent(
          _eventController.text, params);
      _onRequestSuccess();
    } catch (err) {
      _onRequestError(err);
    }
  }

  Future<void> _sendBatchEvent() async {
    try {
      List<Map<String, dynamic>> params = jsonDecode(_paramsListController.text)
          .map((element) => element as Map<String, dynamic>)
          .toList();
      await psSearchClient.sendEvents(_eventController.text, params);
      _onRequestSuccess();
    } catch (err) {
      _onRequestError(err);
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
        home: DefaultTabController(
      length: 3,
      child: Scaffold(
          appBar: AppBar(
            title: const Text('ProductSearch SDK Demo'),
            bottom: const TabBar(
              tabs: [
                Tab(text: 'Rec'),
                Tab(text: 'Search'),
                Tab(text: 'Tracking'),
              ],
            ),
          ),
          body: TabBarView(children: [
            SingleChildScrollView(
              child: Container(
                height: 600,
                padding: const EdgeInsets.all(15),
                child: ListView(
                  children: <Widget>[
                    Text(
                      'last query id: $_lastQueryId',
                    ),
                    const Padding(padding: EdgeInsets.all(20)),
                    TextField(
                      decoration: const InputDecoration(
                        labelText: "Query params",
                      ),
                      controller: _queryParamController,
                    ),
                    TextField(
                      decoration: const InputDecoration(
                        labelText: "Product id",
                      ),
                      controller: _pidController,
                    ),
                    const Padding(padding: EdgeInsets.all(4)),
                    ElevatedButton(
                        onPressed: _searchById,
                        child: const Text('Search by product id')),
                    const Divider(thickness: 2, color: Colors.grey, height: 30),
                    Text(
                      'Response: $_recRequestResult',
                    ),
                    const Padding(padding: EdgeInsets.all(4)),
                    Text("Error: $_trackingRequestResult"),
                  ],
                ),
              ),
            ),
            SingleChildScrollView(
              child: Container(
                height: 900,
                padding: const EdgeInsets.all(15),
                child: ListView(
                  children: <Widget>[
                    Text(
                      'last query id: $_lastQueryId',
                    ),
                    const Padding(padding: EdgeInsets.all(20)),
                    TextField(
                      decoration: const InputDecoration(
                        labelText: "Query params",
                      ),
                      controller: _queryParamController,
                    ),
                    const Padding(padding: EdgeInsets.all(20)),
                    TextField(
                      decoration: const InputDecoration(
                        labelText: "Image url",
                      ),
                      controller: _imgUrlController,
                    ),
                    const Padding(padding: EdgeInsets.all(4)),
                    ElevatedButton(
                        onPressed: _searchByImgUrl,
                        child: const Text('Search by image url')),
                    const Divider(thickness: 2, color: Colors.grey, height: 30),
                    TextField(
                      decoration: const InputDecoration(
                        labelText: "Image id",
                      ),
                      controller: _imgIdController,
                    ),
                    ElevatedButton(
                        onPressed: _searchByImgId,
                        child: const Text('Search by image id')),
                    const Divider(thickness: 2, color: Colors.grey, height: 30),
                    ElevatedButton(
                        onPressed: _searchByImageUpload,
                        child: const Text('Upload search')),
                    const Padding(padding: EdgeInsets.all(4)),
                    ElevatedButton(
                        onPressed: _searchByCamera,
                        child: const Text('Camera search')),
                    const Padding(padding: EdgeInsets.all(4)),
                    Text(
                      'Image name: $_fileName',
                    ),
                    const Divider(thickness: 2, color: Colors.grey, height: 30),
                    Text(
                      'Response: $_searchRequestResult',
                    ),
                    const Divider(thickness: 2, color: Colors.grey, height: 30),
                    const Padding(padding: EdgeInsets.all(4)),
                    Text("Error: $_trackingRequestResult"),
                  ],
                ),
              ),
            ),
            Container(
              height: 600,
              padding: const EdgeInsets.all(15),
              child: ListView(
                children: <Widget>[
                  Text(
                    'last query id: $_lastQueryId',
                  ),
                  const Padding(padding: EdgeInsets.all(4)),
                  Text(
                    'Session id: $_sid',
                  ),
                  Text(
                    'User id: $_uid',
                  ),
                  const Padding(padding: EdgeInsets.all(4)),
                  ElevatedButton(
                      onPressed: _resetSession,
                      child: const Text('Reset session')),
                  const Divider(thickness: 2, color: Colors.grey, height: 30),
                  TextField(
                    decoration: const InputDecoration(
                      labelText: "Event name",
                    ),
                    controller: _eventController,
                  ),
                  TextField(
                    decoration: const InputDecoration(
                      labelText: "Event params",
                    ),
                    controller: _paramsController,
                  ),
                  const Padding(padding: EdgeInsets.all(4)),
                  ElevatedButton(
                      onPressed: _sendEvent, child: const Text('Send event')),
                  TextField(
                    decoration: const InputDecoration(
                      labelText: "Event list params",
                    ),
                    controller: _paramsListController,
                  ),
                  const Padding(padding: EdgeInsets.all(4)),
                  ElevatedButton(
                      onPressed: _sendBatchEvent,
                      child: const Text('Send batch event')),
                  const Padding(padding: EdgeInsets.all(4)),
                  Text(_trackingRequestResult),
                ],
              ),
            ),
          ])),
    ));
  }
}

更多关于Flutter产品搜索插件visenze_productsearch_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter产品搜索插件visenze_productsearch_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


visenze_productsearch_sdk 是 ViSenze 提供的一个 Flutter 插件,用于在 Flutter 应用中集成视觉搜索功能。通过该插件,你可以在应用中实现基于图像的搜索,类似于通过拍照或上传图片来搜索相似商品的功能。

以下是使用 visenze_productsearch_sdk 插件的步骤:

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加 visenze_productsearch_sdk 依赖:

dependencies:
  flutter:
    sdk: flutter
  visenze_productsearch_sdk: ^1.0.0  # 请确保使用最新版本

然后,运行 flutter pub get 来安装依赖。

2. 初始化 SDK

在你的 Flutter 应用中,首先需要初始化 ViSenze 的 SDK。你需要在应用启动时调用 ViSenze.initialize() 方法,并传入你的 API key。

import 'package:visenze_productsearch_sdk/visenze_productsearch_sdk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await ViSenze.initialize(apiKey: 'YOUR_API_KEY');
  runApp(MyApp());
}

3. 使用视觉搜索功能

在应用中使用视觉搜索功能时,你可以通过 ViSenze.searchByImage 方法来搜索相似商品。你可以传入本地图片文件路径或网络图片的 URL。

import 'package:visenze_productsearch_sdk/visenze_productsearch_sdk.dart';
import 'package:flutter/material.dart';

class SearchPage extends StatefulWidget {
  [@override](/user/override)
  _SearchPageState createState() => _SearchPageState();
}

class _SearchPageState extends State<SearchPage> {
  List<SearchResult> _searchResults = [];

  Future<void> _searchByImage(String imagePath) async {
    try {
      final results = await ViSenze.searchByImage(imagePath: imagePath);
      setState(() {
        _searchResults = results;
      });
    } catch (e) {
      print('Error searching by image: $e');
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Visual Search'),
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () {
              _searchByImage('assets/sample_image.jpg'); // 使用本地图片
            },
            child: Text('Search by Image'),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _searchResults.length,
              itemBuilder: (context, index) {
                final result = _searchResults[index];
                return ListTile(
                  title: Text(result.productName),
                  subtitle: Text(result.productDescription),
                  leading: Image.network(result.imageUrl),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!