当前位置:   article > 正文

HarmonyOS学习路之开发篇—数据管理(融合搜索)_全局融合搜索应用

全局融合搜索应用

融合搜索概述

HarmonyOS融合搜索为开发者提供搜索引擎级的全文搜索能力,可支持应用内搜索和系统全局搜索,为用户提供更加准确、高效的搜索体验。

基本概念

  • 全文索引

    记录字或词的位置和次数等属性,建立的倒排索引

  • 全文搜索

    通过全文索引进行匹配查找结果的一种搜索引擎技术。

  • 全局搜索

    可以在系统全局统一的入口进行的搜索行为。

  • 全局搜索应用

    HarmonyOS上提供全局搜索入口的应用,一般为桌面下拉框或悬浮搜索框。

  • 索引源应用

    通过融合搜索索引接口对其数据建立索引的应用。

  • 可搜索配置

    每个索引源应用应该提供一个包括应用包名、是否支持全局搜索等信息的可搜索实体,以便全局搜索应用发起搜索。

  • 群组

    经过认证的可信设备圈,可从账号模块获取群组ID。

  • 索引库

    一种搜索引擎的倒排索引库,包含多个索引文件的整个目录构成一个索引库。

  • 索引域

    索引数据的字段名,比如一张图片有文件名、存储路径、大小、拍摄时间等,文件名就是其中的一个索引域。

  • 索引属性

    描述索引域的信息,包括索引类型、是否为主键、是否存储、是否支持分词等。

运作机制

索引源应用通过融合搜索接口设置可搜索实体,并为其数据内容构建全文索引。全局搜索应用接收用户发起的搜索请求,遍历支持全局搜索的可搜索实体,解析用户输入并构造查询条件,最后通过融合搜索接口获取各应用搜索结果。

图1 融合搜索运作示意图

 

约束与限制

  • 构建索引或者发起搜索前,索引源应用必须先设置索引属性,并且必须有且仅有一个索引域设置为主键,且主键索引域不能分词,索引和搜索都会使用到索引属性。
  • 索引源应用的数据发生变动时,开发者应同步通过融合搜索索引接口更新索引,以保证索引和应用原始数据的一致性。
  • 批量创建、更新、删除索引时,应控制单次待索引内容大小,建议分批创建索引,防止内存溢出。
  • 分页搜索和分组搜索应控制每页返回结果数量,防止内存溢出。
  • 构建和搜索本机索引时,应该使用提供的SearchParameter.DEFAULT_GROUP作为群组ID,分布式索引使用通过账号模块获取的群组ID。
  • 搜索时需先创建搜索会话,并务必在搜索结束时关闭搜索会话,释放内存资源。
  • 使用融合搜索服务接口需要在“config.json”配置文件中添加“ohos.permission.ACCESS_SEARCH_SERVICE”权限。
  • 搜索时的SearchParamter.DEVICE_ID_LIST必须与创建索引时的deviceId一致。

融合搜索开发

场景介绍

索引源应用,一般为有持久化数据的应用,可以通过融合搜索接口为其应用数据建立索引,并配置全局搜索可搜索实体,帮助用户通过全局搜索应用查找本应用内的数据。应用本身提供搜索框时,也可直接在应用内部通过融合搜索接口实现全文搜索功能。

接口说明

HarmonyOS中的融合搜索为开发者提供以下几种能力,详见API参考。

表1 融合搜索接口功能介绍

类名

接口名

描述

SearchAbility

public List<IndexData> insert(String groupId, String bundleName, List<IndexData> indexDataList)

索引插入

public List<IndexData> update(String groupId, String bundleName, List<IndexData> indexDataList)

索引更新

public List<IndexData> delete(String groupId, String bundleName, List<IndexData> indexDataList)

索引删除

SearchSession

public int getSearchHitCount(String queryJsonStr)

搜索命中结果数量

public List<IndexData> search(String queryJsonStr, int start, int limit)

分页搜索

public List<Recommendation> groupSearch(String queryJsonStr, int groupLimit)

分组搜索

开发步骤

在config.json中添加permisssion权限。

  1. // 添加在abilities同一目录层级
  2. "reqPermissions": [
  3. {
  4. "name": "ohos.permission.ACCESS_SEARCH_SERVICE"
  5. }
  6. ]

实例化SearchAbility, 连接融合搜索服务。

  1. SearchAbility searchAbility = new SearchAbility(context);
  2. String bundleName = context.getBundleName();
  3. CountDownLatch lock = new CountDownLatch(1);
  4. // 连接服务
  5. searchAbility.connect(new ServiceConnectCallback() {
  6. @Override
  7. public void onConnect() {
  8. lock.countDown();
  9. }
  10. @Override
  11. public void onDisconnect() {
  12. }
  13. });
  14. // 等待回调,最长等待时间可自定义。
  15. try {
  16. lock.await(3000, TimeUnit.MILLISECONDS);
  17. } catch (InterruptedException e) {
  18. HiLog.error(LABEL, "await failed, %{public}s", e.getMessage());
  19. }
  20. if (searchAbility.hasConnected()) {
  21. // 连接成功
  22. } else {
  23. // 连接失败,可重试。
  24. }

设置索引属性。

  1. // 构造自定义索引属性
  2. List<IndexForm> indexFormList = new ArrayList<IndexForm>() { {
  3. add(new IndexForm("tag", IndexType.SORTED, false, true, false)); // 分词,同时支持排序、分组
  4. add(new IndexForm("bucket_id", IndexType.INTEGER, false, true, false)); // 支持排序和范围查询
  5. add(new IndexForm("latitude", IndexType.FLOAT, false, true, false)); // 支持范围搜索
  6. add(new IndexForm("longitude", IndexType.DOUBLE, false, true, false)); // 支持范围搜索
  7. add(new IndexForm("device_id", IndexType.NO_ANALYZED, false, true, false)); // 支持搜索
  8. } };
  9. // 使用通用模板设置索引属性
  10. int result = searchAbility.setIndexForm(bundleName, 1, indexFormList, IndexSchemaType.COMMON);
  11. if (result == 1) {
  12. // 设置索引属性成功
  13. } else {
  14. // 设置索引属性失败,可重试
  15. }

插入索引。

  1. // 构建索引数据
  2. List<IndexData> indexDataList = new ArrayList<>();
  3. for (int i = 0; i < 5; i++) {
  4. CommonItem commonItem = new CommonItem()
  5. .setIdentifier(LOCAL_DEVICE_ID + i) // identifier为主键
  6. .setTitle("白云")
  7. .setSubtitle("subtitle")
  8. .setCategory("things")
  9. .setDescription("is description")
  10. .setName("name")
  11. .setAlternateName("othername")
  12. .setDateCreate(System.currentTimeMillis())
  13. .setKeywords("key")
  14. .setPotentialAction("com.sample.search.TestAbility")
  15. .setThumbnailUrl(FILE_PATH)
  16. .setUrl(FILE_PATH)
  17. .setReserved1(REVERSE_VALUE)
  18. .setReserved2("reserved");
  19. commonItem.put("tag", "天空" + i);
  20. commonItem.put("bucket_id", i);
  21. commonItem.put("latitude", i / 5.0 * 180);
  22. commonItem.put("longitude", i / 5.0 * 360);
  23. commonItem.put("device_id", "localDeviceId");
  24. indexDataList.add(commonItem);
  25. }
  26. // 插入索引
  27. List<IndexData> failedList = searchAbility.insert(SearchParameter.DEFAULT_GROUP, bundleName, indexDataList);
  28. // 失败的记录可以持久化,稍后重试。

构建查询。

  1. // 构建查询
  2. ZSONObject zsonObject = new ZSONObject();
  3. // SearchParameter.QUERY对应用户输入,建议搜索域分词。
  4. // 这里假设用户输入是“天空”,要在"title", "tag"这两个域上发起搜索。
  5. ZSONObject query = new ZSONObject();
  6. query.put("天空", new ZSONArray(Arrays.asList(CommonItem.TITLE, "tag")));
  7. zsonObject.put(SearchParameter.QUERY, query);
  8. // SearchParameter.FILTER_CONDITION对应的ZSONArray里可以添加搜索条件。
  9. // 对于索引库里的一条索引,ZSONArray下的每个ZSONObject指定的条件都必须满足才会命中,ZSONObject里的条件组合满足其中一个,这个ZSONObject指定的条件即可满足。
  10. ZSONArray filterCondition = new ZSONArray();
  11. // 第一个条件,一个域上可能取多个值。
  12. ZSONObject filter1 = new ZSONObject();
  13. filter1.put("bucket_id", new ZSONArray(Arrays.asList(0, 1, 2))); // 一条索引在"bucket_id"的取值为0或1或2就能命中。
  14. filter1.put(CommonItem.IDENTIFIER, new ZSONArray(Arrays.asList(0, 1))); // 或者在CommonItem.IDENTIFIER的取值为0或者1也可以命中。
  15. filterCondition.add(filter1);
  16. ZSONObject filter2 = new ZSONObject();
  17. filter2.put("tag", new ZSONArray(Arrays.asList("白云")));
  18. filter2.put(CommonItem.TITLE, new ZSONArray(Arrays.asList("白云"))); // 一条索引只要在"tag"或者CommonItem.TITLE上命中"白云"就能命中。
  19. filterCondition.add(filter2);
  20. zsonObject.put(SearchParameter.FILTER_CONDITION, filterCondition); // 一条索引要同时满足第一和第二个条件才能命中。
  21. // SearchParameter.DEVICE_ID_LIST对应设备ID,匹配指定设备ID的索引才会命中。
  22. ZSONObject deviceId = new ZSONObject();
  23. deviceId.put("device_id", new ZSONArray(Arrays.asList("localDeviceId"))); // 指定本机设备。
  24. zsonObject.put(SearchParameter.DEVICE_ID_LIST, deviceId);
  25. // 可以在支持范围搜索的索引域上发起范围搜索,一条索引在指定域的值落在对应的指定范围才会命中。
  26. ZSONObject latitudeObject = new ZSONObject();
  27. latitudeObject.put(SearchParameter.LOWER, -40.0f);
  28. latitudeObject.put(SearchParameter.UPPER, 40.0f);
  29. zsonObject.put("latitude", latitudeObject); // 纬度必须在[-40.0f, 40.0f]
  30. ZSONObject longitudeObject = new ZSONObject();
  31. longitudeObject.put(SearchParameter.LOWER, -90.0);
  32. longitudeObject.put(SearchParameter.UPPER, 90.0);
  33. zsonObject.put("longitude", longitudeObject); // 经度必须在[-90.0, 90.0]
  34. // SearchParameter.ORDER_BY对应搜索结果的排序,排序字段通过SearchParameter.ASC和SearchParameter.DESC指定搜索结果在这个字段上按照升序、降序排序。
  35. // 这里填充字段的顺序是重要的,比如这里两个索引之间会先在CommonItem.CATEGORY字段上升序排序,只有在CommonItem.CATEGORY上相同时,才会继续在"tag"上降序排序,以此类推。
  36. ZSONObject order = new ZSONObject();
  37. order.put(CommonItem.CATEGORY, SearchParameter.ASC);
  38. order.put("tag", SearchParameter.DESC);
  39. zsonObject.put(SearchParameter.ORDER_BY, order);
  40. // SearchParameter.GROUP_FIELD_LIST对应分组搜索的域,调用groupSearch接口需要指定。
  41. zsonObject.put(SearchParameter.GROUP_FIELD_LIST, new ZSONArray(Arrays.asList("tag", CommonItem.CATEGORY)));
  42. // 得到查询字符串。
  43. String queryZsonStr = zsonObject.toString();
  44. // 构建的json字符串如下:
  45. /**
  46. {
  47. "SearchParameter.QUERY": {
  48. "天空": [
  49. "title",
  50. "tag"
  51. ]
  52. },
  53. "SearchParameter.FILTER_CONDITION": [
  54. {
  55. "bucket_id": [
  56. 0,
  57. 1,
  58. 2
  59. ],
  60. "identifier": [
  61. 0,
  62. 1
  63. ]
  64. },
  65. {
  66. "tag": [
  67. "白云"
  68. ],
  69. "title": [
  70. "白云"
  71. ]
  72. }
  73. ],
  74. "SearchParameter.DEVICE_ID_LIST": {
  75. "device_id": [
  76. "localDeviceId"
  77. ]
  78. },
  79. "latitude": {
  80. "SearchParameter.LOWER": -40.0,
  81. "SearchParameter.UPPER": 40.0
  82. },
  83. "longitude": {
  84. "SearchParameter.LOWER": -90.0,
  85. "SearchParameter.UPPER": 90.0
  86. },
  87. "SearchParameter.ORDER_BY": {
  88. "category": "ASC",
  89. "tag": "DESC"
  90. },
  91. "SearchParameter.GROUP_FIELD_LIST": [
  92. "tag",
  93. "category"
  94. ]
  95. }
  96. **/

开始搜索会话,发起搜索。

  1. // 开始搜索会话
  2. SearchSession searchSession = searchAbility.beginSearch(SearchParameter.DEFAULT_GROUP, bundleName);
  3. if (searchSession == null) {
  4. return;
  5. }
  6. try {
  7. int hit = searchSession.getSearchHitCount(queryJsonStr); // 获取总命中数
  8. int batch = 50; // 每页最多返回50个结果
  9. for (int i = 0; i < hit; i += batch) {
  10. List<IndexData> searchResult = searchSession.search(queryJsonStr, i, batch);
  11. // 处理IndexData
  12. }
  13. int groupLimit = 10; // 每个分组域上最多返回10个分组结果
  14. List<Recommendation> recommendationResult = searchSession.groupSearch(queryJsonStr, groupLimit);
  15. // 处理Recommendation
  16. } finally {
  17. // 结束搜索,释放资源
  18. searchAbility.endSearch(SearchParameter.DEFAULT_GROUP, bundleName, searchSession);
  19. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/236764
推荐阅读
相关标签
  

闽ICP备14008679号