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 */ 15 16import ConfigData from '../../../../../../utils/src/main/ets/default/baseUtil/ConfigData'; 17import Log from '../../../../../../utils/src/main/ets/default/baseUtil/LogDecorator'; 18import { BaseData } from '../../../../../../utils/src/main/ets/default/bean/BaseData'; 19import LogUtil from '../../../../../../utils/src/main/ets/default/baseUtil/LogUtil'; 20import { GlobalContext } from '../../../../../../utils/src/main/ets/default/baseUtil/GlobalContext'; 21import SearchConfig from '../common/SearchConfig'; 22import SearchData from '../model/SearchData'; 23import SearchDataProvider from '../provider/SearchDataProvider'; 24import BaseParseConfModel from '../../../../../../utils/src/main/ets/default/model/BaseParseConfModel'; 25import ohosDataRdb from '@ohos.data.rdb'; 26import featureAbility from '@ohos.ability.featureAbility'; 27 28/** 29 * Search service class 30 */ 31export default class SearchModel { 32 private rdbStore; 33 private searchDataConfig; 34 35 constructor(configData: Array<Object>) { 36 this.searchDataConfig = configData; 37 } 38 39 /** 40 * Initialize search data 41 */ 42 @Log 43 public async initSearchData(): Promise<void> { 44 const searchData = await this.gatherSearchData(); 45 await this.initRdbSearchData(searchData); 46 } 47 48 /** 49 * Search 50 * 51 * @param query - query content 52 */ 53 @Log 54 public async search(query: string): Promise<SearchData[]> { 55 if (!query) { 56 return [] 57 } 58 query = this.sqlLikeCharReplace(query); 59 await this.initRdb(); 60 61 // query search data 62 let predicates = new ohosDataRdb.RdbPredicates(SearchConfig.search.TABLE_NAME); 63 predicates.like(SearchConfig.search.FIELD_KEYWORD, `%${query}%`) 64 .or().like(SearchConfig.search.FIELD_SUMMARY, `%${query}%`) 65 .or().like(SearchConfig.search.FIELD_SYNONYM, `%${query}%`) 66 .orderByAsc(SearchConfig.search.FIELD_URI); 67 68 let resultSet = null; 69 try { 70 resultSet = await this.rdbStore.query(predicates, [ 71 SearchConfig.search.FIELD_ICON, 72 SearchConfig.search.FIELD_TITLE, 73 SearchConfig.search.FIELD_KEYWORD, 74 SearchConfig.search.FIELD_SUMMARY, 75 SearchConfig.search.FIELD_URI 76 ]); 77 LogUtil.log("settings: query data: " + resultSet); 78 } catch(err) { 79 LogUtil.error('query data err.' + err.message); 80 return []; 81 } 82 // build search data from resultSet 83 let searchData: SearchData[] = []; 84 while (resultSet.goToNextRow()) { 85 const data = new SearchData(); 86 data.icon = resultSet.getString(0); 87 data.title = resultSet.getString(1); 88 data.keyword = resultSet.getString(2); 89 data.summary = resultSet.getString(3); 90 data.uri = resultSet.getString(4); 91 searchData.push(data); 92 } 93 resultSet.close(); 94 resultSet = null; 95 96 return searchData; 97 } 98 99 /** 100 * Initialize rdb 101 */ 102 @Log 103 private async initRdb(): Promise<void> { 104 LogUtil.log('settings initRdb.'); 105 106 if (!this.rdbStore) { 107 LogUtil.log('settings RDB has not been initialized yet.'); 108 109 // database init 110 const STORE_CONFIG = { 111 name: SearchConfig.RDB_NAME 112 }; 113 let context = GlobalContext.getContext().getObject(GlobalContext.globalKeySettingsAbilityContext) as Context; 114 this.rdbStore = await ohosDataRdb.getRdbStore(context, STORE_CONFIG, 1); 115 LogUtil.log('settings SettingsSearch.db is ready.'); 116 117 // table SEARCH_DATA init 118 try { 119 await this.rdbStore.executeSql(SearchConfig.search.DDL_TABLE_CREATE, null); 120 } catch(err) { 121 LogUtil.error(`settings: executeSql err ${err.message}`); 122 } 123 LogUtil.log('settings table SEARCH_DATA is ready.'); 124 } 125 } 126 127 /** 128 * Handle special characters for sql.like 129 * 130 * @param query - query content 131 */ 132 @Log 133 private sqlLikeCharReplace(query: string): string { 134 query = query.replace("[", "/["); 135 query = query.replace("]", "/]"); 136 query = query.replace("/", "//"); 137 query = query.replace("%", "/%"); 138 query = query.replace("&", "/&"); 139 query = query.replace("'", "''"); 140 query = query.replace("(", "/("); 141 query = query.replace(")", "/)"); 142 query = query.replace("_", "/_"); 143 return query; 144 } 145 146 /** 147 * Gather search data from providers 148 * 149 * @param searchData - search data to be grouped 150 */ 151 @Log 152 private async gatherSearchData(): Promise<SearchData[]> { 153 // init uriConfigMapping 154 const uriConfigMapping = new Map(); 155 this.searchDataConfig.forEach(config => { 156 uriConfigMapping.set(config.settingUri, config); 157 }) 158 159 // get searchData (note: settingList must be the first) 160 let searchData: SearchData[] = await this.getSearchData(this.searchDataConfig[0].settingUri, '', uriConfigMapping); 161 LogUtil.log('settings search searchData: ' + JSON.stringify(searchData)); 162 163 return searchData; 164 } 165 166 /** 167 * Initialize rdb search data 168 * 169 * @param searchData - search data 170 */ 171 @Log 172 private async initRdbSearchData(searchData: SearchData[]): Promise<void> { 173 LogUtil.log('settings search old data if exists.'); 174 await this.initRdb(); 175 176 // delete old data if exists 177 try { 178 await this.rdbStore.executeSql(SearchConfig.search.SQL_DELETE_ALL, null); 179 } catch(err) { 180 LogUtil.error("settings: executeSql DELETE err " + err.message); 181 } 182 LogUtil.log('settings search old data if exists.'); 183 184 // insert new data into table 185 for (let data of searchData) { 186 const valueBucket = {}; 187 valueBucket[SearchConfig.search.FIELD_URI] = data.uri; 188 valueBucket[SearchConfig.search.FIELD_ICON] = data.icon; 189 valueBucket[SearchConfig.search.FIELD_TITLE] = data.title; 190 valueBucket[SearchConfig.search.FIELD_KEYWORD] = data.keyword; 191 valueBucket[SearchConfig.search.FIELD_SUMMARY] = data.summary; 192 valueBucket[SearchConfig.search.FIELD_SYNONYM] = data.synonym; 193 try { 194 let ret = await this.rdbStore.insert(SearchConfig.search.TABLE_NAME, valueBucket); 195 LogUtil.log("settings: search insert data: " + ret); 196 } catch(err) { 197 LogUtil.error('insert data err ' + err.message); 198 } 199 } 200 } 201 202 /** 203 * Get search data 204 * 205 * @param settingUri - setting uri 206 * @param settingIcon - setting icon 207 * @param uriConfigMapping - uri config mapping 208 */ 209 @Log 210 private async getSearchData(settingUri: string, settingIcon: string, uriConfigMapping: Map<string, any>): Promise<SearchData[]> { 211 if (!uriConfigMapping.get(settingUri)) { 212 return []; 213 } 214 215 // get search data from provider 216 const provider: SearchDataProvider = uriConfigMapping.get(settingUri).provider; 217 let searchProviderData: SearchData[] = await provider.getSearchData(); 218 219 // set uri, icon 220 searchProviderData.forEach(providerData => { 221 if (!providerData.uri) { 222 providerData.uri = settingUri; 223 } 224 providerData.icon = settingIcon; 225 }); 226 227 // get setting list 228 let settingConfig = ConfigData.FILE_URI.concat(uriConfigMapping.get(settingUri).settingRawJson) 229 let settingList: BaseData[] = this.getSettingList(settingConfig); 230 231 // get child page search 232 for (const setting of settingList) { 233 const icon = settingIcon ? settingIcon : setting.settingIcon; // higher priority for parent icon 234 const childData = await this.getSearchData(setting.settingUri, icon, uriConfigMapping); 235 searchProviderData = searchProviderData.concat(childData); 236 } 237 ; 238 239 return searchProviderData; 240 } 241 242 /** 243 * Get setting list which includes settingUri in each setting 244 * 245 * @param settingConfig - setting config path 246 */ 247 @Log 248 private getSettingList(settingConfig: string): BaseData[] { 249 let result = []; 250 251 let settingList: any[] = BaseParseConfModel.getJsonData(settingConfig); 252 settingList.forEach(setting => { 253 if (setting.settingUri) { 254 let baseData = new BaseData(); 255 baseData.settingIcon = setting.settingIcon; 256 baseData.settingUri = setting.settingUri; 257 result.push(baseData); 258 } 259 }); 260 261 return result; 262 } 263} 264