1/** 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15import ConfigData from '../../../../../../utils/src/main/ets/default/baseUtil/ConfigData'; 16import Log from '../../../../../../utils/src/main/ets/default/baseUtil/LogDecorator'; 17import { BaseData } from '../../../../../../utils/src/main/ets/default/bean/BaseData'; 18import LogUtil from '../../../../../../utils/src/main/ets/default/baseUtil/LogUtil'; 19import SearchConfig from '../common/SearchConfig'; 20import SearchData from '../model/SearchData'; 21import SearchDataProvider from '../provider/SearchDataProvider'; 22import BaseParseConfModel from '../../../../../../utils/src/main/ets/default/model/BaseParseConfModel'; 23import ohosDataRdb from '@ohos.data.rdb'; 24import featureAbility from '@ohos.ability.featureAbility'; 25 26/** 27 * Search service class 28 */ 29export default class SearchModel { 30 private rdbStore; 31 private searchDataConfig; 32 33 constructor(configData: Array<Object>) { 34 this.searchDataConfig = configData; 35 } 36 37 /** 38 * Initialize rdb 39 */ 40 @Log 41 private async initRdb(): Promise<void> { 42 LogUtil.log('settings initRdb.'); 43 44 if (!this.rdbStore) { 45 LogUtil.log('settings RDB has not been initialized yet.'); 46 47 // database init 48 const STORE_CONFIG = { name: SearchConfig.RDB_NAME}; 49 this.rdbStore = await ohosDataRdb.getRdbStore(globalThis.settingsAbilityContext, STORE_CONFIG, 1); 50 LogUtil.log('settings SettingsSearch.db is ready.'); 51 52 // table SEARCH_DATA init 53 await this.rdbStore.executeSql(SearchConfig.search.DDL_TABLE_CREATE, null); 54 LogUtil.log('settings table SEARCH_DATA is ready.'); 55 } 56 } 57 58 /** 59 * Initialize search data 60 */ 61 @Log 62 public async initSearchData(): Promise<void> { 63 const searchData = await this.gatherSearchData(); 64 await this.initRdbSearchData(searchData); 65 } 66 67 /** 68 * Search 69 * 70 * @param query - query content 71 */ 72 @Log 73 public async search(query: string): Promise<SearchData[]> { 74 if (!query) { 75 return [] 76 } 77 query = this.sqlLikeCharReplace(query); 78 await this.initRdb(); 79 80 // query search data 81 let predicates = new ohosDataRdb.RdbPredicates(SearchConfig.search.TABLE_NAME); 82 predicates.like(SearchConfig.search.FIELD_KEYWORD, `%${query}%`) 83 .or().like(SearchConfig.search.FIELD_SUMMARY, `%${query}%`) 84 .or().like(SearchConfig.search.FIELD_SYNONYM, `%${query}%`) 85 .orderByAsc(SearchConfig.search.FIELD_URI); 86 87 let resultSet = await this.rdbStore.query(predicates, [ 88 SearchConfig.search.FIELD_ICON, 89 SearchConfig.search.FIELD_TITLE, 90 SearchConfig.search.FIELD_KEYWORD, 91 SearchConfig.search.FIELD_SUMMARY, 92 SearchConfig.search.FIELD_URI 93 ]); 94 95 // build search data from resultSet 96 let searchData: SearchData[] = []; 97 while (resultSet.goToNextRow()) { 98 const data = new SearchData(); 99 data.icon = resultSet.getString(0); 100 data.title = resultSet.getString(1); 101 data.keyword = resultSet.getString(2); 102 data.summary = resultSet.getString(3); 103 data.uri = resultSet.getString(4); 104 searchData.push(data); 105 } 106 resultSet.close(); 107 resultSet = null; 108 109 return searchData; 110 } 111 112 /** 113 * Handle special characters for sql.like 114 * 115 * @param query - query content 116 */ 117 @Log 118 private sqlLikeCharReplace(query: string): string { 119 query = query.replace("[", "/["); 120 query = query.replace("]", "/]"); 121 query = query.replace("/", "//"); 122 query = query.replace("%", "/%"); 123 query = query.replace("&", "/&"); 124 query = query.replace("'", "''"); 125 query = query.replace("(", "/("); 126 query = query.replace(")", "/)"); 127 query = query.replace("_", "/_"); 128 return query; 129 } 130 131 /** 132 * Gather search data from providers 133 * 134 * @param searchData - search data to be grouped 135 */ 136 @Log 137 private async gatherSearchData(): Promise<SearchData[]> { 138 // init uriConfigMapping 139 const uriConfigMapping = new Map(); 140 this.searchDataConfig.forEach(config => { 141 uriConfigMapping.set(config.settingUri, config); 142 }) 143 144 // get searchData (note: settingList must be the first) 145 let searchData: SearchData[] = await this.getSearchData(this.searchDataConfig[0].settingUri, '', uriConfigMapping); 146 LogUtil.log('settings search searchData: ' + JSON.stringify(searchData)); 147 148 return searchData; 149 } 150 151 /** 152 * Initialize rdb search data 153 * 154 * @param searchData - search data 155 */ 156 @Log 157 private async initRdbSearchData(searchData: SearchData[]): Promise<void> { 158 LogUtil.log('settings search old data if exists.'); 159 await this.initRdb(); 160 161 // delete old data if exists 162 await this.rdbStore.executeSql(SearchConfig.search.SQL_DELETE_ALL, null); 163 LogUtil.log('settings search old data if exists.'); 164 165 // insert new data into table 166 for (let data of searchData) { 167 const valueBucket = {}; 168 valueBucket[SearchConfig.search.FIELD_URI] = data.uri; 169 valueBucket[SearchConfig.search.FIELD_ICON] = data.icon; 170 valueBucket[SearchConfig.search.FIELD_TITLE] = data.title; 171 valueBucket[SearchConfig.search.FIELD_KEYWORD] = data.keyword; 172 valueBucket[SearchConfig.search.FIELD_SUMMARY] = data.summary; 173 valueBucket[SearchConfig.search.FIELD_SYNONYM] = data.synonym; 174 let ret = await this.rdbStore.insert(SearchConfig.search.TABLE_NAME, valueBucket) 175 LogUtil.log("settings: search insert data: " + ret) 176 } 177 } 178 179 /** 180 * Get search data 181 * 182 * @param settingUri - setting uri 183 * @param settingIcon - setting icon 184 * @param uriConfigMapping - uri config mapping 185 */ 186 @Log 187 private async getSearchData(settingUri: string, settingIcon: string, uriConfigMapping: Map<string, any>): Promise<SearchData[]> { 188 if (!uriConfigMapping.get(settingUri)) { 189 return []; 190 } 191 192 // get search data from provider 193 const provider: SearchDataProvider = uriConfigMapping.get(settingUri).provider; 194 let searchProviderData: SearchData[] = await provider.getSearchData(); 195 196 // set uri, icon 197 searchProviderData.forEach(providerData => { 198 if (!providerData.uri) { 199 providerData.uri = settingUri; 200 } 201 providerData.icon = settingIcon; 202 }); 203 204 // get setting list 205 let settingConfig = ConfigData.FILE_URI.concat(uriConfigMapping.get(settingUri).settingRawJson) 206 let settingList: BaseData[] = this.getSettingList(settingConfig); 207 208 // get child page search 209 for (const setting of settingList) { 210 const icon = settingIcon ? settingIcon : setting.settingIcon; // higher priority for parent icon 211 const childData = await this.getSearchData(setting.settingUri, icon, uriConfigMapping); 212 searchProviderData = searchProviderData.concat(childData); 213 }; 214 215 return searchProviderData; 216 } 217 218 /** 219 * Get setting list which includes settingUri in each setting 220 * 221 * @param settingConfig - setting config path 222 */ 223 @Log 224 private getSettingList(settingConfig: string): BaseData[] { 225 let result = []; 226 227 let settingList: any[] = BaseParseConfModel.getJsonData(settingConfig); 228 settingList.forEach(setting => { 229 if (setting.settingUri) { 230 let baseData = new BaseData(); 231 baseData.settingIcon = setting.settingIcon; 232 baseData.settingUri = setting.settingUri; 233 result.push(baseData); 234 } 235 }); 236 237 return result; 238 } 239 240} 241