文件库的元数据仓库:ArkTS 为鸿蒙文件管理器设计 UNIQUE 路径字段

文件库的元数据仓库:ArkTS 为鸿蒙文件管理器设计 UNIQUE 路径字段
实例文件资料库FileLib技术UNIQUE 路径去重、元数据字段、类型统计一、业务需求分析文件库的数据形态文件资料库管理文档/图片/视频/音频/压缩包是文件元数据管理应用——数据库存「文件的描述信息」名称、类型、大小、路径、标签、备注不存文件本体真实文件在存储系统里。核心业务需求文件记录名称、类型文档/图片/视频/音频/压缩包/其他、大小、路径、标签、备注路径去重同一路径的文件不能重复导入——UNIQUE 约束通配符搜索按名称或标签模糊搜索——LIKE 通配符类型筛选与统计按类型筛选 每类型文件数与总大小——GROUP BY 统计批量导入一次导入多个文件已存在的路径跳过。二、字段设计文件元数据文件库表 file_lib字段名类型约束说明idINTEGERPRIMARY KEY AUTOINCREMENT自增主键nameTEXTNOT NULL文件名file_typeTEXTNOT NULL类型文档/图片/视频/音频/压缩包/其他sizeINTEGERNOT NULL DEFAULT 0大小字节pathTEXTNOT NULL UNIQUE唯一路径tagsTEXTDEFAULT ‘’逗号分隔标签noteTEXTDEFAULT ‘’备注created_timeINTEGERNOT NULL导入时间戳设计要点拆解1. path 用 TEXT UNIQUE。文件路径/docs/需求/项目需求文档.docx是文件在存储系统中的唯一标识——UNIQUE 约束保证同一路径不重复入库。为什么不用 id 去重id 是自增的每行不同文件路径才是业务上的「唯一键」同一文件导入两次应该是同一份。业务唯一键用 UNIQUE 约束——这是数据库完整性的核心。2. size 用 INTEGER 存字节。文件大小以字节为整数存储120 * 1048576 120MB——存储最小单位展示时换算页面 fmtSize 换算 KB/MB/GB。为什么存字节而不是直接存「120MB」字符串因为要参与排序与统计SUM(size) 算总大小——数值字段才能聚合计算。3. file_type 用文本枚举。六类文档/图片/视频/音频/压缩包/其他——参与等值筛选equalTo(file_type, 文档)与分组统计GROUP BY file_type文本直接用与影音 type 同理。4. tags 用逗号分隔字符串。需求,产品——多标签的简化存储不建标签关联表。搜索时 LIKE 匹配LIKE %需求%——Demo 级多值方案真实应用标签多时建「文件-标签」关联表第 12 实例 CRM 的进阶模式。5. 元数据字段name/type/size/path/tags/note全部是「描述」——不存文件本体真实文件在文件系统数据库只存索引。这是文件管理器类应用的通用架构DB 存元数据文件系统存本体。三、建表 SQLCREATETABLEIFNOTEXISTSfile_lib(idINTEGERPRIMARYKEYAUTOINCREMENT,nameTEXTNOTNULL,file_typeTEXTNOTNULL,sizeINTEGERNOTNULLDEFAULT0,pathTEXTNOTNULLUNIQUE,tagsTEXTDEFAULT,noteTEXTDEFAULT,created_timeINTEGERNOTNULL);path TEXT NOT NULL UNIQUE——唯一约束的核心体现插入重复路径 → SQLite 抛约束错误批量导入的「跳过逻辑」在应用层处理见 16-3。UNIQUE 是「路径去重」的数据库级保障——即使应用层忘了判断数据库也不会产生重复路径。四、FileLibDao 封装搜索与统计是亮点数据层核心FileLibDao本实例的独特方法是searchLIKE 通配符搜索与typeStats类型分组统计按类型筛选staticasyncqueryByType(context:common.Context,fileType:string):PromiseFileRecord[]{conststoreawaitFileLibDao.getStore(context);constpredicatesnewrelationalStore.RdbPredicates(FileLibDao.TABLE);if(fileType!全部){predicates.equalTo(file_type,fileType);}predicates.orderByDesc(created_time);constresultawaitstore.query(predicates);returnFileLibDao.collect(result);}‘全部’ 哨兵值fileType 全部时不过滤查全表——筛选条的「全部」选项在 DAO 层用哨兵值跳过谓词。「全部」不在数据库层处理在查询层跳过。通配符搜索LIKEstaticasyncsearch(context:common.Context,keyword:string):PromiseFileRecord[]{conststoreawaitFileLibDao.getStore(context);constpredicatesnewrelationalStore.RdbPredicates(FileLibDao.TABLE);predicates.like(name,%${keyword}%).or().like(tags,%${keyword}%).orderByDesc(created_time);constresultawaitstore.query(predicates);returnFileLibDao.collect(result);}生成的 SQLSELECT*FROMfile_libWHEREnameLIKE%需求%ORtagsLIKE%需求%ORDERBYcreated_timeDESC;LIKE 通配符%关键字%匹配「包含关键字」——%需求%匹配「项目需求文档.docx」与 tags 含「需求」的文件。%是任意字符通配符0 个或多个_是单字符通配符。LIKE 的 OR 组合名称或标签任一命中即返回——搜索的「多字段命中」。类型统计GROUP BY COUNT SUMstaticasynctypeStats(context:common.Context):PromiseTypeStat[]{conststoreawaitFileLibDao.getStore(context);constresultawaitstore.querySql(SELECT file_type, COUNT(*) AS cnt, SUM(size) AS total_size FROM${FileLibDao.TABLE}GROUP BY file_type ORDER BY cnt DESC);constlist:TypeStat[][];while(result.goToNextRow()){constitem:TypeStat{fileType:result.getString(result.getColumnIndex(file_type)),count:result.getLong(result.getColumnIndex(cnt)),totalSize:result.getLong(result.getColumnIndex(total_size)),};list.push(item);}result.close();returnlist;}GROUP BY COUNT SUM 三合一每个类型一行——类型名 文件数COUNT 总大小SUM。ORDER BY cnt DESC文件数多的类型排前文档通常最多。TypeStat 接口{ fileType, count, totalSize }——统计结果的结构化返回。总统计staticasynctotalStats(context:common.Context):PromiseTotalStat{// SELECT COUNT(*) AS cnt, SUM(size) AS total_size FROM file_lib}标题栏「18 个文件 · 共 1.7GB」——COUNT SUM 无分组全表统计。TotalStat 接口{ count, totalSize }。五、技术要点对照表技术点实现方式生产价值路径去重path UNIQUE 约束防重复导入模糊搜索LIKE ‘%kw%’ OR LIKE名称/标签命中类型统计GROUP BY COUNT SUM每类文件数/大小全表统计COUNT SUM 无分组标题栏数字批量导入路径先查后插去重导入大小单位字节存储 fmtSize 换算可聚合可展示六、文章小结文件资料库的数据层核心是**「UNIQUE 路径去重 LIKE 搜索 GROUP BY 统计」**path UNIQUE 约束从数据库层保证文件路径唯一防重复导入LIKE 通配符实现名称/标签的多字段模糊搜索GROUP BY COUNT SUM 三合一给出每类型文件数与总大小类型筛选条的动态来源。字节整数存储让大小可聚合SUM可换算展示。这是「文件元数据管理类应用」的通用数据模型。下一篇16-2展示文件浏览器 UI——搜索框 动态类型筛选条 图标行列表。七、文件元数据字段设计详解文件库 7 个字段各司其职其中path、size、file_type、tags四个字段的设计决策最值得展开——它们分别对应「去重、聚合、筛选、搜索」四类核心能力。7.1 path UNIQUE路径是业务唯一键对比项id 主键path UNIQUE生成方式自增数据库自动文件系统决定业务产生语义行标识与业务无关文件的真实身份位置重复导入id 不同无法拦截约束报错天然拦截结论仅作内部主键业务唯一键必须 UNIQUE关键认知UNIQUE 约束是数据库级的最后防线。批量导入时应用层「先查再插」只是第一道防线省一次插入开销即使应用层判断逻辑写错path UNIQUE也会让 SQLite 抛约束错误杜绝脏数据。约束在数据库防御分两层// 应用层先查重第一道防线constexistawaitFileLibDao.queryByPath(context,path);if(exist){return;}// 已存在则跳过// 数据库层UNIQUE 兜底第二道防线// 若上面判断漏了INSERT 会抛 SQLITE_CONSTRAINT_UNIQUE7.2 size 字节存储整数才能聚合存字节INTEGER2.3MB 2.3 * 1048576的整数——可 SUM、可排序、可比较存「2.3MB」字符串SUM 无意义、排序错乱‘120MB’ 按字典序排在 ‘25MB’ 前面展示换算fmtSize读取时按 1024 分级换算 KB/MB/GB——存储与展示分离。7.3 file_type 文本枚举六类文本枚举文档/图片/视频/音频/压缩包/其他直接参与等值筛选equalTo(file_type, 图片)与分组统计GROUP BY file_type。文本在此场景与数值效率无差异六类基数极小且可读性最好——SQL 结果直接显示无需再映射成中文。7.4 tags 逗号分隔需求,产品是多值属性的简化建模——一个字段存多个标签。代价无法做「按标签精确统计」搜索只能 LIKE 模糊命中。Demo 级取舍文件数少18 个时 LIKE 扫描全表毫无压力标签多、要按标签统计时升级为「文件-标签」关联表CRM 实例的进阶模式。八、元数据与文件本体的分离架构┌───────────────┐ path ┌──────────────────┐ │ file_lib 表 │ ────────────► │ 文件系统/相册 │ │ 存「描述」 │ │ 存「本体」 │ │ name/size/tags │ │ /docs/需求/xx.docx │ └───────────────┘ └──────────────────┘ DB 索引 真实数据为什么分离DB 不存大对象文件本体可能几百 MB本实例种子最大 800MB入库会让数据库膨胀、备份变慢文件系统已是最好的存储读写、流式播放、权限管理都是系统级能力元数据可高速查询文件数统计、类型聚合、模糊搜索都在 DB 索引层面完成不用打开文件删除语义清晰删记录 ≠ 删文件只清索引「彻底删除」才联动文件系统——两个动作由应用层编排。鸿蒙侧实现元数据在file_lib表本体在沙箱路径指向的真实文件。UI 点击行时用 path 打开/分享文件DB 只管描述——path 是连接两层的桥梁字段interfaceFileRecord{id:number;name:string;// 展示用path:string;// 定位本体用桥梁size:number;// 统计用字节fileType:string;// 筛选/分组用tags:string;// 搜索用note:string;createdTime:number;}九、字节存储的可聚合性类型统计的核心 SQL 依赖 size 是数值SELECTfile_type,COUNT(*)AScnt,SUM(size)AStotal_sizeFROMfile_libGROUPBYfile_typeORDERBYcntDESC;存储类型COUNTSUMAVG排序INTEGER 字节✅✅✅✅ 数值序TEXT “120MB”✅❌❌❌ 字典序一个字段的存储类型决定它的能力边界——选 INTEGER 存字节等于同时解锁「总大小SUM」「每类平均大小AVG」「按大小排序ORDER BY size DESC」三个能力。展示侧 fmtSize 是纯函数functionfmtSize(size:number):string{if(size1024*1024*1024)return(size/(1024**3)).toFixed(1)GB;if(size1024*1024)return(size/(1024**2)).toFixed(1)MB;if(size1024)return(size/1024).toFixed(0)KB;returnsizeB;}存储不做加工展示才换算——数据库永远存原始字节单位换算只发生在渲染层。十、LIKE 搜索思路预览后续文章16-2/16-3将展开完整搜索实现这里先给出建表层就要想好的搜索设计-- 名称或标签命中关键字SELECT*FROMfile_libWHEREnameLIKE%需求%ORtagsLIKE%需求%ORDERBYcreated_timeDESC;三个提前设计name 与 tags 都是 TEXT——LIKE 可直接作用无需转换tags 的逗号分隔格式决定了搜索是「包含匹配」而非「等值匹配」——建表时就想清楚了中文 LIKE 无需转义英文如需忽略大小写可配合COLLATE NOCASE。十一、FAQQ1path 用 UNIQUE重复导入时会发生什么ASQLite 抛SQLITE_CONSTRAINT_UNIQUE约束错误。应用层两条路插入前先查重省开销或 try/catch 捕获约束错误后跳过该条——查重省开销UNIQUE 兜底。Q2size 为什么不直接存「2.3MB」这种人类可读字符串A字符串无法 SUM/排序/比较‘120MB’ 字典序小于 ‘25MB’。字节整数存储后由展示层按 1024 换算——存储与展示解耦统计能力完整。Q3tags 用逗号分隔会不会有歧义A约定标签本身不含逗号导入时清洗/替换即可。这是「一列多值」的简化方案查询用 LIKE 包含匹配需要精确按标签筛选时可用LIKE %,需求,%加逗号边界。Q4UNIQUE 和 PRIMARY KEY 都是唯一为什么还要 path UNIQUEAPRIMARY KEY 约束主键id自增语义是「行标识」UNIQUE 约束业务键path语义是「业务上不能重复」——主键管内部UNIQUE 管业务两者各司其职。Q5文件删除了数据库记录还在怎么办A文件库只管理元数据删记录只清 DB 索引若要求同步清理本体应用层在删除时用 path 调文件系统删除接口——删记录与删文件两个动作要在同一事务语义里编排先删 DB 成功再删文件或反之。

最新新闻

日新闻

周新闻

月新闻