diff --git a/ohos/.eslintignore b/ohos/.eslintignore new file mode 100644 index 00000000..45014887 --- /dev/null +++ b/ohos/.eslintignore @@ -0,0 +1,11 @@ +hvigor/hvigor-wrapper.js +entry/src/main/ets/entryability/EntryAbility.ts +lib_flexsearch-ohos/Index.d.ts +lib_flexsearch-ohos/src/lang/latin/balance.js +lib_flexsearch-ohos/src/intersect.js +lib_flexsearch-ohos/src/type.js +lib_flexsearch-ohos/src/index.js +lib_flexsearch-ohos/src/cache.js +lib_flexsearch-ohos/src/lang/latin/advanced.js +lib_flexsearch-ohos/src/lang.js +lib_flexsearch-ohos/src/document.js \ No newline at end of file diff --git a/ohos/.gitignore b/ohos/.gitignore new file mode 100644 index 00000000..23b16336 --- /dev/null +++ b/ohos/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/package-lock.json +**/oh-package-lock.json5 +**/oh_modules \ No newline at end of file diff --git a/ohos/AppScope/app.json5 b/ohos/AppScope/app.json5 new file mode 100644 index 00000000..13ff3f2e --- /dev/null +++ b/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "cn.openharmony.flexsearch", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/ohos/AppScope/resources/base/element/string.json b/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 00000000..4d0c5fc8 --- /dev/null +++ b/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "flexsearch" + } + ] +} diff --git a/ohos/AppScope/resources/base/media/app_icon.png b/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 00000000..ce307a88 Binary files /dev/null and b/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/ohos/CHANGELOG.md b/ohos/CHANGELOG.md new file mode 100644 index 00000000..ca82351e --- /dev/null +++ b/ohos/CHANGELOG.md @@ -0,0 +1,39 @@ +## 2.0.1 +- Add obfuscation configuration to the document +- Switch the repository address to gitcode + +## 2.0.0 +* 名称由flexsearch-ohos修改为flexsearch。 +* 旧的包flexsearch-ohos已不维护,请使用新包flexsearch。 +* 工程由api8升级到api9 +* 适配DevEco Studio 版本: 4.1 Canary(4.1.3.317), OpenHarmony SDK: API11 (4.1.0.36) +* 补充XTS单元测试用例 + + +## 1.0.2 +新增OpenHarmony关键字 + + +## 1.0.1 +适配了ark引擎上无法运行的问题 +* fix:空格正则表达式由ECMA2018语法不支持的问题。 +* fix:ark引擎不支持new Function()导致的worker功能无法使用问题。 + +## 1.0.0 +适配兼容OpenHarmony系统,修复相关问题,具体如下: +* 源库Async方法在sdk6上运行会出错,但sdk7以上运行没问题,做了sdk6的兼容。 +* 源库worker功能切换到ark环境上,删除了浏览器和node环境worker功能相关的代码。 +* 源库Index.import方法无效的问题修复。 +* 源库入口文件src/webpack.js重命名为src/flexsearch.js,并修改adm和commonJS方式的模块化为ES6方式的模块化。 + +对源库文件改动如下: +* 删除原库src/config目录所有文件,该目录代码用于打包成浏览器上使用的不同功能版本的js bundle,OpenHarmony环境支持全部,无需多种配置。 +* 删除原库src/worker/handler.js和src/worker/node.js和src/worker/worker.js文件,该代码为浏览器和node环境上的worker相关代码。 +* 新增src/worker/ark.js,新增OpenHarmony环境上的worker功能实现。 +* 修改该src/worker/index.js,修改worker功能相关代码以适配OpenHarmony环境。 +* 修改src/async.js,兼容sdk6异步方法无效问题。 +* 删除src/engine.js,该代码ES6模块化中无用。 +* 修改src/index.js,Fix源库import方法无效问题。 +* 删除src/polyfill.js,浏览器环境做es5兼容相关的代码。 +* 修改src/webpack.js, 重命名并修改模块化方式。 + diff --git a/ohos/LICENSE b/ohos/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/ohos/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ohos/NOTICE b/ohos/NOTICE new file mode 100644 index 00000000..69688404 --- /dev/null +++ b/ohos/NOTICE @@ -0,0 +1,192 @@ +OPEN SOURCE SOFTWARE NOTICE + +Please note we provide an open source software notice for the third party open source software along with this software and/or this software component (in the following just “this SOFTWARE”). The open source software licenses are granted by the respective right holders. + +Warranty Disclaimer +THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + +Copyright Notice and License Texts + +---------------------------------------------------------------------- +Software: flexsearch-android 6.1.0 + +Copyright notice: +Copyright 2018-2023 Thomas Wilkerling, Hosted by Nextapps GmbH + +License : Apache License 2.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/ohos/OAT.xml b/ohos/OAT.xml new file mode 100644 index 00000000..b2bd40e2 --- /dev/null +++ b/ohos/OAT.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ohos/README.OpenSource b/ohos/README.OpenSource new file mode 100644 index 00000000..1516c3da --- /dev/null +++ b/ohos/README.OpenSource @@ -0,0 +1,11 @@ +[ + { + "Name": "flexsearch", + "License": "Apache License", + "License File": " LICENSE ", + "Version Number": "0.7.2", + "Owner": "xiafeng@huawei.com", + "Upstream URL": "https://github.com/nextapps-de/flexsearch/", + "Description": "Next-Generation full text search library for Browser and Node.js" + } +] \ No newline at end of file diff --git a/ohos/README.md b/ohos/README.md new file mode 100644 index 00000000..12a2b121 --- /dev/null +++ b/ohos/README.md @@ -0,0 +1,329 @@ +# FlexSearch + +## Introduction + +This project is an OpenHarmony-adapted version that is transplanted based on the open-source JS library [FlexSearch](https://github.com/nextapps-de/flexsearch/tree/0.7.2). + +FlexSearch is a fast, full-text search library with zero dependencies. When it comes to raw search speed, FlexSearch outperforms every single searching library out there +and also provides flexible search capabilities like multi-field search, phonetic transformations, or partial matching. Depending on the used options, it also provides the most memory-efficient index. +FlexSearch introduces a new scoring algorithm called "contextual index" based on a pre-scored lexical dictionary architecture, which greatly improves the query speed in actual use compared with other libraries. +FlexSearch also provides you a non-blocking asynchronous processing model as well as web workers to perform any updates or queries on the index in parallel through dedicated balanced threads. + +## How to Install + +```shell +ohpm install @ohos/flexsearch +``` + +For details about the OpenHarmony ohpm environment configuration, see [OpenHarmony HAR](https://gitcode.com/openharmony-tpc/docs/blob/master/OpenHarmony_har_usage.en.md). + +## How to Use + +### Quick Start + +Use **Index** for single-field search and **Document** for multi-field search. The following uses **Document** as an example. + +```javascript +import FlexSearch, { StoreOption, IndexOptionsForDocumentSearch } from "@ohos/flexsearch"; +import docs, { DocsDataType } from './docsData'; + +// DocsDataType is a custom multi-field data type. For details, see entry/src/main/ets/model/docsModel.ets. +const options: IndexOptionsForDocumentSearch = { + // The CJK encoding mode or user-defined mode is required for Chinese search. + charset: "cjk", + document: { + id: "url", + tag: "tag", + index: ["title", "content"], + store: ["url", "title"], + } +} + +const index = new FlexSearch.Document(options); + +// For details about DocsDataType, see entry/src/main/ets/model/docsData.ets. +const doc1 = new DocsDataType( + "url1", + "Pandemic", + "The pandemic affects 8 provinces. CDC experts remind you of the pandemic prevention in winter...", + "Elephant News reports that in just three days, it has... On October 18, the Shaanxi Province Health Commission reported..." +); +index.add(doc1); +let result = index.search("Pandemic", { + limit: 100, + offset: 0, + suggest: false, + index: ["title", "content"], + tag: false, + enrich: false, + bool: "or", +}); + +// Search in asynchronous callback mode. +index.searchAsync( + "Pandemic", + { + limit: 100, + offset: 0, + suggest: false, + index: ["title", "content"], + tag: false, + enrich: false, + bool: "or", + }, + function (result) { + console.log(result); + } +); + +// Search in asynchronous promise mode. +index + .searchAsync("Pandemic", { + limit: 100, + offset: 0, + suggest: false, + index: ["title", "content"], + tag: false, + enrich: false, + bool: "or", + }) + .then(function (result) { + console.log(result); + }); + +// Search in async await mode. +async function search() { + const results = await index.searchAsync("Pandemic", { + limit: 100, + offset: 0, + suggest: false, + index: ["title", "content"], + tag: false, + enrich: false, + bool: "or", + }); + console.log(result); +} +``` + +For details about the parameter **options**, see [FlexSearch README](https://github.com/nextapps-de/flexsearch/tree/0.7.2). + +### Available APIs + +Global methods: + +- FlexSearch.registerCharset(name, charset)(id, string) \* +- FlexSearch.registerLanguage(name, language) \* + +Index methods: + +- Index.**add**(id, string) \* +- Index.**append**(id, string) \* +- Index.**update**(id, string) \* +- Index.**remove**(id) \* +- Index.**search**(string, \, \) \* +- Index.**search**(options) \* +- _async_ Index.**export**(handler) +- _async_ Index.**import**(key, data) + +WorkerIndex methods: + +- _async_ Index.**add**(id, string) +- _async_ Index.**append**(id, string) +- _async_ Index.**update**(id, string) +- _async_ Index.**remove**(id) +- _async_ Index.**search**(string, \, \) +- _async_ Index.**search**(options) +- _async_ ~~Index.**export**(handler)~~ (WIP) +- _async_ ~~Index.**import**(key, data)~~ (WIP) + +Document methods: + +- Document.**add**(\, document) \* +- Document.**append**(\, document) \* +- Document.**update**(\, document) \* +- Document.**remove**(id || document) \* +- Document.**search**(string, \, \) \* +- Document.**search**(options) \* +- _async_ Document.**export**(handler) +- _async_ Document.**import**(key, data) + +\* indicates that for each of those methods there exists an asynchronous equivalent. + +Async version: + +- _async_ .**addAsync**( ... , \) +- _async_ .**appendAsync**( ... , \) +- _async_ .**updateAsync**( ... , \) +- _async_ .**removeAsync**( ... , \) +- _async_ .**searchAsync**( ... , \) + +Async methods will return a `Promise`. Alternatively, you can pass a callback function as the last parameter. + +Methods `export` and `import` are always async as well as every method you call on a Worker-based Index. + +### Worker Function Description + +**Use FlexSearch.Worker instead of FlexSearch.Index during the single-field search using Worker.** +Due to the serialization problem of data transfer between threads, the **encode** attribute (value is **function**) in the **charset** object is ignored. In this case, the **charset** object needs to be split and transferred, and **charset.encode** needs to be specially processed, that is, **function.name** is transferred instead of **function**. + +```javascript +import FlexSearch from "@ohos/flexsearch"; +import charsetDefault from "@ohos/flexsearch/src/lang/latin/default.js"; + +const index = new FlexSearch.Worker({ + encode: charsetDefault.encode.name, + tokenize: charsetDefault.tokenize, + rtl: charsetDefault.rtl, +}); +``` + +In the project directory **entry**, create the directory **workers** at the same level as the directory **pages**, and create a **worker.js** file in **workers**. + +```javascript +// worker.js +import charsetDefault from "@ohos/flexsearch/src/lang/latin/default.js"; +import { + setEncode, + handler, + parentPort, +} from "@ohos/flexsearch/src/worker/ark.js"; + +setEncode(charsetDefault.encode); +parentPort.onmessage = handler; +``` + +**Use the Document object for multi-field search, which requires enabling Worker in configuration of a build object.** + +```javascript +import FlexSearch, { StoreOption } from "@ohos/flexsearch"; +import charsetCJK from "@ohos/flexsearch/src/lang/cjk/default.js"; + +const options: IndexOptionsForDocumentSearch = { + rtl:false, + tokenize: 'strict', + encode: false, + worker:true +} + +const index = new FlexSearch.Document(options); +``` + +In the project directory **entry**, create the directory **workers** at the same level as the directory **pages**, and create a **worker.js** file in **workers**. +**worker.js** can be replaced with another file name, but the preceding configuration also needs to be modified as it changes. For example, if the file name is **myworker.js**, the preceding configuration is modified to **new Document({worker: "workers/myworker.js"});**. + +```javascript +import charsetCJK from "@ohos/flexsearch/src/lang/cjk/default.js"; +import { + setEncode, + handler, + parentPort, +} from "@ohos/flexsearch/src/worker/ark.js"; + +setEncode(charsetCJK.encode); +parentPort.onmessage = handler; +``` + +### Description of charset and lang + +When the library is initialized, only the following five charsets are registered by default: + +```javascript +// flexsearch.js +if (SUPPORT_ENCODER) { + registerCharset("latin:default", charset_default); + registerCharset("latin:simple", charset_simple); + registerCharset("latin:balance", charset_balance); + registerCharset("latin:advanced", charset_advanced); + registerCharset("latin:extra", charset_extra); + registerCharset("cjk:default", charset_cjk); +} +``` + +Therefore, when **charset** uses a string as the value, only the preceding five types can be used by default. **latin** indicates **latin:default**, and **cjk** indicates **cjk:default**. +Alternatively, if you need to use another built-in **charset** or a custom **charset**, perform global registration first. For example: + +```javascript +import FlexSearch from "@ohos/flexsearch"; +import charsetArabic from "@ohos/flexsearch/dist/module/lang/arabic/default.js"; +import langEN from "@ohos/flexsearch/dist/module/lang/en.js"; + +FlexSearch.registerCharset("arabic:default", charsetArabic); +FlexSearch.registerLanguage("en", langEN); + +const index = new FlexSearch.Index({ + charset: "arabic", + lang: "en", +}); +``` + +Directly use the **import** object. For example: + +```javascript +import FlexSearch from "@ohos/flexsearch"; +import charsetLatinDefault from "@ohos/flexsearch/dist/module/lang/latin/default.js"; +import langEN from "@ohos/flexsearch/dist/module/lang/en.js"; + +const index = new FlexSearch.Index({ + charset: charsetLatinDefault, + lang: langEN, +}); +``` + +## About obfuscation +- Code obfuscation, please see[Code Obfuscation](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/arkts-utils/source-obfuscation.md) +- If you want the flexsearch library not to be obfuscated during code obfuscation, you need to add corresponding exclusion rules in the obfuscation rule configuration file obfuscation-rules.txt: +``` +-keep +./oh_modules/@ohos/flexsearch +``` + +## Constraints + +- DevEco Studio: 4.1 Canary (4.1.3.317) +- OpenHarmony SDK: API 11 (4.1.0.36) + +## Directory Structure + +``` + ./entry/src/main/ets/MainAbility/ + │── library + │ └──src Library source code + │ └──lang Processing built-in languages and character sets + │ └──arabic Processing Arabic character sets + │ └──cjk Processing Chinese, Japanese, and Korean character sets + │ └──cyrillic Processing Cyrillic character sets + │ └──latin Processing Latin character sets + │ └──at.js Processing the Austrian language + │ └──de.js Processing the German language + │ └──en.js Processing British English + │ └──us.js Processing American English + │ └──worker Internal implementation of worker threads + │ └──ark.js Logic implementation of worker threads + │ └──index.js Logic implementation of WorkerIndex objects + │ └──async.js Public implementation logic of the Async method + │ └──cache.js Cache implementation logic + │ └──common.js Common utility functions + │ └──config.js Internal logic configurations + │ └──document.js Logic implementation of Document objects + │ └──flexsearch.js FlexSearch library entry file + │ └──global.js Global configurations + │ └──index.js Logic implementation of Index objects + │ └──intersect.js Processing logic of suggest in SearchOptions + │ └──lang.js Common functions for language processing + │ └──preset.js Processing logic of preset construction configuration items in Index + │ └──serialize.js Serialization processing logic such as import and export + │ └──type.js Type annotations + │── model demo logic processing and data files + │── pages page files + │── utils Common utilities + │── workers Worker scripts in the Ark environment +``` + +## License + +This project is licensed under [Apache License 2.0](https://gitcode.com/openharmony-tpc/flexsearch-ohos/blob/master/LICENSE). + +## How to Contribute + +If you find any problem when using the project, submit an [issue](https://gitcode.com/openharmony-tpc/flexsearch-ohos/issues) or a [PR](https://gitcode.com/openharmony-tpc/flexsearch-ohos/pulls). diff --git a/ohos/README_zh.md b/ohos/README_zh.md new file mode 100644 index 00000000..1163c08a --- /dev/null +++ b/ohos/README_zh.md @@ -0,0 +1,328 @@ +# FlexSearch + +## 简介 + +本项目是基于 js 库开源库 [FlexSearch](https://github.com/nextapps-de/flexsearch/tree/0.7.2) 进行移植的 OpenHarmony 适配版本。 + +FlexSearch 是一个快速、零依赖的全文搜索库。 在原始搜索速度方面,FlexSearch 优于每一个搜索库, +并提供灵活的搜索功能,如多字段搜索,语音转换或部分匹配。根据使用的选项,它还提供最高内存效率的索引。 +FlexSearch 引入了一种新的评分算法,称为“上下文索引”,基于预先评分的词典字典体系结构,与其它库相比,实际执行的查询速度有大幅度提高。 +FlexSearch 还为您提供非阻塞异步处理模型,以通过专用平衡线程并行地对索引执行任何更新或查询。 + +## 下载安装 + +```shell +ohpm install @ohos/flexsearch +``` + +OpenHarmony ohpm 环境配置等更多内容,请参考 [如何安装OpenHarmony ohpm 包](https://gitcode.com/openharmony-tpc/docs/blob/master/OpenHarmony_har_usage.md) + +## 使用说明 + +### 快速接入 + +单字段检索使用 Index 更简单,多字段检索使用 Document,以 Document 为例 + +```javascript +import FlexSearch, { StoreOption, IndexOptionsForDocumentSearch } from "@ohos/flexsearch"; +import docs, { DocsDataType } from './docsData'; + +// DocsDataType是自定义的多字段数据类型,详情参考entry/src/main/ets/model/docsModel.ets +const options: IndexOptionsForDocumentSearch = { + // 中文检索需要使用CJK编码方式或自定义 + charset: "cjk", + document: { + id: "url", + tag: "tag", + index: ["title", "content"], + store: ["url", "title"], + } +} + +const index = new FlexSearch.Document(options); + +// DocsDataType数据类型可参考entry/src/main/ets/model/docsData.ets +const doc1 = new DocsDataType( + "url1", + "流行病", + "流行病波及8省份!疾控专家提醒冬季流行病 ...", + "大象新闻消息,仅三天时间,已经 ... 10月18日,陕西省卫健委通报..." +); +index.add(doc1); +let result = index.search("流行病", { + limit: 100, + offset: 0, + suggest: false, + index: ["title", "content"], + tag: false, + enrich: false, + bool: "or", +}); + +// 异步版本搜索方法callback方式 +index.searchAsync( + "流行病", + { + limit: 100, + offset: 0, + suggest: false, + index: ["title", "content"], + tag: false, + enrich: false, + bool: "or", + }, + function (result) { + console.log(result); + } +); + +// 异步版本搜索方法promise方式 +index + .searchAsync("流行病", { + limit: 100, + offset: 0, + suggest: false, + index: ["title", "content"], + tag: false, + enrich: false, + bool: "or", + }) + .then(function (result) { + console.log(result); + }); + +// async await方式 +async function search() { + const results = await index.searchAsync("流行病", { + limit: 100, + offset: 0, + suggest: false, + index: ["title", "content"], + tag: false, + enrich: false, + bool: "or", + }); + console.log(result); +} +``` + +**关于配置项参数 options 的详细说明,原库 readme 中有详细的说明,请见**[FlexSearch readme](https://github.com/nextapps-de/flexsearch/tree/0.7.2) + +### API 预览 + +Global Methods: + +- FlexSearch.registerCharset(name, charset)(id, string) \* +- FlexSearch.registerLanguage(name, language) \* + +Index Methods: + +- Index.**add**(id, string) \* +- Index.**append**(id, string) \* +- Index.**update**(id, string) \* +- Index.**remove**(id) \* +- Index.**search**(string, \, \) \* +- Index.**search**(options) \* +- _async_ Index.**export**(handler) +- _async_ Index.**import**(key, data) + +WorkerIndex Methods: + +- _async_ Index.**add**(id, string) +- _async_ Index.**append**(id, string) +- _async_ Index.**update**(id, string) +- _async_ Index.**remove**(id) +- _async_ Index.**search**(string, \, \) +- _async_ Index.**search**(options) +- _async_ ~~Index.**export**(handler)~~ (WIP) +- _async_ ~~Index.**import**(key, data)~~ (WIP) + +Document Methods: + +- Document.**add**(\, document) \* +- Document.**append**(\, document) \* +- Document.**update**(\, document) \* +- Document.**remove**(id || document) \* +- Document.**search**(string, \, \) \* +- Document.**search**(options) \* +- _async_ Document.**export**(handler) +- _async_ Document.**import**(key, data) + +\* 表示此方法存在一个等价的异步方法 + +Async Version: + +- _async_ .**addAsync**( ... , \) +- _async_ .**appendAsync**( ... , \) +- _async_ .**updateAsync**( ... , \) +- _async_ .**removeAsync**( ... , \) +- _async_ .**searchAsync**( ... , \) + +异步方法会返回一个 `Promise`, 或者,您可以将回调函数作为最后一个参数传递. + +方法`export`和`import`始终是异步的,您在基于 Worker 的索引上调用的每个方法也都是异步的. + +### worker 功能使用说明 + +**单字段检索使用使用 worker 功能需用 FlexSearch.Worker 代替 FlexSearch.Index** +由于线程间数据传递序列化问题,charset 对象里的 encode 属性(value 为 function)会被忽略,此时需将 charset 对象拆分传递,并且将 charset.encode 做个特殊处理,不传递 function 改为传递 function.name + +```javascript +import FlexSearch from "@ohos/flexsearch"; +import charsetDefault from "@ohos/flexsearch/src/lang/latin/default.js"; + +const index = new FlexSearch.Worker({ + encode: charsetDefault.encode.name, + tokenize: charsetDefault.tokenize, + rtl: charsetDefault.rtl, +}); +``` + +然后 entry 工程目录里,与 pages 目录同级创建 workers 目录,然后在 workers 目录里新建 worker.js 文件 + +```javascript +// worker.js +import charsetDefault from "@ohos/flexsearch/src/lang/latin/default.js"; +import { + setEncode, + handler, + parentPort, +} from "@ohos/flexsearch/src/worker/ark.js"; + +setEncode(charsetDefault.encode); +parentPort.onmessage = handler; +``` + +**多字段检索还是使用 Document 对象,需在构建对象的配置里设置开启 worker** + +```javascript +import FlexSearch, { StoreOption } from "@ohos/flexsearch"; +import charsetCJK from "@ohos/flexsearch/src/lang/cjk/default.js"; + +const options: IndexOptionsForDocumentSearch = { + rtl:false, + tokenize: 'strict', + encode: false, + worker:true +} + +const index = new FlexSearch.Document(options); +``` + +然后 entry 工程目录里,与 pages 目录同级创建 workers 目录,然后在 workers 目录里新建 worker.js 文件 +worker.js 也可为其它文件名,但是上面的配置也需改动,如 myworker.js,则上面配置为 new Document({worker: "workers/myworker.js"}); + +```javascript +import charsetCJK from "@ohos/flexsearch/src/lang/cjk/default.js"; +import { + setEncode, + handler, + parentPort, +} from "@ohos/flexsearch/src/worker/ark.js"; + +setEncode(charsetCJK.encode); +parentPort.onmessage = handler; +``` + +### 关于 charset 和 lang 的说明 + +本库初始化时,仅默认注册了 5 种 charset,如下: + +```javascript +// flexsearch.js +if (SUPPORT_ENCODER) { + registerCharset("latin:default", charset_default); + registerCharset("latin:simple", charset_simple); + registerCharset("latin:balance", charset_balance); + registerCharset("latin:advanced", charset_advanced); + registerCharset("latin:extra", charset_extra); + registerCharset("cjk:default", charset_cjk); +} +``` + +故 charset 以字符串为值的时候,只能默认使用以上 5 种,'latin'即'latin:default','cjk'即'cjk:default'。 +或者为了方便,在需要使用到其它内置的 charset 或自定义的 charset 时,也可先全局注册,eg: + +```javascript +import FlexSearch from "@ohos/flexsearch"; +import charsetArabic from "@ohos/flexsearch/dist/module/lang/arabic/default.js"; +import langEN from "@ohos/flexsearch/dist/module/lang/en.js"; + +FlexSearch.registerCharset("arabic:default", charsetArabic); +FlexSearch.registerLanguage("en", langEN); + +const index = new FlexSearch.Index({ + charset: "arabic", + lang: "en", +}); +``` + +直接使用 import 对象的方式,eg: + +```javascript +import FlexSearch from "@ohos/flexsearch"; +import charsetLatinDefault from "@ohos/flexsearch/dist/module/lang/latin/default.js"; +import langEN from "@ohos/flexsearch/dist/module/lang/en.js"; + +const index = new FlexSearch.Index({ + charset: charsetLatinDefault, + lang: langEN, +}); +``` +## 关于混淆 +- 代码混淆,请查看[代码混淆简介](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/arkts-utils/source-obfuscation.md) +- 如果希望flexsearch库在代码混淆过程中不会被混淆,需要在混淆规则配置文件obfuscation-rules.txt中添加相应的排除规则: +``` +-keep +./oh_modules/@ohos/flexsearch +``` + +## 约束与限制 + +- DevEco Studio 版本: 4.1 Canary(4.1.3.317) +- OpenHarmony SDK: API11 (4.1.0.36) + +## 目录 + +``` + ./entry/src/main/ets/MainAbility/ + │── library + │ └──src 库源码目录 + │ └──lang 内置语言和字符集处理目录 + │ └──arabic 阿拉伯字符集处理目录 + │ └──cjk 中日韩字符集处理目录 + │ └──cyrillic 西里尔字符集处理目录 + │ └──latin 拉丁字符集处理目录 + │ └──at.js 奥地利语言处理 + │ └──de.js 德国语言处理 + │ └──en.js 英式英语处理 + │ └──us.js 美式英语处理 + │ └──worker worker线程内部实现目录 + │ └──ark.js worker线程逻辑实现 + │ └──index.js WorkerIndex对象逻辑实现 + │ └──async.js Async方法的公共实现逻辑 + │ └──cache.js 缓存实现逻辑 + │ └──common.js 通用工具函数 + │ └──config.js 内部逻辑配置 + │ └──document.js Document对象逻辑实现 + │ └──flexsearch.js flexsearch库入口文件 + │ └──global.js 全局配置 + │ └──index.js Index对象逻辑实现 + │ └──intersect.js SearchOptions中suggest的处理逻辑 + │ └──lang.js 语言处理的公共函数 + │ └──preset.js Index中preset构造配置项的处理逻辑 + │ └──serialize.js 序列化处理逻辑,如导入和导出 + │ └──type.js 用来做类型注释 + │── model demo逻辑处理和数据文件目录 + │── pages 页面文件目录 + │── utils 工具类目录 + │── workers ark环境worker脚本目录 +``` + +## 开源协议 + +本项目基于 [Apache License 2.0](https://gitcode.com/openharmony-tpc/flexsearch-ohos/blob/master/LICENSE) ,请自由地享受和参与开源。 + +## 贡献代码 + +使用过程中发现任何问题都可以提 [Issue](https://gitcode.com/openharmony-tpc/flexsearch-ohos/issues) 给组件,当然,也非常欢迎发 [PR](https://gitcode.com/openharmony-tpc/flexsearch-ohos/pulls) 共建。 diff --git a/ohos/TEST.md b/ohos/TEST.md new file mode 100644 index 00000000..ab8157e4 --- /dev/null +++ b/ohos/TEST.md @@ -0,0 +1,50 @@ +# flexsearch-ohos单元测试用例 + +该测试用例基于OpenHarmony系统下,自己编写用例进行单元测试。 + +单元测试用例覆盖情况 + +## Index单字段索引 +| 接口名 | 是否通过 |备注| +|:-----------------------------------------------------------------------------------------------------------:|:--------------:|:---:| + | add(id: Id, item: string) | PASS | | + | append(id: Id, item: string) | PASS | | + | update(id: Id, item: string) | PASS | | + | remove(target: Id) | PASS | | + | search(query: string, options?: Limit | SearchOptions) | PASS | | + | search(query: string,limit: number,options: SearchOptions) | PASS | | + | search(options: SearchOptions) | PASS | | + | contain(id: Id) | PASS | | + | export(handler: ExportHandler) | PASS | | + | import(id: Id, item: string) | PASS | | + | addAsync(id: Id, item: string, callback?: AsyncCallback) | PASS | | + | appendAsync(id: Id, item: string, callback?: AsyncCallback) | PASS | | + | updateAsync(id: Id, item: string, callback?: AsyncCallback) | PASS | | + | removeAsync(target: Id, callback?: AsyncCallback) | PASS | | + | searchAsync(query: string,options?: Limit | SearchOptions,callback?: AsyncCallback) | PASS | | + | searchAsync(query: string,limit: number,options?: Limit | SearchOptions) | PASS | | + | searchAsync(options: SearchOptions) | PASS | | + +## Documnet多字段索引 +| 接口名 | 是否通过 |备注| +|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---------------------------:|:---:| +| add(document: T) | PASS | | +| add(id: Id, document: T) | PASS | | +| append(document: T) | PASS | | +| append(id: Id, document: T) | PASS | | +| update(document: T) | PASS | | +| update(id: Id, document: T) | PASS | | +| remove(target: Id | T) | PASS | | +| search(query: string, limit?: number) | PASS | | +| search(query: string, options: string[] | Partial>) | PASS | | +| search(query: string,limit?: number,options?: Partial>) | PASS | | +| search(options: Partial>) | PASS | | +| export(handler: ExportHandler) | PASS | | +| import(id: Id, document: T) | PASS | | +| addAsync(id: Id, document: T, callback?: AsyncCallback) | PASS | | +| appendAsync(id: Id, document: T, callback?: AsyncCallback) | PASS | | +| updateAsync(id: Id, document: T, callback?: AsyncCallback) | PASS | | +| searchAsync(query: string,options: string[] | Partial>) | PASS | | +| searchAsync(query: string,limit?: number,) | T, callback?: AsyncCallback) | PASS | | +| searchAsync(query: string,limit: number,callback: AsyncCallback) | T, callback?: AsyncCallback) | PASS | | +| searchAsync(query: string,options: Partial>,callback: AsyncCallback>) | T, callback?: AsyncCallback) | PASS | | \ No newline at end of file diff --git a/ohos/build-profile.json5 b/ohos/build-profile.json5 new file mode 100644 index 00000000..f0668edb --- /dev/null +++ b/ohos/build-profile.json5 @@ -0,0 +1,41 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compileSdkVersion": 10, + "compatibleSdkVersion": 10, + "targetSdkVersion": 10, + "runtimeOS": "OpenHarmony" + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "library", + "srcPath": "./library" + } + ] +} \ No newline at end of file diff --git a/ohos/entry/.gitignore b/ohos/entry/.gitignore new file mode 100644 index 00000000..e2713a27 --- /dev/null +++ b/ohos/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/ohos/entry/build-profile.json5 b/ohos/entry/build-profile.json5 new file mode 100644 index 00000000..5d6654b1 --- /dev/null +++ b/ohos/entry/build-profile.json5 @@ -0,0 +1,34 @@ +{ + "apiType": "stageMode", + "buildOption": { + "sourceOption": { + "workers": [ + "./src/main/ets/workers/worker.js" + ] + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + // 混淆规则文件 + ] + } + } + } + } + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/ohos/entry/hvigorfile.ts b/ohos/entry/hvigorfile.ts new file mode 100644 index 00000000..c6edcd90 --- /dev/null +++ b/ohos/entry/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/ohos/entry/obfuscation-rules.txt b/ohos/entry/obfuscation-rules.txt new file mode 100644 index 00000000..d31b705e --- /dev/null +++ b/ohos/entry/obfuscation-rules.txt @@ -0,0 +1,26 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation + +-keep +./oh_modules/@ohos/flexsearch \ No newline at end of file diff --git a/ohos/entry/oh-package.json5 b/ohos/entry/oh-package.json5 new file mode 100644 index 00000000..41882a1c --- /dev/null +++ b/ohos/entry/oh-package.json5 @@ -0,0 +1,16 @@ +{ + "license": "ISC", + "devDependencies": {}, + "name": "entry", + "ohos": { + "org": "huawei", + "directoryLevel": "module", + "buildTool": "hvigor" + }, + "description": "example description", + "repository": {}, + "version": "2.0.1", + "dependencies": { + "@ohos/flexsearch": "file:../library" + } +} diff --git a/ohos/entry/src/main/ets/entryability/EntryAbility.ts b/ohos/entry/src/main/ets/entryability/EntryAbility.ts new file mode 100644 index 00000000..d7f69658 --- /dev/null +++ b/ohos/entry/src/main/ets/entryability/EntryAbility.ts @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import hilog from '@ohos.hilog'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import Want from '@ohos.app.ability.Want'; +import window from '@ohos.window'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/main', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy() { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground() { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground() { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/ohos/entry/src/main/ets/model/docsData.ets b/ohos/entry/src/main/ets/model/docsData.ets new file mode 100644 index 00000000..c5af5b35 --- /dev/null +++ b/ohos/entry/src/main/ets/model/docsData.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class DocsDataType { + public url: string; + public tag: string; + public title: string; + public content: string; + + constructor(url: string, tag: string, title: string, content: string) { + this.url = url; + this.tag = tag; + this.title = title; + this.content = content; + } +} +function getResourceString(res:Resource){ + return getContext().resourceManager.getStringSync(res.id) +} + +const docs: DocsDataType[] = [ + new DocsDataType('url1', getResourceString($r('app.string.Property')), getResourceString($r('app.string.title1')), + getResourceString($r('app.string.content1'))), + new DocsDataType('url2', getResourceString($r('app.string.Epidemic')), getResourceString($r('app.string.title2')), + getResourceString($r('app.string.content2'))), + new DocsDataType('url3', getResourceString($r('app.string.Livelihood')), getResourceString($r('app.string.title3')), + getResourceString($r('app.string.content3'))), + new DocsDataType('url4', getResourceString($r('app.string.Property')), getResourceString($r('app.string.title4')), + getResourceString($r('app.string.content4'))), + new DocsDataType('url5', getResourceString($r('app.string.Economy')), getResourceString($r('app.string.title5')), getResourceString($r('app.string.content5'))), + new DocsDataType('url6', getResourceString($r('app.string.Epidemic')), getResourceString($r('app.string.title6')), + getResourceString($r('app.string.content6'))), + new DocsDataType('url7', getResourceString($r('app.string.Epidemic')), getResourceString($r('app.string.title7')), + getResourceString($r('app.string.content7'))), + new DocsDataType('url8', getResourceString($r('app.string.Property')), getResourceString($r('app.string.title8')), + getResourceString($r('app.string.content8'))), + new DocsDataType('url9', getResourceString($r('app.string.Property')), getResourceString($r('app.string.title9')), getResourceString($r('app.string.content9'))), + new DocsDataType('url10', getResourceString($r('app.string.Property')), getResourceString($r('app.string.title10')), getResourceString($r('app.string.content10'))), + new DocsDataType('url11', getResourceString($r('app.string.Livelihood')), getResourceString($r('app.string.title11')), + getResourceString($r('app.string.content11'))) +]; + +export default docs diff --git a/ohos/entry/src/main/ets/model/docsModel.ets b/ohos/entry/src/main/ets/model/docsModel.ets new file mode 100644 index 00000000..b4bfb0ac --- /dev/null +++ b/ohos/entry/src/main/ets/model/docsModel.ets @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import FlexSearch, { + Id, + IndexOptionsForDocumentSearch, + SimpleDocumentSearchResultSetUnit, + StoreOption +} from '@ohos/flexsearch'; +import docs, { DocsDataType } from './docsData'; +import { logDebug } from '../utils/LogUtil'; + +const options: IndexOptionsForDocumentSearch = { + // 设置优化侧重方向,内存、性能、匹配、评分,default代表多方平衡 + preset: 'default', + + // 索引模式 + tokenize: 'strict', + + // 开启缓存,使用缓存的常见场景是键入时自动完成或即时搜索 + cache: 100, + + resolution: 9, + + // 是否开启上下文评分,仅在tokenize = 'strict'时可用 + context: false, + + // 开启内存优化 + optimize: true, + + charset: 'cjk', + + // 可自定义中文的stemmer、filter、matcher + language: undefined, + + // encode值为false 或者 '' 或者 自定义function,优先级会比charset['encode']高 + encode: false, + + // 词干器,中文不需要 + stemmer: false, + + // 停用词过滤器 + filter: ['的', '又', '及'], + + matcher: false, + + worker: false, + + // Document索引专有 + document: { + id: 'url', + tag: 'tag', + index: ['title', 'content'], + store: ['url', 'title'] + } +}; +const index = new FlexSearch.Document(options); + +for (let i = 0; i < docs.length; i++) { + index.add(docs[i]); +} + +export function oneFieldSearchTest(): SimpleDocumentSearchResultSetUnit[] { + + return index.search(getResourceString($r('app.string.Property2')), 100, { + limit: 100, + offset: 0, + suggest: false, + index: 'content', + enrich: true // 结果是否包含stored文档详情 + }); +} + +export function oneFieldSearchAsyncTest() { + + index.searchAsync(getResourceString($r('app.string.Property2')), { + limit: 100, + offset: 0, + suggest: false, + index: 'content', + enrich: true // 结果是否包含stored文档详情 + }).then((result)=> { + logDebug('result = ', result); + }); + +} + +export function multiFieldSearchTest(): SimpleDocumentSearchResultSetUnit[] { + + return index.search(getResourceString($r('app.string.Property2')), { + limit: 100, + offset: 0, + suggest: false, + index: ['title', 'content'], + enrich: false, + bool: 'or' + }); +} + +export function paginationQuery(offset: ESObject): SimpleDocumentSearchResultSetUnit[] { + + return index.search(getResourceString($r('app.string.housing')), { limit: 1, offset: offset, suggest: false }); +} + +export function searchByTag(): SimpleDocumentSearchResultSetUnit[] { + + return index.search({ tag: [getResourceString($r('app.string.Property')), getResourceString($r('app.string.Epidemic'))] }); +} + +function getResourceString(res:Resource){ + return getContext().resourceManager.getStringSync(res.id) +} + +export function storeGetTest(): DocsDataType[] { + + let stores: DocsDataType[]= []; + for (let i = 1; i <= docs.length; i++) { + stores.push(docs[i]); + } + return stores; + +} + +export function storeSetTest(): DocsDataType { + index.update(new DocsDataType("url1", getResourceString($r('app.string.Property')), "title", "")); + return docs[0]; +} + +export function appendTest(): SimpleDocumentSearchResultSetUnit[] { + index.add("appendTest", new DocsDataType("url15", getResourceString($r('app.string.montage')), getResourceString($r('app.string.Before_montage')), getResourceString($r('app.string.the_value_before_montage')))); + index.append("appendTest", new DocsDataType("url11", getResourceString($r('app.string.Property')), getResourceString($r('app.string.tail_title')), getResourceString($r('app.string.tail_content')))); + let result: SimpleDocumentSearchResultSetUnit[] = index.search(getResourceString($r("app.string.tail_connection")), { + index: ['title', 'content'], + enrich: true + }); + let tail_connection = getResourceString($r("app.string.tail_connection")); + logDebug(`append then search(${tail_connection}), result = `, JSON.stringify(result)); + return result; +} + +export function removeTest() { + const options: ESObject = { + limit: 100, + offset: 0, + suggest: false, + index: ['title', 'content'], + enrich: true, + bool: 'or' + }; + let res = index.search(getResourceString($r('app.string.Economy')),options); + logDebug('removeAsync result please check [before deletion]:', res) + index.removeAsync('url5').then((indexObj)=> { + let res1 = indexObj.search(getResourceString($r('app.string.Economy')),options); + logDebug('removeAsync result please check [after deletion]:', res1) + if (res1.length == 0) { + logDebug('removeAsync result please check [after deletion]:', "已删除...."); + } + storeGetTest(); + }); + +} + +export function updateTest() { + let options: ESObject = { + limit: 100, + offset: 0, + suggest: false, + index: ['title', 'content'], + tag: false, + enrich: false, + bool: 'or' + }; + index.add("updateTest", new DocsDataType("url55", getResourceString($r('app.string.update')), getResourceString($r('app.string.before_update')), getResourceString($r('app.string.value_before_update')))); + index.updateAsync("updateTest" + ,new DocsDataType("url2",getResourceString($r('app.string.Epidemic')),getResourceString($r('app.string.update_title')),getResourceString($r('app.string.update_content_value'))) + ).then((indexObj)=> { + let res = indexObj.search( getResourceString($r('app.string.update_content')),options); + logDebug('updateAsync result please check :', res) + storeGetTest(); + }); + +} + +export function searchCacheTest(): SimpleDocumentSearchResultSetUnit[] { + + return index.search(getResourceString($r('app.string.housing'))); +} + +const _map: Map = new Map(); +export function exportTest() { + + index.export((key, data)=> { + + logDebug('export log please check key = ', key); + logDebug('export log please check data = ', data); + _map.set(key, data); + }); +} + +export function importTest() { + + const index2 = new FlexSearch.Document(options); + + _map.forEach((value, key)=> { + index2.import(key, value); + }); + + index2.searchAsync(getResourceString($r('app.string.Property2')), { + limit: 100, + offset: 0, + suggest: false, + index: ['title', 'content'], + enrich: false, + bool: 'or' + }, (result) => { + logDebug("IMPORT log please check : ", result); + }); +} \ No newline at end of file diff --git a/ohos/entry/src/main/ets/model/indexData.ets b/ohos/entry/src/main/ets/model/indexData.ets new file mode 100644 index 00000000..433be271 --- /dev/null +++ b/ohos/entry/src/main/ets/model/indexData.ets @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const indexData: string[] = [ + 'a', 'a aa', 'a aa aaa', + 'b', 'b bb', 'b bb bbb', + 'c', 'c cc', 'c cc ccc', + 'd', 'd dd', 'd dd ddd', + 'e', 'e ee', 'e ee eee', + 'f', 'f ff', 'f ff fff', + 'g', 'g gg', 'g gg ggg', + 'h', 'h hh', 'h hh hhh', + 'i', 'i ii', 'i ii iii', + 'j', 'j jj', 'j jj jjj', + 'k', 'k kk', 'k kk kkk', + 'l', 'l ll', 'l ll lll', + 'm', 'm mm', 'm mm mmm', + 'n', 'n nn', 'n nn nnn', + 'o', 'o oo', 'o oo ooo', + 'p', 'p pp', 'p pp ppp', + 'q', 'q qq', 'q qq qqq' +]; + +export default indexData diff --git a/ohos/entry/src/main/ets/model/indexModel.ets b/ohos/entry/src/main/ets/model/indexModel.ets new file mode 100644 index 00000000..4fb3e51b --- /dev/null +++ b/ohos/entry/src/main/ets/model/indexModel.ets @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FlexSearch, { Id, IndexOptions, IndexSearchResult } from '@ohos/flexsearch'; +import indexData from './indexData'; +import langEN from '../utils/langEN' +import { logDebug } from '../utils/LogUtil'; + +FlexSearch.registerLanguage('en', langEN); +const options: IndexOptions = { + preset: 'default', + tokenize: 'strict', + cache: true, + resolution: 9, + context: false, + optimize: true, + boost: undefined, + charset: 'latin', + language: 'en', + encode: false, + stemmer: false, + filter: false, + matcher: false, +}; +const index = new FlexSearch.Index(options); +for (let i = 0; i < indexData.length; i++) { + asyncAddIndex(i, indexData[i]); +} + +function checkId(id: Id) { + return index.contain(id); +} + +function addIndex(id: Id, content: string): boolean { + + index.add(id, content); + return checkId(id); + +} + +function asyncAddIndex(id: Id, content: string) { + + index.addAsync(id, content, () => { + if (checkId(id)) { + logDebug('addAsync success: ', id); + } else { + logDebug('addAsync fail: ', id); + } + }); + + +} + +function appendIndex(id: Id, content: string) { + + index.append(id, content); +} + +function asyncAppendIndex(id: Id, content: string) { + + index.appendAsync(id, content, () => { + logDebug('appendAsync success: ', content); + }); + +} + +function updateIndex(id: Id, content: string) { + + index.update(id, content); +} + +function asyncUpdateIndex(id: Id, content: string) { + + index.updateAsync(id, content, () => { + logDebug('updateAsync success: ', content); + }); +} + + +function deleteIndex(id: Id): boolean { + + index.remove(id); + return checkId(id) + +} + +function asyncDeleteIndex(id: Id) { + + index.removeAsync(id, () => { + if (checkId(id)) { + logDebug('removeAsync fail: ', id); + } else { + logDebug('removeAsync success: ', id); + } + }); + +} + +function search(text: string) { + + return index.search(text); + +} + +function asyncSearch(query: string) { + + index.searchAsync(query).then((result: IndexSearchResult) => { + logDebug('searchAsync result = ', result); + }) + +} + +class ExportedData { + key: number | string = ''; + data: string = ''; + + constructor(key?: number | string, data?: string) { + if (key != undefined) { + this.key = key; + } + if (data != undefined) { + this.data = data; + } + } +} + +let exportedData: ExportedData[] = []; + +async function exportIndex() { + await index.export((key: string | number, data: string) => { + exportedData.push(new ExportedData(key, data)) + logDebug('exportIndex success, key = ', key); + logDebug('exportIndex success, data = ', data); + }) +} + +function importIndex() { + logDebug("importIndex exportedData.length : ", JSON.stringify(exportedData.length)); + const index2 = new FlexSearch.Index(options); + if (exportedData != null && exportedData != undefined && exportedData.length > 0) { + exportedData.forEach((exportedDataObj: ExportedData) => { + logDebug('importIndex success, key = ', exportedDataObj.key); + logDebug('importIndex success, data = ', exportedDataObj.data); + index2.import(exportedDataObj.key, exportedDataObj.data); + }) + } + setTimeout(() => { + + const result = index2.search('dd'); + logDebug('index2.search(dd) result = ', result); + }, 1000); + +} + +export class FunctionDataType { + name: string; + fun: Function; + + constructor(name: string, fun: Function) { + this.name = name; + this.fun = fun; + } +} + +const model: FunctionDataType[] = [ + new FunctionDataType('addIndex', addIndex), + new FunctionDataType('deleteIndex', deleteIndex), + new FunctionDataType('updateIndex', updateIndex), + new FunctionDataType('appendIndex', appendIndex), + new FunctionDataType('exportIndex', exportIndex), + new FunctionDataType('importIndex', importIndex), + new FunctionDataType('search', search) +] + +export default model; \ No newline at end of file diff --git a/ohos/entry/src/main/ets/model/workerIndexModel.ets b/ohos/entry/src/main/ets/model/workerIndexModel.ets new file mode 100644 index 00000000..d1d69352 --- /dev/null +++ b/ohos/entry/src/main/ets/model/workerIndexModel.ets @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FlexSearch, { Id, IndexOptions, IndexSearchResult, Worker } from '@ohos/flexsearch'; +import indexData from './indexData'; +import langEN from '../utils/langEN'; +import { logDebug } from '../utils/LogUtil'; +import { FunctionDataType } from './indexModel'; + +let options: IndexOptions = { + preset: 'default', + cache: true, + resolution: 9, + context: false, + optimize: true, + language: langEN, +}; +const index = new FlexSearch.Worker(options); + +for (let i = 0; i < indexData.length; i++) { + asyncAddIndex(i, indexData[i]); +} + +function asyncAddIndex(id: Id, content: string) { + + index.addAsync(id, content, ()=> { + logDebug('flexsearch---WorkerIndex.addAsync success: ', id); + }); +} + +function asyncAppendIndex(id: Id, content: string) { + + index.appendAsync(id, content, ()=> { + logDebug('WorkerIndex.appendAsync success: ', id); + }); +} + +function asyncUpdateIndex(id: Id, content: string) { + + index.updateAsync(id, content, ()=> { + logDebug('WorkerIndex.updateAsync success: ', content); + }); +} + + +function asyncDeleteIndex(id: Id) { + + index.removeAsync(id, ()=> { + logDebug('WorkerIndex.removeAsync success: ', id); + }); +} + +function asyncSearch(query: string) { + + index.searchAsync(query,10,(result:IndexSearchResult)=>{ + logDebug('WorkerIndex.searchAsync result = ', result); + }); +} + +const model: FunctionDataType[] = [ + new FunctionDataType('asyncAddIndex', asyncAddIndex), + new FunctionDataType('asyncAppendIndex', asyncAppendIndex), + new FunctionDataType('asyncDeleteIndex', asyncDeleteIndex), + new FunctionDataType('asyncUpdateIndex', asyncUpdateIndex), + new FunctionDataType('asyncSearch', asyncSearch), +] + +export default model; \ No newline at end of file diff --git a/ohos/entry/src/main/ets/pages/document.ets b/ohos/entry/src/main/ets/pages/document.ets new file mode 100644 index 00000000..814507ca --- /dev/null +++ b/ohos/entry/src/main/ets/pages/document.ets @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + oneFieldSearchTest, + oneFieldSearchAsyncTest, + multiFieldSearchTest, + searchByTag, + searchCacheTest, + appendTest, + exportTest, + importTest, + paginationQuery, + removeTest, + updateTest, + storeGetTest, + storeSetTest +} from '../model/docsModel'; +import docs from '../model/docsData'; + +@Entry +@Component + +struct DocumentPage { + @State rst: string = 'Document page' + @State pageNo: number = 0 + + @Builder MyButton(content: string, eventType: number) { + Button(content) + .size({ width: 160, height: 36 }) + .margin({ top: 10, right: 6, bottom: 0, left: 6 }) + .fontColor('white') + .fontSize(12) + .fontWeight(FontWeight.Bold) + .backgroundColor('#00a6ff') + .border({ width: 1, color: '#28c989', style: BorderStyle.Solid }) + .borderRadius(2) + .onClick(() => { + this.onMyButtonClick(eventType) + }) + } + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + Text($r('app.string.document_data')) + .alignSelf(ItemAlign.Start) + .fontSize(12) + .fontWeight(FontWeight.Bold) + Text(JSON.stringify(docs)) + .width('100%') + .height('26%') + .margin(2) + .borderWidth(1) + .borderColor('#28c989') + .borderRadius(4) + .fontSize(12) + .fontWeight(FontWeight.Normal) + .backgroundColor('#f3f1f1') + .maxLines(10) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + Row({ space: 10 }) { + this.MyButton(this.getResourceString($r('app.string.Single_field')), 0) + this.MyButton(this.getResourceString($r('app.string.Multi_field')), 1) + } + + Row({ space: 10 }) { + this.MyButton(this.getResourceString($r('app.string.Search_by_tag')), 2) + this.MyButton(this.getResourceString($r('app.string.page_search')), 3) + } + + Row({ space: 10 }) { + this.MyButton('removeAsyncTest', 4) + this.MyButton('updateAsyncTest', 5) + } + + Row({ space: 10 }) { + this.MyButton('storeGetTest', 6) + this.MyButton('storeSetTest', 7) + } + + Row({ space: 10 }) { + this.MyButton('appendTest', 8) + this.MyButton('searchCacheTest', 9) + } + + Row({ space: 10 }) { + this.MyButton('exportTest', 10) + this.MyButton(this.getResourceString($r('app.string.importTest')), 11) + } + + Text($r('app.string.Test_Result')) + .alignSelf(ItemAlign.Start) + .fontSize(12) + .fontWeight(FontWeight.Bold) + Text(this.rst) + .width('100%') + .height('30%') + .margin(2) + .borderWidth(1) + .borderColor('#28c989') + .borderRadius(4) + .fontSize(12) + .fontWeight(FontWeight.Normal) + .backgroundColor('#f3f1f1') + .maxLines(10) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + } + .width('100%') + .height('100%') + } + + getResourceString(res:Resource){ + return getContext().resourceManager.getStringSync(res.id) + } + + onMyButtonClick(eventType: number) { + switch (eventType) { + case 0: + this.rst = JSON.stringify(oneFieldSearchTest()) + break + case 1: + this.rst = JSON.stringify(multiFieldSearchTest()) + break + case 2: + this.rst = JSON.stringify(searchByTag()) + break + case 3: + let result = paginationQuery(this.pageNo); + this.rst = JSON.stringify(result); + if (result.length == 0) { + this.rst = this.getResourceString($r('app.string.Page_content_display_completed')); + } + this.pageNo++ + break + case 4: + removeTest() + this.rst = 'removeAsync method result, please look for log,search in hilog:removeAsync result please check' + break + case 5: + updateTest() + this.rst = 'updateAsync method result, please look for log,search in hilog:updateAsync result please check' + break + case 6: + this.rst = JSON.stringify(storeGetTest()) + break + case 7: + this.rst = JSON.stringify(storeSetTest()) + break + case 8: + this.rst = JSON.stringify(appendTest()) + break + case 9: + this.rst = JSON.stringify(searchCacheTest()) + break + case 10: + exportTest() + this.rst = 'export async method result, please look for log,search in hilog:export log please check' + break + case 11: + importTest() + this.rst = 'import async method result, please look for log,search in hilog:IMPORT log please check :' + break + } + } +} \ No newline at end of file diff --git a/ohos/entry/src/main/ets/pages/index.ets b/ohos/entry/src/main/ets/pages/index.ets new file mode 100644 index 00000000..10a8f8cc --- /dev/null +++ b/ohos/entry/src/main/ets/pages/index.ets @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import indexData from '../model/indexData'; +import model from '../model/indexModel'; + +const searchIndex: number = model.length - 1; + +@Entry +@Component +struct IndexPage { + @State rst: string = 'Index page'; + + @Builder MyButton(content: string, eventType: number) { + Button(content) + .size({ width: 100, height: 40 }) + .margin({ top: 10, right: 6, bottom: 0, left: 6 }) + .fontColor('white') + .fontSize(12) + .fontWeight(FontWeight.Bold) + .backgroundColor('#00a6ff') + .border({ width: 1, color: '#28c989', style: BorderStyle.Solid }) + .borderRadius(2) + .onClick(() => { + this.onMyButtonClick(eventType) + }) + } + + getResourceString(res:Resource){ + return getContext().resourceManager.getStringSync(res.id) + } + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + Text($r('app.string.document_data')) + .alignSelf(ItemAlign.Start) + .fontSize(12) + .fontWeight(FontWeight.Bold) + Text(JSON.stringify(indexData)) + .width('100%') + .height('26%') + .margin(2) + .borderWidth(1) + .borderColor('#28c989') + .borderRadius(4) + .fontSize(12) + .fontWeight(FontWeight.Normal) + .backgroundColor('#f3f1f1') + .maxLines(10) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + Row() { + this.MyButton("add(26, 'z zz zzz')", 0) + this.MyButton('remove(0)', 1) + this.MyButton("update(1, 'nb nbb nbbb')", 2) + } + + Row() { + this.MyButton("search('c')", 3) + this.MyButton("append(26,' append ')", 4) + this.MyButton('export()', 5) + } + + Row() { + this.MyButton(this.getResourceString($r('app.string.import')), 6) + } + + Text($r('app.string.Test_Result')) + .alignSelf(ItemAlign.Start) + .fontSize(12) + .fontWeight(FontWeight.Bold) + Text(this.rst) + .width('100%') + .height('30%') + .margin(2) + .borderWidth(1) + .borderColor('#28c989') + .borderRadius(4) + .fontSize(12) + .fontWeight(FontWeight.Normal) + .backgroundColor('#f3f1f1') + .maxLines(10) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + } + .width('100%') + .height('100%') + } + + onMyButtonClick(eventType: number) { + switch (eventType) { + case 0: + if (model[0].fun(26, 'z zz zzz')) { + this.rst = 'add index data of id 26 is success' + } else { + this.rst = 'add index data of id 26 is fail' + } + break + case 1: + if (model[1].fun(0)) { + this.rst = 'remove index data of id 0 is fail' + } else { + this.rst = 'remove index data of id 0 is success' + } + break + case 2: + model[2].fun(1, 'updateIndex') + this.rst = JSON.stringify(model[searchIndex].fun('updateIndex')) + break + case 3: + this.rst = JSON.stringify(model[searchIndex].fun('c')) + break + case 4: + model[3].fun(indexData.length + 1, 'model.appendIndex') + this.rst = JSON.stringify(model[searchIndex].fun('model.appendIndex')) + break + case 5: + model[4].fun() + this.rst = 'export async method result, please look for log,search in hilog: exportIndex success, key =' + break + case 6: + model[5].fun() + this.rst = 'import async method result, please look for log,search in hilog : importIndex success, data =' + break + } + } +} \ No newline at end of file diff --git a/ohos/entry/src/main/ets/pages/main.ets b/ohos/entry/src/main/ets/pages/main.ets new file mode 100644 index 00000000..26a93dd3 --- /dev/null +++ b/ohos/entry/src/main/ets/pages/main.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import router from '@ohos.router'; + +@Entry +@Component +struct MainPage { + @Builder MyButton(content: string, pagePath: string) { + Button(content) + .size({ width: 300, height: 36 }) + .margin({ top: 20, right: 6, bottom: 0, left: 6 }) + .fontColor('white') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .backgroundColor('#00a6ff') + .borderWidth(1) + .borderColor('#28c989') + .borderRadius(2) + .borderStyle(BorderStyle.Solid) + .onClick(() => { + router.pushUrl({ url: pagePath }) + }) + } + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + this.MyButton('demo for {@code Index}', 'pages/index') + this.MyButton('demo for {@code Document}', 'pages/document') + this.MyButton('demo for {@code WorkerIndex}', 'pages/worker') + } + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/ohos/entry/src/main/ets/pages/worker.ets b/ohos/entry/src/main/ets/pages/worker.ets new file mode 100644 index 00000000..1a843976 --- /dev/null +++ b/ohos/entry/src/main/ets/pages/worker.ets @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import indexData from '../model/indexData'; +import model from '../model/workerIndexModel'; + +@Entry +@Component +struct WorkerPage { + @State rst: string = 'WorkerIndex page'; + + @Builder MyButton(content: string, eventType: number) { + Button(content) + .size({ width: 100, height: 40 }) + .margin({ top: 10, right: 6, bottom: 0, left: 6 }) + .fontColor('white') + .fontSize(12) + .fontWeight(FontWeight.Bold) + .backgroundColor('#00a6ff') + .border({ width: 1, color: '#28c989', style: BorderStyle.Solid }) + .borderRadius(2) + .onClick(() => { + this.onMyButtonClick(eventType) + }) + } + + build() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + Text($r('app.string.document_data')) + .alignSelf(ItemAlign.Start) + .fontSize(12) + .fontWeight(FontWeight.Bold) + Text(JSON.stringify(indexData)) + .width('100%') + .height('26%') + .margin(2) + .borderWidth(1) + .borderColor('#28c989') + .borderRadius(4) + .fontSize(12) + .fontWeight(FontWeight.Normal) + .backgroundColor('#f3f1f1') + .maxLines(10) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + Row() { + this.MyButton("add(26, 'z zz zzz')", 0) + this.MyButton('remove(0)', 1) + this.MyButton("update(1, 'nb nbb nbbb')", 2) + } + + Row() { + this.MyButton("search('c')", 3) + this.MyButton("append(26,' append ')", 4) + } + + Text($r('app.string.Test_Result')) + .alignSelf(ItemAlign.Start) + .fontSize(12) + .fontWeight(FontWeight.Bold) + Text(this.rst) + .width('100%') + .height('30%') + .margin(2) + .borderWidth(1) + .borderColor('#28c989') + .borderRadius(4) + .fontSize(12) + .fontWeight(FontWeight.Normal) + .backgroundColor('#f3f1f1') + .maxLines(10) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + + } + .width('100%') + .height('100%') + } + + onMyButtonClick(eventType: number) { + + switch (eventType) { + case 0: + model[0].fun(26, 'z zz zzz') + + this.rst = 'addAsync method, please look for log,search in hilog:flexsearch---WorkerIndex.addAsync success:' + break + case 1: + model[2].fun(0) + this.rst = 'removeAsync method, please look for log,search in hilog:WorkerIndex.removeAsync success:' + break + case 2: + model[3].fun(1, 'nb nbb nbbb') + this.rst = 'updateAsync method, please look for log,search in hilog:WorkerIndex.updateAsync success:' + break + case 3: + model[4].fun('c'); + this.rst = 'searchAsync method, please look for log,search in hilog:WorkerIndex.searchAsync result =' + break + case 4: + model[1].fun(26, ' append '); + this.rst = 'appendAsync method, please look for log,search in hilog:WorkerIndex.appendAsync success:' + break + } + } +} + + + + diff --git a/ohos/entry/src/main/ets/utils/LogUtil.ts b/ohos/entry/src/main/ets/utils/LogUtil.ts new file mode 100644 index 00000000..03ce69f6 --- /dev/null +++ b/ohos/entry/src/main/ets/utils/LogUtil.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const DEBUG = true; + +export function logDebug(msg, arg) { + + if (DEBUG) { + if (typeof arg === 'string') { + console.log('my log::' + msg + arg); + } else if (typeof arg === 'number' || typeof arg == 'boolean' || typeof arg == 'function') { + console.log('my log::' + msg + arg.toString()); + } else if (typeof arg === 'undefined') { + console.log('my log::' + msg + ' undefined'); + } else if (typeof arg === 'object') { + console.log('my log::' + msg + JSON.stringify(arg)); + } else { + console.log('my log::' + msg + arg); + } + } +} diff --git a/ohos/entry/src/main/ets/utils/langEN.ets b/ohos/entry/src/main/ets/utils/langEN.ets new file mode 100644 index 00000000..75203039 --- /dev/null +++ b/ohos/entry/src/main/ets/utils/langEN.ets @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * http://www.ranks.nl/stopwords + * @type {Array} + */ +import { Stemmer } from '@ohos/flexsearch'; +import _default from '@ohos/flexsearch/src/lang/latin/default'; + +export const filter: string[] = [ + + "a", + "about", + "above", + "after", + "again", + "against", + "all", + "also", + "am", + "an", + "and", + "any", + "are", + "aren't", + "as", + "at", + //"back", + "be", + "because", + "been", + "before", + "being", + "below", + //"between", + "both", + "but", + "by", + "can", + "cannot", + "can't", + "come", + "could", + "couldn't", + //"day", + "did", + "didn't", + "do", + "does", + "doesn't", + "doing", + "dont", + "down", + "during", + "each", + "even", + "few", + "first", + "for", + "from", + "further", + "get", + //"give", + "go", + //"good", + "had", + "hadn't", + "has", + "hasn't", + "have", + "haven't", + "having", + "he", + "hed", + //"hell", + "her", + "here", + "here's", + "hers", + "herself", + "hes", + "him", + "himself", + "his", + "how", + "how's", + "i", + "id", + "if", + "ill", + "im", + "in", + "into", + "is", + "isn't", + "it", + "it's", + "itself", + "i've", + "just", + "know", + "let's", + "like", + //"look", + "make", + "me", + "more", + "most", + "mustn't", + "my", + "myself", + "new", + "no", + "nor", + "not", + "now", + "of", + "off", + "on", + "once", + //"one", + "only", + "or", + "other", + "ought", + "our", + "our's", + "ourselves", + "out", + "over", + "own", + //"people", + "same", + "say", + "see", + "shan't", + "she", + "she'd", + "shell", + "shes", + "should", + "shouldn't", + "so", + "some", + "such", + //"take", + "than", + "that", + "that's", + "the", + "their", + "theirs", + "them", + "themselves", + "then", + "there", + "there's", + "these", + "they", + "they'd", + "they'll", + "they're", + "they've", + //"think", + "this", + "those", + "through", + "time", + "to", + "too", + //"two", + //"under", + "until", + "up", + "us", + //"use", + "very", + "want", + "was", + "wasn't", + "way", + "we", + "wed", + "well", + "were", + "weren't", + "we've", + "what", + "what's", + "when", + "when's", + "where", + "where's", + "which", + "while", + "who", + "whom", + "who's", + "why", + "why's", + "will", + "with", + "won't", + //"work", + "would", + "wouldn't", + //"year", + "you", + "you'd", + "you'll", + "your", + "you're", + "your's", + "yourself", + "yourselves", + "you've" +]; + +/** + * @type {Object} + */ + +export const stemmer: Stemmer = { + + "ational": "ate", + "iveness": "ive", + "fulness": "ful", + "ousness": "ous", + "ization": "ize", + "tional": "tion", + "biliti": "ble", + "icate": "ic", + "ative": "", + "alize": "al", + "iciti": "ic", + "entli": "ent", + "ousli": "ous", + "alism": "al", + "ation": "ate", + "aliti": "al", + "iviti": "ive", + "ement": "", + "enci": "ence", + "anci": "ance", + "izer": "ize", + "alli": "al", + "ator": "ate", + "logi": "log", + "ical": "ic", + "ance": "", + "ence": "", + "ness": "", + "able": "", + "ible": "", + "ment": "", + "eli": "e", + "bli": "ble", + "ful": "", + "ant": "", + "ent": "", + "ism": "", + "ate": "", + "iti": "", + "ous": "", + "ive": "", + "ize": "", + "al": "", + "ou": "", + "er": "", + "ic": "" +}; + +export const matcher = ""; + +export class LangEN { + public filter: string[]; + public stemmer: Stemmer; + public matcher: string; + + constructor(filter: string[], stemmer: Stemmer, matcher: string) { + this.filter = filter; + this.stemmer = stemmer; + this.matcher = matcher; + } +} + +const langEN = new LangEN(filter,stemmer,matcher); +export default langEN; \ No newline at end of file diff --git a/ohos/entry/src/main/ets/workers/docWorker.js b/ohos/entry/src/main/ets/workers/docWorker.js new file mode 100644 index 00000000..d1da743b --- /dev/null +++ b/ohos/entry/src/main/ets/workers/docWorker.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import charsetCJK from '@ohos/flexsearch/src/lang/cjk/default.js' +import { setEncode, handler, parentPort } from '@ohos/flexsearch/src/worker/ark.js'; + +setEncode(charsetCJK.encode); +parentPort.onmessage = handler; diff --git a/ohos/entry/src/main/ets/workers/worker.js b/ohos/entry/src/main/ets/workers/worker.js new file mode 100644 index 00000000..8d2f8722 --- /dev/null +++ b/ohos/entry/src/main/ets/workers/worker.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import charsetDefault from '@ohos/flexsearch/src/lang/latin/default.js' +import { setEncode, handler, parentPort } from '@ohos/flexsearch/src/worker/ark.js'; + +setEncode(charsetDefault.encode); +parentPort.onmessage = handler; diff --git a/ohos/entry/src/main/module.json5 b/ohos/entry/src/main/module.json5 new file mode 100644 index 00000000..e3d39334 --- /dev/null +++ b/ohos/entry/src/main/module.json5 @@ -0,0 +1,43 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ts", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "metadata": [ + { + "name":"ArkTSPartialUpdate", + "value": "true" + } + ] + } +} \ No newline at end of file diff --git a/ohos/entry/src/main/resources/base/element/color.json b/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 00000000..3c712962 --- /dev/null +++ b/ohos/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/ohos/entry/src/main/resources/base/element/string.json b/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 00000000..e2e29e20 --- /dev/null +++ b/ohos/entry/src/main/resources/base/element/string.json @@ -0,0 +1,212 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "FlexSearch" + }, + { + "name": "document_data", + "value": "Document data:" + }, + { + "name": "Single_field", + "value": "Single field (content) search - keyword \"real estate\"" + }, + { + "name": "Multi_field", + "value": "Multi field (title&content) search - keyword \\\"real estate\\\"" + }, + { + "name": "Search_by_tag", + "value": "Search by tag (property, epidemic)" + }, + { + "name": "page_search", + "value": "Page Search - Add 1 to page No every time you click" + }, + { + "name": "Test_Result", + "value": "Test Result: " + }, + { + "name": "Page_content_display_completed", + "value": "Page_content_display_completed" + }, + { + "name": "importTest", + "value": "importTest, Need to perform exportTest first" + }, + { + "name": "import", + "value": "Import(), you need to first execute export()" + }, + { + "name": "Property", + "value": "Property" + }, + { + "name": "title1", + "value": "Take-out delivery guy buys a house in a small city for 70,000 yuan" + }, + { + "name": "content1", + "value": "A year ago, Li detailed his experience of buying a house in the \"Wander bar\". Compared to the exorbitant prices in the first-tier cities, the 50,000-yuan-per-house property deal went viral. Therefore, many \"wanderers\" also brought tens of thousands of cash to the area, trying to end their wandering life." + }, + { + "name": "Epidemic", + "value": "Epidemic" + }, + { + "name": "Livelihood", + "value": "Livelihood" + }, + { + "name": "Economy", + "value": "Economy" + }, + { + "name": "title2", + "value": "A mother-son pair confirmed to enter a certain place" + }, + { + "name": "title3", + "value": "Rainstorm waterlogging" + }, + { + "name": "title4", + "value": "New data on the housing market: No more golden September, cooling may continue in the fourth quarter" + }, + { + "name": "title5", + "value": "Economic trends" + }, + { + "name": "title6", + "value": "Epidemic affecting 8 provinces! When will we get the booster shot? Experts from disease control remind of winter epidemics..." + }, + { + "name": "title7", + "value": "Epidemic: Variant virus causing concern leads to a parliamentary report for response..." + }, + { + "name": "title8", + "value": "Property: 8 am news bulletin: Discussing an event publicly twice within a week releases what kind of information? | A real estate company\\'s current risks include..." + }, + { + "name": "title9", + "value": "Property: Planned pilot cities for real estate tax collection" + }, + { + "name": "title10", + "value": "Development of China\\'s real estate industry" + }, + { + "name": "title11", + "value": "Talent attraction and technology retention (frontline exploration of livelihood) - news report" + }, + { + "name": "content2", + "value": "Ms. Chen, 31 years old, unemployed, has been living in a certain area for the past half year before entering. She mainly stays at home and goes out to buy groceries once in 2-3 days. On the morning of July 17th, she experienced throat pain and had no other symptoms such as fever..." + }, + { + "name": "content3", + "value": "Several regions in a certain area experienced heavy to torrential rain, resulting in a sharp rise in water levels of rivers and severe flooding in Enshi city. Property damage was caused to the public. The picture shows the masses clearing up mud from the road." + }, + { + "name": "content4", + "value": "According to the housing price data for 70 large and medium-sized cities released on Wednesday, new housing prices in first and second-tier cities... In the third quarter financial data press conference, it was also stated that some financial institutions have issues with financing convention rules..." + }, + { + "name": "content5", + "value": "Pulse of Economic Trends. By Reuters Staff. 2 Min Read." + }, + { + "name": "content6", + "value": "According to Elephant News, within just three days, there have been... On October 18th, there were 6 new locally confirmed cases and 1 asymptomatic infection case reported." + }, + { + "name": "content7", + "value": "Sources informed CNN reporters that it may soon recommend people who are 40 or above and have previously received Moderna or Pfizer vaccines to receive a third dose." + }, + { + "name": "content8", + "value": "Property: A real estate company currently faces risks related to its inability to repay maturing debt, partial construction site shutdowns, and uncertainty in timely delivery of pre-sold properties. Overall, the risk is considered a case-specific risk. Yi Gang said..." + }, + { + "name": "content9", + "value": "Property: (...), real estate value..." + }, + { + "name": "content10", + "value": "In terms of land supply, guarantees are provided for affordable housing..." + }, + { + "name": "content11", + "value": "In recent years, talent attraction and training have been strengthened: platforms have been built based on industrial development needs to specifically introduce high-level talents; group-based support has been provided in healthcare, education, and other livelihood sectors..." + }, + { + "name": "Property2", + "value": "Property" + }, + { + "name": "housing", + "value": "housing" + }, + { + "name": "montage", + "value": "montage" + }, + { + "name": "Before_montage", + "value": "Before montage" + }, + { + "name": "the_value_before_montage", + "value": "This is the value before montage" + }, + { + "name": "tail_connection", + "value": "Tail connection" + }, + { + "name": "tail_title", + "value": "I am the tail title" + }, + { + "name": "tail_content", + "value": "I am the tail content" + }, + { + "name": "update", + "value": "update" + }, + { + "name": "before_update", + "value": "before update" + }, + { + "name": "value_before_update", + "value": "This is the value before the update" + }, + { + "name": "update_content", + "value": "Update" + }, + { + "name": "update_title", + "value": "A mother and son tested positive upon entering a certain area, ######### title after the update #########." + }, + { + "name": "update_content_value", + "value": "Update: Chen, female, 31 years old, unemployed, had been living in a certain area for the past six months, primarily staying at home, going out every 2-3 days to buy groceries. On the early morning of July 17, she experienced a sore throat, with no fever or other symptoms..." + } + ] +} \ No newline at end of file diff --git a/ohos/entry/src/main/resources/base/media/icon.png b/ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 00000000..ce307a88 Binary files /dev/null and b/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/ohos/entry/src/main/resources/base/profile/main_pages.json b/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 00000000..1dd3f27c --- /dev/null +++ b/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,9 @@ +{ + "src": [ + "pages/main", + "pages/document", + "pages/worker", + "pages/index" + + ] +} \ No newline at end of file diff --git a/ohos/entry/src/main/resources/zh_CN/element/string.json b/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 00000000..5ccadcfe --- /dev/null +++ b/ohos/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,212 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "FlexSearch" + }, + { + "name": "document_data", + "value": "文档数据:" + }, + { + "name": "Single_field", + "value": "单字段(content)搜索-关键词“房地产”" + }, + { + "name": "Search_by_tag", + "value": "通过tag(房产、流行病)搜索" + }, + { + "name": "Multi_field", + "value": "多字段(title&content)搜索-关键词“房地产”" + }, + { + "name": "page_search", + "value": "分页搜索-每次点击pageNo加1" + }, + { + "name": "Test_Result", + "value": "测试结果:" + }, + { + "name": "Page_content_display_completed", + "value": "分页内容展示完毕" + }, + { + "name": "importTest", + "value": "importTest,需先执行exportTest" + }, + { + "name": "import", + "value": "import(),需先先执行export()" + }, + { + "name": "Property", + "value": "房产" + }, + { + "name": "title1", + "value": "外卖小哥7万元小城买房" + }, + { + "name": "content1", + "value": "一年前,李某在“流浪吧”详述了自己前往买房的经历,相比于一线城市的天价,5万一套房的叙说令一夜爆火。于是很多“流浪”的人也携带几万块现金,去往该地试图终结自己的流浪生涯" + }, + { + "name": "Epidemic", + "value": "流行病" + }, + { + "name": "Livelihood", + "value": "民生" + }, + { + "name": "Economy", + "value": "经济" + }, + { + "name": "title2", + "value": "一对母子入境某地确诊" + }, + { + "name": "title3", + "value": "暴雨内涝" + }, + { + "name": "title4", + "value": "楼市新数据:金九不再 四季度或持续降温" + }, + { + "name": "title5", + "value": "经济走向" + }, + { + "name": "title6", + "value": "流行病波及8省份!加强针什么时候能打?疾控专家提醒冬季流行病 ..." + }, + { + "name": "title7", + "value": "流行病:变种病毒在引发关注议会报告以应对 ..." + }, + { + "name": "title8", + "value": "新闻8点见丨一周内两度公开谈事件,释放哪些信息?_ ..." + }, + { + "name": "title9", + "value": "征收房地产税计划试点城市" + }, + { + "name": "title10", + "value": "中国房地产发展" + }, + { + "name": "title11", + "value": "人才引进来技术留下来(一线探民生)--新闻报道" + }, + { + "name": "content2", + "value": "陈某,女,31岁,无业,入境前半年一直在某地区居住生活,以居家为主,2-3天外出买菜一次。7月17日凌晨出现咽痛,无发热等其他症状..." + }, + { + "name": "content3", + "value": "某地区多地出现暴雨到大暴雨,清江水位暴涨,恩施城区内涝严重,民众财产受损。图为群众清理道路淤泥" + }, + { + "name": "content4", + "value": "周三公布的70个大中城市房价数据显示,一、二线城市新建 ... 在3季度金融数据新闻发布会上还表示,部分金融机构对融资惯例规则存在..." + }, + { + "name": "content5", + "value": "把脉经济走向. By Reuters Staff. 2 Min Read." + }, + { + "name": "content6", + "value": "大象新闻消息,仅三天时间,已经 ... 10月18日,当地新增本土确诊病例6例,无症状感染者1..." + }, + { + "name": "content7", + "value": "消息人士向有线电视新闻网记者透露,可能很快就会推荐40岁或40岁以上、此前曾接种过莫德纳或辉瑞疫苗的人员接种第三剂疫苗。" + }, + { + "name": "content8", + "value": "一家房地产公司,目前的主要风险是到期债务未能偿还,部分工地停工,已预售的房产按时交付有不确定性。总体而言,风险是个案风险。 易纲称,..." + }, + { + "name": "content9", + "value": "... 在房地产中;房地产价值的...。" + }, + { + "name": "content10", + "value": "土地供应方面,保障住房..." + }, + { + "name": "content11", + "value": "近年来,加强人才引进与培养:根据产业发展需求搭建平台,有针对性地引入高层次人才;在医疗、教育等民生领域进行组团式帮..." + }, + { + "name": "Property2", + "value": "房地产" + }, + { + "name": "housing", + "value": "住房" + }, + { + "name": "montage", + "value": "拼接" + }, + { + "name": "Before_montage", + "value": "拼接前" + }, + { + "name": "the_value_before_montage", + "value": "这是拼接之前的值" + }, + { + "name": "tail_connection", + "value": "尾接" + }, + { + "name": "tail_title", + "value": "我是尾接的标题" + }, + { + "name": "tail_content", + "value": "我是尾接的内容" + }, + { + "name": "update", + "value": "更新" + }, + { + "name": "before_update", + "value": "更新前" + }, + { + "name": "value_before_update", + "value": "这是更新之前的值" + }, + { + "name": "update_content", + "value": "更新内容" + }, + { + "name": "update_title", + "value": "一对母子入境某地确诊,######### update以后的title #########" + }, + { + "name": "update_content_value", + "value": "更新内容陈某,女,31岁,无业,入境前半年一直在某地区居住生活,以居家为主,2-3天外出买菜一次。7月17日凌晨出现咽痛,无发热等其他症状..." + } + ] +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts b/ohos/entry/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts new file mode 100644 index 00000000..9547fc9b --- /dev/null +++ b/ohos/entry/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + const bundleName = abilityDelegatorArguments.bundleName; + const testAbilityName = 'TestAbility'; + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + const want = { + bundleName: bundleName, + abilityName: testAbilityName + }; + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + abilityDelegator.startAbility(want, (err : any, data : any) => { + hilog.info(0x0000, 'testTag', 'startAbility : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'startAbility : data : %{public}s',JSON.stringify(data) ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/ets/test/Bean.ets b/ohos/entry/src/ohosTest/ets/test/Bean.ets new file mode 100644 index 00000000..0c09d9f2 --- /dev/null +++ b/ohos/entry/src/ohosTest/ets/test/Bean.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class DocumentSearchResultBean { + public field: string; + public result: ResultBeanStyle[] | string[] = []; + + constructor(field: string, result: ResultBeanStyle[] | string[]) { + this.field = field; + if (result != undefined) { + this.result = result; + } + } +} + +export class ResultBeanStyle { + public id: number | string = ""; + public doc: DocBeanStyle; + + constructor(id: number | string, doc: DocBeanStyle) { + if (id != undefined) { + this.id = id; + } + this.doc = doc; + } +} + +export class DocBeanStyle { + public url: string = ""; + public title: string; + + constructor(url: string, title: string) { + this.url = url; + this.title = title; + } +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/ets/test/Document.test.ets b/ohos/entry/src/ohosTest/ets/test/Document.test.ets new file mode 100644 index 00000000..7e3088af --- /dev/null +++ b/ohos/entry/src/ohosTest/ets/test/Document.test.ets @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, it, expect } from '@ohos/hypium'; +import FlexSearch, { + DocumentSearchResult, + Id, + IndexOptionsForDocumentSearch, + SimpleDocumentSearchResultSetUnit, + StoreOption, +} from '@ohos/flexsearch'; + +const TAG: string = "flexSearch document :: "; + +class DocsDataType { + public url: string; + public tag: string; + public title: string; + public content: string; + + constructor(url: string, tag: string, title: string, content: string) { + this.url = url; + this.tag = tag; + this.title = title; + this.content = content; + } +} + +const docs: DocsDataType[] = [ + new DocsDataType('url1', '房产', '外卖小哥7万元小城买房', '一年前,李某在“流浪吧”详述了自己前往买房的经历,相比于一线城市的天价,5万一套房的叙说令一夜爆火。于是很多“流浪”的人也携带几万块现金,去往该地试图终结自己的流浪生涯'), + new DocsDataType('url2', '流行病', '一对母子入境某地确诊', '陈某,女,31岁,无业,入境前半年一直在某地区居住生活,以居家为主,2-3天外出买菜一次。7月17日凌晨出现咽痛,无发热等其他症状...'), + new DocsDataType('url3', '民生', '暴雨内涝', '某地区多地出现暴雨到大暴雨,清江水位暴涨,恩施城区内涝严重,民众财产受损。图为群众清理道路淤泥'), + new DocsDataType('url4', '房产', '楼市新数据:金九不再 四季度或持续降温', '周三公布的70个大中城市房价数据显示,一、二线城市新建 ... 在3季度金融数据新闻发布会上还表示,部分金融机构对融资惯例规则存在...'), + new DocsDataType('url5', '经济', '经济走向', '把脉经济走向. By Reuters Staff. 2 Min Read.'), + new DocsDataType('url6', '流行病', '流行病波及8省份!加强针什么时候能打?疾控专家提醒冬季流行病 ...', '大象新闻消息,仅三天时间,已经 ... 10月18日,当地新增本土确诊病例6例,无症状感染者1...'), + new DocsDataType('url7', '流行病', '流行病:变种病毒在引发关注议会报告以应对 ...', '消息人士向有线电视新闻网记者透露,可能很快就会推荐40岁或40岁以上、此前曾接种过莫德纳或辉瑞疫苗的人员接种第三剂疫苗。'), + new DocsDataType('url8', '房产', '新闻8点见丨一周内两度公开谈事件,释放哪些信息?_ ...', '一家房地产公司,目前的主要风险是到期债务未能偿还,部分工地停工,已预售的房产按时交付有不确定性。总体而言,风险是个案风险。 易纲称,...'), + new DocsDataType('url9', '房产', '征收房地产税计划试点城市', '... 在房地产中;房地产价值的...。'), + new DocsDataType('url10', '房产', '中国房地产发展', '土地供应方面,保障住房...'), + new DocsDataType('url11', '民生', '人才引进来技术留下来(一线探民生)--新闻报道', '近年来,加强人才引进与培养:根据产业发展需求搭建平台,有针对性地引入高层次人才;在医疗、教育等民生领域进行组团式帮...') +]; + +const options: IndexOptionsForDocumentSearch = { + preset: 'default', + tokenize: 'strict', + cache: 100, + resolution: 9, + context: false, + optimize: true, + charset: 'cjk', + language: undefined, + encode: false, + stemmer: false, + filter: ['的', '又', '及'], + matcher: false, + worker: false, + document: { + id: 'url', + tag: 'tag', + index: ['title', 'content'], + store: ['url', 'title'] + } +}; + +let flexSearchDocument = new FlexSearch.Document(options); +// add init data +for (let index = 0; index < docs.length; index++) { + flexSearchDocument.add(docs[index]); +} + +const map: Map = new Map(); + +export default function documentClassXts() { + describe('DocumentClassTest', () => { + it("Document.addAndSearch_case1", 0, () => { + flexSearchDocument.add(new DocsDataType('url12', '开源', '开源鸿蒙', 'Document.add_case1。。')); + flexSearchDocument.searchAsync("开源").then((result: SimpleDocumentSearchResultSetUnit[]) => { + console.log(TAG + "Document.addAndSearch_case1 : " + JSON.stringify(result)); + expect(result[0].result[0]).assertEqual("url12"); + }); + }) + + it("Document.addAndSearch_case2", 0, () => { + flexSearchDocument.add(2222, + new DocsDataType('url13', '2222数字', '数字经济', 'Document.这有数字add_case2。。')); + let result: SimpleDocumentSearchResultSetUnit[] = flexSearchDocument.search("数字", 10); + console.log(TAG + "Document.addAndSearch_case2 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": [2222] + }, { + "field": "content", "result": [2222] + }]); + }) + + it("Document.addAndSearch_case3", 0, () => { + flexSearchDocument.add("add_case3", + new DocsDataType('url14', '吃喝', '谈吃喝', 'Document.add_case3。。')); + let result: SimpleDocumentSearchResultSetUnit[] = flexSearchDocument.search("开源", ['title', 'content']); + console.log(TAG + "Document.addAndSearch_case3 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url12"] + }]); + }) + + it("Document.addAndSearch_case4", 0, () => { + flexSearchDocument.add("add_case4", + new DocsDataType('url15', '语言', 'ArkTs 语言', 'ArkTs 语言 for app。。')); + let result: SimpleDocumentSearchResultSetUnit[] = flexSearchDocument.search("语言", 10); + console.log(TAG + " Document.addAndSearch_case4 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["add_case4"] + }, { + "field": "content", "result": ["add_case4"] + }]); + }) + + it("Document.addAndSearch_case5", 0, () => { + class DocumentAnother { + label: string; + name: string; + value: string; + + constructor(label: string, name: string, value: string) { + this.label = label; + this.name = name; + this.value = value; + } + } + + const optionsTmp: IndexOptionsForDocumentSearch = { + preset: 'default', + tokenize: 'strict', + cache: 100, + resolution: 9, + context: false, + optimize: true, + charset: 'cjk', + language: undefined, + encode: false, + stemmer: false, + filter: ['的', '又', '及'], + matcher: false, + worker: false, + document: { + id: 'label', + index: ['name', 'value'], + store: ['label', 'value'] + } + }; + let flexSearchDocumentTmp = new FlexSearch.Document(optionsTmp); + flexSearchDocumentTmp.add("number001", new DocumentAnother("开发语言", "ArkTs语言", "app 开发语言")); + let result: SimpleDocumentSearchResultSetUnit[] = flexSearchDocumentTmp.search("语言", { + index: ['name', 'value'] + }); + console.log(TAG + " Document.addAndSearch_case5 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "name", "result": ["number001"] + }, { + "field": "value", "result": ["number001"] + }]); + }) + + it("Document.append_documentT_AndSearch_case1", 0, () => { + flexSearchDocument.append(new DocsDataType("url1", "拼接", "拼接标题", "这是拼接内容")); + const searchResult: DocumentSearchResult = flexSearchDocument.search("拼接", 10, { + index: ['title'], + limit: 10, + enrich: true, + bool: 'and' + }); + console.log(TAG + " append_documentT_AndSearch_case1 : " + JSON.stringify(searchResult)); + expect(searchResult).not().assertNull(); + }) + + it("Document.append_documentT_AndSearch_case2", 0, () => { + flexSearchDocument.append(new DocsDataType("url2", "拼接+2", "拼接标题+2", "这是拼接内容+2")); + let result = flexSearchDocument.search("拼接"); + console.log(TAG + " append_documentT_AndSearch_case2 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url1", "url2"] + }, { + "field": "content", "result": ["url1", "url2"] + }]); + }) + + it("Document.append_documentT_AndSearch_case3", 0, () => { + flexSearchDocument.append(new DocsDataType("url3", "拼接拼接", "拼接标题到append_documentT_AndSearch_case3", + "这是拼接内容append_documentT_AndSearch_case3")); + let result = flexSearchDocument.search("拼接"); + console.log(TAG + " append_documentT_AndSearch_case3 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url1", "url2", "url3"] + }, { + "field": "content", "result": ["url1", "url2", "url3"] + }]); + }) + + it("Document.append_idId_documentT_AndSearch_case4", 0, () => { + flexSearchDocument.append(new DocsDataType("url4", "你好", "你好的解释", "你好是问候语")); + let result = flexSearchDocument.search("你好"); + console.log(TAG + " append_idId_documentT_AndSearch_case4 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url4"] + }, { + "field": "content", "result": ["url4"] + }]); + }) + + it("Document.append_idId_documentT_AndSearch_case5", 0, () => { + flexSearchDocument.append(new DocsDataType("url5", "辛苦", "辛苦的解释", "辛苦可以形容工作或学习努力")); + let result = flexSearchDocument.search("辛苦"); + console.log(TAG + " append_idId_documentT_AndSearch_case5 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url5"] + }, { + "field": "content", "result": ["url5"] + }]); + }) + + it("Document.append_idId_documentT_AndSearch_case6", 0, () => { + flexSearchDocument.append(new DocsDataType("url6", "开朗", "开朗的解释", "开朗可以形容人阳光积极")); + let result = flexSearchDocument.search({ + index: ['title', 'content'], + query: "开朗", + limit: 10, + enrich: true, + bool: 'and' + }); + console.log(TAG + " append_idId_documentT_AndSearch_case6 : " + JSON.stringify(result)); + expect(result.length).assertEqual(2); + }) + + it("Document.update_documentT_AndSearch_case1", 0, () => { + flexSearchDocument.update(new DocsDataType("url1", "更新", "标题已更新", "这是更新后的内容")); + let result = flexSearchDocument.search("更新"); + console.log(TAG + " update_documentT_AndSearch_case1 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url1"] + }, { + "field": "content", "result": ["url1"] + }]); + let result1 = flexSearchDocument.search("房产"); + console.log(TAG + " update_documentT_AndSearch_case1 000 : " + JSON.stringify(result1)); + expect(result1.length).assertEqual(2); + }) + + it("Document.update_documentT_AndSearch_case2", 0, () => { + flexSearchDocument.update(new DocsDataType("url2", "变更22", "标题已变更", "这是变更后的内容")); + let result = flexSearchDocument.search("变更"); + console.log(TAG + " update_documentT_AndSearch_case2 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url2"] + }, { + "field": "content", "result": ["url2"] + }]); + let result1 = flexSearchDocument.search("陈某"); + console.log(TAG + " update_documentT_AndSearch_case2 000 : " + JSON.stringify(result1)); + expect(result1.length).assertEqual(0) + }) + + it("Document.update_idId_documentT_AndSearch_case3", 0, () => { + flexSearchDocument.update("update_idId_documentT_AndSearch_case3", new DocsDataType("url3", "詹姆斯", "NBA球星", "这是詹姆斯")); + let result = flexSearchDocument.search("詹姆斯"); + console.log(TAG + " update_idId_documentT_AndSearch_case3 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "content", "result": ["update_idId_documentT_AndSearch_case3"] + }]); + }) + + it("Document.update_idId_documentT_AndSearch_case4", 0, () => { + flexSearchDocument.update("update_idId_documentT_AndSearch_case4", new DocsDataType("url4", "斯蒂芬库里", "NBA球星", "这是库里")); + let result = flexSearchDocument.search("库里"); + console.log(TAG + " update_idId_documentT_AndSearch_case4 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "content", "result": ["update_idId_documentT_AndSearch_case4"] + }]); + }) + + it("Document.update_idId_documentT_AndSearch_case5", 0, () => { + flexSearchDocument.update("update_idId_documentT_AndSearch_case5", new DocsDataType("url5", "杜兰特", "NBA球星", "杜兰特布克")); + let result = flexSearchDocument.search("布克"); + console.log(TAG + " update_idId_documentT_AndSearch_case5 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "content", "result": ["update_idId_documentT_AndSearch_case5"] + }]); + }) + + it("Document.update_idId_documentT_AndSearch_case6", 0, () => { + flexSearchDocument.update("update_idId_documentT_AndSearch_case6", new DocsDataType("url6", "克莱汤普森", "NBA球星", "这是克莱汤普森")); + let result = flexSearchDocument.search("克莱汤普森"); + console.log(TAG + " update_idId_documentT_AndSearch_case6 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "content", "result": ["update_idId_documentT_AndSearch_case6"] + }]); + }) + + it("Document.update_idId_documentT_AndSearch_case7", 0, () => { + flexSearchDocument.update("update_idId_documentT_AndSearch_case7", new DocsDataType("url7", "维金斯", "NBA球星", "布克维金斯")); + let result = flexSearchDocument.search("金"); + console.log(TAG + " update_idId_documentT_AndSearch_case7 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url4"] + }, { + "field": "content", "result": ["update_idId_documentT_AndSearch_case7", "url4"] + }]); + }) + + it("Document.remove_targetIdAndT_AndSearch_case1", 0, () => { + flexSearchDocument.add("remove_targetIdAndT_AndSearch_case1", new DocsDataType("number001", "库里库里", "超级巨星", "斯蒂芬库里库里")); + let result0 = flexSearchDocument.search("库里库里"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case1 000 : " + JSON.stringify(result0)); + expect(result0).assertDeepEquals([{ + "field": "content", + "result": ["update_idId_documentT_AndSearch_case4", "remove_targetIdAndT_AndSearch_case1"] + }]); + flexSearchDocument.remove("remove_targetIdAndT_AndSearch_case1"); + let result = flexSearchDocument.search("库里库里"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case1 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "content", "result": ["update_idId_documentT_AndSearch_case4"] + }]); + }) + + it("Document.remove_targetIdAndT_AndSearch_case2", 0, () => { + flexSearchDocument.add("remove_targetIdAndT_AndSearch_case2", new DocsDataType("number002", "詹姆斯詹姆斯", "超级巨星", "勒布朗詹姆斯詹姆斯")); + let result0 = flexSearchDocument.search("詹姆斯詹姆斯"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case2 000 : " + JSON.stringify(result0)); + expect(result0).assertDeepEquals([{ + "field": "content", + "result": ["update_idId_documentT_AndSearch_case3", "remove_targetIdAndT_AndSearch_case2"] + }]); + flexSearchDocument.remove("remove_targetIdAndT_AndSearch_case2"); + let result = flexSearchDocument.search("詹姆斯詹姆斯"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case2 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "content", "result": ["update_idId_documentT_AndSearch_case3"] + }]); + }) + + it("Document.remove_targetIdAndT_AndSearch_case3", 0, () => { + flexSearchDocument.add("remove_targetIdAndT_AndSearch_case3", new DocsDataType("number003", "杜兰特杜兰特", "超级巨星", "凯文杜兰特杜兰特")); + let result0 = flexSearchDocument.search("杜兰特杜兰特"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case3 000 : " + JSON.stringify(result0)); + expect(result0).assertDeepEquals([{ + "field": "content", + "result": ["update_idId_documentT_AndSearch_case5", "remove_targetIdAndT_AndSearch_case3"] + }]); + flexSearchDocument.remove("remove_targetIdAndT_AndSearch_case3"); + let result = flexSearchDocument.search("杜兰特杜兰特"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case3 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "content", "result": ["update_idId_documentT_AndSearch_case5"] + }]); + }) + + it("Document.remove_targetIdAndT_AndSearch_case4", 0, () => { + flexSearchDocument.add("remove_targetIdAndT_AndSearch_case4", new DocsDataType("number004", "ID", "删除的标记", "根据ID删除")); + let result0 = flexSearchDocument.search("删除"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case4 000 : " + JSON.stringify(result0)); + expect(result0).assertDeepEquals([{ + "field": "title", "result": ["remove_targetIdAndT_AndSearch_case4"] + }, { + "field": "content", "result": ["remove_targetIdAndT_AndSearch_case4"] + }]); + flexSearchDocument.remove("remove_targetIdAndT_AndSearch_case4"); + let result = flexSearchDocument.search("删除"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case4 : " + JSON.stringify(result)); + expect(result.length).assertEqual(0); + }) + + it("Document.remove_targetIdAndT_AndSearch_case5", 0, () => { + flexSearchDocument.add("remove_targetIdAndT_AndSearch_case5", new DocsDataType("number005", "ID", "删除的标记第二次", "根据ID删除第二次")); + let result0 = flexSearchDocument.search("第二次"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case5 000 : " + JSON.stringify(result0)); + expect(result0).assertDeepEquals([{ + "field": "title", "result": ["remove_targetIdAndT_AndSearch_case5"] + }, { + "field": "content", "result": ["remove_targetIdAndT_AndSearch_case5"] + }]); + flexSearchDocument.remove("remove_targetIdAndT_AndSearch_case5"); + let result = flexSearchDocument.search("第二次"); + console.log(TAG + " remove_targetIdAndT_AndSearch_case5 : " + JSON.stringify(result)); + expect(result.length).assertEqual(0); + }) + + it("Document.exportCase", 0, () => { + flexSearchDocument.export((id, value) => { + map.set(id, value); + }) + setTimeout(() => { + console.log(TAG + " export map.size : " + map.size); + expect(9).assertEqual(map.size); + }, 1000) + }) + + it("Document.importCase", 0, () => { + const importDocument = new FlexSearch.Document(options); + setTimeout(() => { + console.log(TAG + " import map.size : " + map.size); + map.forEach((value, id) => { + importDocument.import(id, value); + }) + let result = importDocument.search("一线探民生"); + console.log(TAG + " Document.importCase : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["url11"] + }]); + let result1 = importDocument.search("流行病"); + console.log(TAG + " Document.importCase result1 : " + JSON.stringify(result1)); + expect(result1).assertDeepEquals([{ + "field": "title", "result": ["url6", "url7"] + }]); + }, 1000) + }) + }) +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/ets/test/DocumentAsyncMethod.test.ets b/ohos/entry/src/ohosTest/ets/test/DocumentAsyncMethod.test.ets new file mode 100644 index 00000000..8f2f64fc --- /dev/null +++ b/ohos/entry/src/ohosTest/ets/test/DocumentAsyncMethod.test.ets @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, it, expect } from '@ohos/hypium'; +import FlexSearch, { + IndexOptionsForDocumentSearch, + SimpleDocumentSearchResultSetUnit, + StoreOption, +} from '@ohos/flexsearch'; +import { DocBeanStyle, DocumentSearchResultBean, ResultBeanStyle } from './Bean'; + +const TAG: string = "flexSearch document async method :: "; + +class DocsDataType { + public url: string; + public tag: string; + public title: string; + public content: string; + + constructor(url: string, tag: string, title: string, content: string) { + this.url = url; + this.tag = tag; + this.title = title; + this.content = content; + } +} + +const docs: DocsDataType[] = [ + new DocsDataType('url1', '房产', '外卖小哥7万元小城买房', '一年前,李某在“流浪吧”详述了自己前往买房的经历,相比于一线城市的天价,5万一套房的叙说令一夜爆火。于是很多“流浪”的人也携带几万块现金,去往该地试图终结自己的流浪生涯'), + new DocsDataType('url2', '流行病', '一对母子入境某地确诊', '陈某,女,31岁,无业,入境前半年一直在某地区居住生活,以居家为主,2-3天外出买菜一次。7月17日凌晨出现咽痛,无发热等其他症状...'), + new DocsDataType('url3', '民生', '暴雨内涝', '某地区多地出现暴雨到大暴雨,清江水位暴涨,恩施城区内涝严重,民众财产受损。图为群众清理道路淤泥'), + new DocsDataType('url4', '房产', '楼市新数据:金九不再 四季度或持续降温', '周三公布的70个大中城市房价数据显示,一、二线城市新建 ... 在3季度金融数据新闻发布会上还表示,部分金融机构对融资惯例规则存在...'), + new DocsDataType('url5', '经济', '经济走向', '把脉经济走向. By Reuters Staff. 2 Min Read.'), + new DocsDataType('url6', '流行病', '流行病波及8省份!加强针什么时候能打?疾控专家提醒冬季流行病 ...', '大象新闻消息,仅三天时间,已经 ... 10月18日,当地新增本土确诊病例6例,无症状感染者1...'), + new DocsDataType('url7', '流行病', '流行病:变种病毒在引发关注议会报告以应对 ...', '消息人士向有线电视新闻网记者透露,可能很快就会推荐40岁或40岁以上、此前曾接种过莫德纳或辉瑞疫苗的人员接种第三剂疫苗。'), + new DocsDataType('url8', '房产', '新闻8点见丨一周内两度公开谈事件,释放哪些信息?_ ...', '一家房地产公司,目前的主要风险是到期债务未能偿还,部分工地停工,已预售的房产按时交付有不确定性。总体而言,风险是个案风险。 易纲称,...'), + new DocsDataType('url9', '房产', '征收房地产税计划试点城市', '... 在房地产中;房地产价值的...。'), + new DocsDataType('url10', '房产', '中国房地产发展', '土地供应方面,保障住房...'), + new DocsDataType('url11', '民生', '人才引进来技术留下来(一线探民生)--新闻报道', '近年来,加强人才引进与培养:根据产业发展需求搭建平台,有针对性地引入高层次人才;在医疗、教育等民生领域进行组团式帮...') +]; + +const options: IndexOptionsForDocumentSearch = { + preset: 'default', + tokenize: 'strict', + cache: 100, + resolution: 9, + context: false, + optimize: true, + charset: 'cjk', + language: undefined, + encode: false, + stemmer: false, + filter: ['的', '又', '及'], + matcher: false, + worker: false, + document: { + id: 'url', + tag: 'tag', + index: ['title', 'content'], + store: ['url', 'title'] + } +}; + +let flexSearchDocument = new FlexSearch.Document(options); +// add init data +for (let index = 0; index < docs.length; index++) { + flexSearchDocument.add(docs[index]); +} + +export default function documentClassAsyncMethodXts() { + describe('documentClassAsyncMethodTest', () => { + it("Document.addAsyncAndSearchAsync_case1", 0, () => { + flexSearchDocument.addAsync("addAsyncAndSearchAsync_case1", new DocsDataType('url12', '开源', '开源鸿蒙', 'Document.add_case1。。')) + setTimeout(() => { + flexSearchDocument.searchAsync("开源").then((result: SimpleDocumentSearchResultSetUnit[]) => { + console.log(TAG + "Document.addAsyncAndSearchAsync_case1 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["addAsyncAndSearchAsync_case1 oo"] + }]); + }); + }, 500) + }) + + it("Document.addAsyncAndSearchAsync_case2", 0, () => { + flexSearchDocument.addAsync(2222, + new DocsDataType('url13', '2222数字', '数字经济', 'Document.这有数字add_case2。。')) + .then(() => { + let result: SimpleDocumentSearchResultSetUnit[] = flexSearchDocument.search("数字", 10); + console.log(TAG + "Document.addAsyncAndSearchAsync_case2 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": [2222] + }, { + "field": "content", "result": [2222] + }]); + }) + }) + + it("Document.addAsyncAndSearchAsync_case3", 0, () => { + flexSearchDocument.addAsync("add_case3", + new DocsDataType('url14', '吃喝', '谈吃喝', 'Document.add_case3。。')); + setTimeout(() => { + flexSearchDocument.searchAsync("开源", ['title', 'content']).then((result) => { + console.log(TAG + "Document.addAsyncAndSearchAsync_case3 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["addAsyncAndSearchAsync_case1"] + }]); + }) + }, 300) + + }) + + it("Document.addAsyncAndSearchAsync_case4", 0, () => { + flexSearchDocument.addAsync("add_case4", + new DocsDataType('url15', '语言', 'ArkTs 语言', 'ArkTs 语言 for app。。')); + setTimeout(() => { + flexSearchDocument.searchAsync("语言", 10) + .then((result) => { + console.log(TAG + " Document.addAsyncAndSearchAsync_case4 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "title", "result": ["add_case4"] + }, { + "field": "content", "result": ["add_case4"] + }]); + }) + }, 300); + }) + + it("Document.addAsyncAndSearchAsync_case5", 0, () => { + class DocumentAnother { + label: string; + name: string; + value: string; + + constructor(label: string, name: string, value: string) { + this.label = label; + this.name = name; + this.value = value; + } + } + + const optionsTmp: IndexOptionsForDocumentSearch = { + preset: 'default', + tokenize: 'strict', + cache: 100, + resolution: 9, + context: false, + optimize: true, + charset: 'cjk', + language: undefined, + encode: false, + stemmer: false, + filter: ['的', '又', '及'], + matcher: false, + worker: false, + document: { + id: 'label', + index: ['name', 'value'], + store: ['label', 'value'] + } + }; + let flexSearchDocumentTmp = new FlexSearch.Document(optionsTmp); + flexSearchDocumentTmp.addAsync("number001", new DocumentAnother("开发语言", "ArkTs语言", "app 开发语言")); + setTimeout(() => { + flexSearchDocumentTmp.searchAsync("语言", { + index: ['name', 'value'] + }).then((result) => { + console.log(TAG + " Document.addAsyncAndSearchAsync_case5 : " + JSON.stringify(result)); + expect(result).assertDeepEquals([{ + "field": "name", "result": ["number001"] + }, { + "field": "value", "result": ["number001"] + }]); + }) + }, 300) + }) + + it("Document.appendAsyncAndSearchAsync_case1", 0, () => { + flexSearchDocument.appendAsync("appendAsyncAndSearchAsync_case1", new DocsDataType("url1", "拼接", "拼接标题", "这是拼接内容")); + setTimeout(() => { + flexSearchDocument.searchAsync("拼接", 10, (searchResult: SimpleDocumentSearchResultSetUnit[]) => { + console.log(TAG + " appendAsyncAndSearchAsync_case1 : " + JSON.stringify(searchResult)); + expect(searchResult).assertDeepEquals([{ + "field": "title", "result": ["appendAsyncAndSearchAsync_case1", 1001] + }, { + "field": "content", "result": ["appendAsyncAndSearchAsync_case1", 1001] + }]); + }) + }, 300); + }) + + it("Document.appendAsyncAndSearchAsync_case2", 0, () => { + flexSearchDocument.appendAsync(1001, new DocsDataType("url2", "拼接+2", "拼接标题+2", "这是拼接内容+2")); + setTimeout(() => { + flexSearchDocument.searchAsync("拼接", { + index: ['title'], + limit: 10, + enrich: true, + bool: 'and' + }, (searchResult: SimpleDocumentSearchResultSetUnit[]) => { + console.log(TAG + " appendAsyncAndSearchAsync_case2 : " + JSON.stringify(searchResult)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean( + "title", [ + new ResultBeanStyle("appendAsyncAndSearchAsync_case1", new DocBeanStyle("url1", "拼接标题")), + new ResultBeanStyle(1001, new DocBeanStyle("url2", "拼接标题+2")) + ]); + expect(searchResult).assertDeepEquals(documentSearchResultBean); + }) + }, 300); + }) + + it("Document.appendAsyncAndSearchAsync_case3", 0, () => { + flexSearchDocument.appendAsync(404, new DocsDataType("url3", "介绍", "自我介绍", "这是自我介绍内容")); + setTimeout(() => { + flexSearchDocument.searchAsync("介绍", { + index: ['title'], + limit: 10, + enrich: true, + bool: 'and' + }, (searchResult: SimpleDocumentSearchResultSetUnit[]) => { + console.log(TAG + " appendAsyncAndSearchAsync_case3 : " + JSON.stringify(searchResult)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean( + "title", [new ResultBeanStyle(404, new DocBeanStyle("url3", "自我介绍"))]); + expect(searchResult).assertDeepEquals(documentSearchResultBean); + }) + }, 300); + }) + + it("Document.appendAsyncAndSearchAsync_case4", 0, () => { + flexSearchDocument.appendAsync(505, new DocsDataType("url4", "你好", "你好的解释", "你好是问候语")); + setTimeout(() => { + flexSearchDocument.searchAsync("你好", { + index: ['title'], + limit: 10, + enrich: true, + bool: "and" + }, (searchResult: SimpleDocumentSearchResultSetUnit[]) => { + console.log(TAG + " appendAsyncAndSearchAsync_case4 : " + JSON.stringify(searchResult)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean( + "title", [new ResultBeanStyle(505, new DocBeanStyle("url4", "你好的解释"))]); + expect(searchResult).assertDeepEquals(documentSearchResultBean); + }) + }, 300); + }) + + it("Document.appendAsyncAndSearchAsync_case5", 0, () => { + flexSearchDocument.appendAsync(200, new DocsDataType("url5", "辛苦", "辛苦的解释", "辛苦可以形容工作或学习努力")); + setTimeout(() => { + flexSearchDocument.searchAsync("辛苦", { + index: ['title'], + limit: 10, + enrich: true, + bool: 'or' + }, (searchResult: SimpleDocumentSearchResultSetUnit[]) => { + console.log(TAG + " appendAsyncAndSearchAsync_case5 : " + JSON.stringify(searchResult)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean( + "title", [new ResultBeanStyle("url5", new DocBeanStyle("url5", "经济走向")), + new ResultBeanStyle(200, new DocBeanStyle("url5", "辛苦的解释")) + ]); + expect(searchResult).assertDeepEquals(documentSearchResultBean); + }) + }, 300); + }) + + it("Document.updateAsync_case1", 0, () => { + flexSearchDocument.add("原始id", + new DocsDataType('url15', '语言', 'ArkTs 语言', 'ArkTs 语言 for app。。')); + flexSearchDocument.updateAsync("原始id", new DocsDataType("url1", "更新", "标题已更新", "这是更新后的内容")); + setTimeout(() => { + flexSearchDocument.searchAsync({ + query: "更新", + index: ['title'], + limit: 10, + enrich: true, + bool: "and" + }, (result) => { + console.log(TAG + " updateAsync_case1 : " + JSON.stringify(result)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean( + "title", [new ResultBeanStyle("原始id", new DocBeanStyle("url1", "标题已更新")), + new ResultBeanStyle("ID2", new DocBeanStyle("url1", "标题已更新")), + new ResultBeanStyle("IDID", new DocBeanStyle("url1", "标题已更新")), + new ResultBeanStyle("使用options", new DocBeanStyle("url1", "标题已更新")), + ]); + expect(result).assertDeepEquals(documentSearchResultBean); + }); + }, 300) + }) + + it("Document.updateAsync_case2", 0, () => { + flexSearchDocument.add("updateAsync_case2", + new DocsDataType('url15', '语言', 'ArkTs 语言', 'ArkTs 语言 for app。。')); + flexSearchDocument.updateAsync("updateAsync_case2", new DocsDataType("url1", "更新双倍", "标题已更新", "我爱我家")); + setTimeout(() => { + flexSearchDocument.searchAsync({ + query: "我家", + index: ['content'], + limit: 10, + enrich: true, + bool: "or" + }, (result) => { + console.log(TAG + " updateAsync_case2 : " + JSON.stringify(result)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean( + "content", [new ResultBeanStyle("updateAsync_case2", new DocBeanStyle("url1", "标题已更新")) + ]); + expect(result).assertDeepEquals(documentSearchResultBean); + }); + }, 300) + }) + + it("Document.updateAsync_case3", 0, () => { + flexSearchDocument.add("IDID", + new DocsDataType('url16', '语言', 'ArkTs 语言', 'ArkTs 语言 for app。。')); + flexSearchDocument.updateAsync("IDID", new DocsDataType("url1", "更新三倍倍", "标题已更新", "这是更新三倍倍内容")); + setTimeout(() => { + flexSearchDocument.searchAsync({ + query: "三倍", + index: ['content'], + limit: 10, + enrich: true, + bool: "or" + }).then((result) => { + console.log(TAG + " updateAsync_case3 : " + JSON.stringify(result)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean( + "content", [new ResultBeanStyle("IDID", new DocBeanStyle("url1", "标题已更新")) + ]); + expect(result).assertDeepEquals(documentSearchResultBean); + }); + }, 300) + }) + + it("Document.updateAsync_case4", 0, () => { + flexSearchDocument.add("使用options", + new DocsDataType('url17', '语言', 'ArkTs 语言', 'ArkTs 语言 for app。。')); + flexSearchDocument.updateAsync("使用options", new DocsDataType("url1", "使用options", "标题已更新", "只使用options的异步搜索")); + setTimeout(() => { + flexSearchDocument.searchAsync({ + query: "异步搜索", + index: ['title','content'], + limit: 10, + enrich: true, + bool: "and" + }).then((result) => { + console.log(TAG + " updateAsync_case4 : " + JSON.stringify(result)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean( + "title", [new ResultBeanStyle("updateAsync_case5", new DocBeanStyle("url1", "异步搜索")) + ]); + const documentSearchResultBean1: DocumentSearchResultBean = new DocumentSearchResultBean( + "content", [new ResultBeanStyle("updateAsync_case5", new DocBeanStyle("url1", "异步搜索")), + new ResultBeanStyle("使用options", new DocBeanStyle("url1", "标题已更新")) + ]); + expect(result).assertDeepEquals([documentSearchResultBean, documentSearchResultBean1]); + }); + }, 300) + }) + + it("Document.updateAsync_case5", 0, () => { + flexSearchDocument.add("updateAsync_case5", + new DocsDataType('url17', '语言', 'ArkTs 语言', 'ArkTs 语言 for app。。')); + flexSearchDocument.updateAsync("updateAsync_case5", new DocsDataType("url1", "搜索", "异步搜索", "全参异步搜索")); + setTimeout(() => { + flexSearchDocument.searchAsync({ + query: "异步搜索", + index: ['title','content'], + bool: "and" + }).then((result) => { + console.log(TAG + " updateAsync_case5 : " + JSON.stringify(result)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean("title",["updateAsync_case5"]); + const documentSearchResultBean1: DocumentSearchResultBean = new DocumentSearchResultBean("content", ["updateAsync_case5", "使用options"]); + expect(result).assertDeepEquals([documentSearchResultBean, documentSearchResultBean1]); + }); + }, 300) + }) + + it("Document.removeAsync_case1", 0, () => { + flexSearchDocument.add("remove_targetIdAndT_AndSearch_case1", new DocsDataType("number001", "库里库里", "超级巨星", "斯蒂芬库里库里")); + let result0 = flexSearchDocument.search("库里库里"); + console.log(TAG + " removeAsync_case1 000 : " + JSON.stringify(result0)); + expect(result0).assertDeepEquals([{"field":"content","result":["remove_targetIdAndT_AndSearch_case1"]}]); + flexSearchDocument.removeAsync(new DocsDataType("number001", "库里库里", "超级巨星", "斯蒂芬库里库里")); + setTimeout(() => { + flexSearchDocument.searchAsync({ + query: "库里库里", + index: ['title', 'content'], + limit: 10, + enrich: true, + bool: "and" + }).then((result) => { + console.log(TAG + " removeAsync_case1 111 : " + JSON.stringify(result)); + expect(result.length).assertEqual(0); + }) + }, 300) + }) + + it("Document.removeAsync_case2", 0, () => { + flexSearchDocument.add("remove_targetIdAndT_AndSearch_case2", new DocsDataType("number002", "詹姆斯詹姆斯", "超级巨星", "勒布朗詹姆斯詹姆斯")); + let result0 = flexSearchDocument.search("詹姆斯詹姆斯"); + console.log(TAG + " removeAsync_case2 000 : " + JSON.stringify(result0)); + expect(result0).assertDeepEquals([{"field":"content","result":["remove_targetIdAndT_AndSearch_case2"]}]); + flexSearchDocument.removeAsync("remove_targetIdAndT_AndSearch_case2"); + setTimeout(()=>{ + flexSearchDocument.searchAsync({ + query: "库里库里", + index: ['title', 'content'], + limit: 10, + enrich: true, + bool: "and" + }).then((result) => { + console.log(TAG + " removeAsync_case2 111 : " + JSON.stringify(result)); + expect(result.length).assertEqual(0); + }) + },300) + }) + + it("Document.removeAsync_case3", 0, () => { + flexSearchDocument.add("remove_targetIdAndT_AndSearch_case3", new DocsDataType("number003", "杜兰特杜兰特", "超级巨星", "凯文杜兰特杜兰特")); + let result0: SimpleDocumentSearchResultSetUnit[] = flexSearchDocument.search("杜兰特杜兰特"); + console.log(TAG + " removeAsync_case3 000 : " + JSON.stringify(result0)); + const documentSearchResultBean: DocumentSearchResultBean = new DocumentSearchResultBean("content", ["remove_targetIdAndT_AndSearch_case3"]); + expect(result0.length).assertDeepEquals(1); + expect(result0[0].result).assertDeepEquals(documentSearchResultBean.result); + flexSearchDocument.removeAsync("remove_targetIdAndT_AndSearch_case3", () => { + flexSearchDocument.searchAsync("杜兰特杜兰特").then((result) => { + console.log(TAG + " removeAsync_case3 111 : " + JSON.stringify(result)); + expect(result.length).assertEqual(0); + }) + }); + }) + }) +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/ets/test/Index.test.ets b/ohos/entry/src/ohosTest/ets/test/Index.test.ets new file mode 100644 index 00000000..63ec53c7 --- /dev/null +++ b/ohos/entry/src/ohosTest/ets/test/Index.test.ets @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import langEN from '../../../main/ets/utils/langEN'; +import { describe, it, expect } from '@ohos/hypium' +import flexSearch, { Index, Id, SearchOptions, IndexSearchResult } from '@ohos/flexsearch'; + +flexSearch.registerLanguage('en', langEN); + + +namespace ohos_index { + export function addData(): Index { + const indexData: string[] = [ + 'a', 'a aa', 'a aa aaa', + 'b', 'b bb', 'b bb bbb', + 'a', 'c cc', 'c cc ccc', + 'd', 'b bb', 'd dd ddd' + ]; + let index = new flexSearch.Index(); + for (let i = 0; i < indexData.length; i++) { + index.add(i, indexData[i]); + } + return index; + } +} + +export default function indexClassXTS() { + + const indexData: string[] = [ + 'a', 'a aa', 'a aa aaa', + 'b', 'b bb', 'b bb bbb', + 'a', 'c cc', 'c cc ccc', + 'd', 'b bb', 'd dd ddd' + ]; + let index = new flexSearch.Index(); + for (let i = 0; i < indexData.length; i++) { + index.add(i, indexData[i]); + } + + describe('indexClassTest', () => { + it('Index.add_case_1', 0, () => { + index.add(0, "content-0"); + expect(index.contain(0)).assertTrue(); + }) + + it('Index.add_case_2', 0, () => { + index.add(1, "content-1"); + expect(1).assertEqual(index.search("content-1")[0]); + }) + + it('Index.add_case_3', 0, () => { + index.add(2, "content-2"); + expect(2).assertEqual(index.search("content-2")[0]); + }) + + it('Index.add_case_4', 0, () => { + index.add(4, "content-3"); + expect(4).assertEqual(index.search("content-3")[0]); + }) + + it('Index.add_case_5', 0, () => { + index.add('five', "content-five"); + expect('five').assertEqual(index.search("content-five")[0]); + }) + + it("Index.addAsync_case_1", 0, () => { + index.addAsync(0, "content-1") + .then((index: flexSearch.Index) => { + expect(index.contain(0)).assertTrue(); + }) + }) + + it("Index.addAsync_case_2", 0, async () => { + let res: Index = await index.addAsync(indexData.length, "ddd") + let resSearch = res.search("ddd"); + console.log("flexSearch result 000 : " + JSON.stringify(resSearch)); + expect(12).assertEqual(resSearch[0]); + }) + + it("Index.addAsync_case_3", 0, async () => { + let res: Index = await index.addAsync(1, "content-2") + let resSearch = res.search("content-2"); + console.log("flexSearch result 111 : " + JSON.stringify(resSearch)); + expect(2).assertEqual(resSearch.length); + }) + + it("Index.addAsync_case_4", 0, async () => { + let res: Index = await index.addAsync("addKey", "addKey") + let resSearch = res.search(' '); + console.log("flexSearch result 222 : " + JSON.stringify(resSearch)); + expect(resSearch[0]).assertUndefined(); + }) + + it("Index.addAsync_case_5", 0, async () => { + let res: Index = await index.addAsync(9, " "); + let resSearch = res.search("addKey"); + console.log("flexSearch result 333 : " + JSON.stringify(resSearch)); + expect("addKey").assertEqual(resSearch[0]); + }) + + it("Index.append_case_1", 0, () => { + index.append(3, "append_3"); + let res = index.search("append_3"); + console.log("flexSearch append 000 :" + JSON.stringify(res)); + expect(3).assertEqual(res[0]); + }) + + it("Index.append_case_2", 0, () => { + index.append(2, "append_case_2"); + let res = index.search("append_case_2"); + console.log("flexSearch append 222 :" + JSON.stringify(res)); + expect(2).assertEqual(res[0]); + }) + + it("Index.append_case_3", 0, () => { + index.append(1, "append_case_3"); + let res = index.search("append_case_3"); + console.log("flexSearch append 333 :" + JSON.stringify(res)); + expect(1).assertEqual(res[0]); + }) + + it("Index.append_case_4", 0, () => { + index.append(0, "append_case_4"); + let res = index.search("append_case_4"); + console.log("flexSearch append 444 :" + JSON.stringify(res)); + expect(0).assertEqual(res[0]); + }) + + it("Index.append_case_5", 0, () => { + index.append(5, "append_case_5"); + let res = index.search("append_case_5"); + console.log("flexSearch append 555 :" + JSON.stringify(res)); + expect(5).assertEqual(res[0]); + }) + + it("Index.appendAsync_case_1", 0, async () => { + let index = new flexSearch.Index(); + let res: Index = await index.appendAsync(0, "appendAsync_case_1"); + let resSearch = res.search("appendAsync_case_2"); + console.log("flexSearch appendAsync 111 :" + JSON.stringify(resSearch)); + expect(0).assertEqual(resSearch.length); + }) + + it("Index.appendAsync_case_2", 0, async () => { + let index = new flexSearch.Index(); + let res: Index = await index.appendAsync(1, "appendAsync_case_2"); + let resSearch = res.search("appendAsync_case_2"); + console.log("flexSearch appendAsync 222 :" + JSON.stringify(resSearch)); + expect(res.contain(1)).assertTrue(); + }) + + it("Index.appendAsync_case_3", 0, async () => { + let res: Index = await index.appendAsync(0, "appendAsync_case_3", () => { + let resSearch = res.search("appendAsync_case_1"); + console.log("flexSearch appendAsync 333 :" + JSON.stringify(resSearch)); + expect(0).assertEqual(resSearch.length); + }); + }) + + it("Index.appendAsync_case_4", 0, async () => { + let res: Index = await index.appendAsync(8, "appendAsync_case_4", () => { + let resSearch = res.search("appendAsync_case_4"); + console.log("flexSearch appendAsync 444 :" + JSON.stringify(resSearch)); + expect(8).assertEqual(resSearch[0]); + }); + }) + + it("Index.appendAsync_case_5", 0, async () => { + let index = new flexSearch.Index(); + let res: Index = await index.appendAsync(7, "appendAsync_case_5", () => { + let resSearch = res.search("appendAsync_case_5"); + console.log("flexSearch appendAsync 555 :" + JSON.stringify(resSearch)); + expect(7).assertEqual(resSearch[0]); + }); + }) + + it("Index.update_case_1", 0, () => { + let index = ohos_index.addData(); + index.update(3, "update_case_1"); + expect(index.search("update_case_1", + { + query: "update_case_1", limit: 10, offset: 0, suggest: true + } + )[0]).assertEqual(3) + }) + + it("Index.update_case_2", 0, () => { + let index = ohos_index.addData(); + index.update(8, "update_case_2"); + expect(index.search("update_case_2", 10)[0]).assertEqual(8); + }) + + it("Index.update_case_3", 0, () => { + index.update(3, "update_case_3"); + let resSearch = index.search("update_case_3", 10); + console.log("flexSearch update 333 :" + JSON.stringify(resSearch)); + expect(3).assertEqual(resSearch[0]); + }) + + it("Index.update_case_4", 0, () => { + index.update("update_case_4", "update_case_4"); + let resSearch = index.search("update_case_4", 10); + console.log("flexSearch update 444 :" + JSON.stringify(resSearch)); + expect("update_case_4").assertEqual(resSearch[0]); + }) + + it("Index.update_case_5", 0, () => { + index.update("update_case_5", "update_case_5"); + let resSearch = index.search("update_case_5", 10); + console.log("flexSearch update 555 :" + JSON.stringify(resSearch)); + expect("update_case_5").assertEqual(resSearch[0]); + }) + + + it("Index.updateAsync_case_1", 0, async () => { + let index = ohos_index.addData(); + let res = await index.updateAsync(3, "updateAsync_case_1"); + expect(res.search("updateAsync_case_1", + { + query: "updateAsync_case_1", limit: 10, offset: 0, suggest: true + } + )[0]).assertEqual(3) + }) + + it("Index.updateAsync_case_2", 0, async () => { + let index = ohos_index.addData(); + let res = await index.updateAsync(3, "updateAsync_case_2"); + expect(res.search("updateAsync_case_2", 10)[0]).assertEqual(3); + }) + + it("Index.updateAsync_case_3", 0, async () => { + let index = ohos_index.addData(); + let res = await index.updateAsync(7, "updateAsync_case_3"); + let resSearch = res.search("updateAsync_case_3", 10); + console.log("flexSearch update 333 :" + JSON.stringify(resSearch)); + expect(7).assertEqual(resSearch[0]) + }) + + it("Index.updateAsync_case_4", 0, async () => { + let index = ohos_index.addData(); + let res = await index.updateAsync(9, "updateAsync_case_4"); + let resSearch = res.search("updateAsync_case_4", + { + query: "updateAsync_case_4", limit: 10, offset: 0, suggest: true + } + ) + console.log("flexSearch update 444 :" + JSON.stringify(resSearch)); + expect(9).assertEqual(resSearch[0]) + }) + + it("Index.updateAsync_case_5", 0, async () => { + let index = ohos_index.addData(); + let res = await index.updateAsync(0, "updateAsync_case_5"); + let resSearch = res.search("updateAsync_case_5", + { + query: "updateAsync_case_5", limit: 10, offset: 0, suggest: true + } + ) + console.log("flexSearch update 555 :" + JSON.stringify(resSearch)); + expect(0).assertEqual(resSearch[0]) + }) + + it("Index.removeAndContain_case_1", 0, () => { + let index = ohos_index.addData(); + let resSearch = index.search("a"); + console.log("flexSearch removeAndContain_case_1 :" + JSON.stringify(resSearch)); + expect(index.contain(0)).assertTrue() + expect(index.contain(6)).assertTrue() + index.remove(0); + expect(index.contain(0)).assertFalse(); + expect(index.contain(6)).assertTrue(); + }) + + it("Index.removeAndContain_case_2", 0, () => { + let index = ohos_index.addData(); + index.add("removeAndContain_case_2", "removeAndContain_case_2"); + let resSearch0 = index.search("removeAndContain_case_2"); + console.log("flexSearch removeAndContain_case_2 00 :" + JSON.stringify(resSearch0)); + expect("removeAndContain_case_2").assertEqual(resSearch0[0]); + index.remove("removeAndContain_case_2"); + let resSearch = index.search("removeAndContain_case_2"); + console.log("flexSearch removeAndContain_case_2 :" + JSON.stringify(resSearch)); + expect(0).assertEqual(resSearch.length); + }) + + it("Index.removeAndContain_case_3", 0, () => { + let index = ohos_index.addData(); + let resSearch = index.search("b bb"); + console.log("flexSearch removeAndContain_case_3 :" + JSON.stringify(resSearch)); + expect(resSearch.length).assertEqual(2); + expect(index.contain(4)).assertTrue(); + index.remove(4); + expect(index.contain(4)).assertFalse(); + expect(resSearch.length).assertEqual(1); + expect(index.contain(10)).assertTrue(); + index.remove(10); + expect(index.contain(10)).assertFalse(); + expect(resSearch.length).assertEqual(0); + }) + + it("Index.removeAsync_case_1", 0, async () => { + let res = await ohos_index.addData().removeAsync(6) + expect(res.search("6")[0]).assertUndefined(); + }) + + it("Index.removeAsync_case_2", 0, async () => { + let index = ohos_index.addData(); + let res = await index.removeAsync(3) + expect(res.search("1")).not().assertUndefined(); + }) + + it("Index.removeAsync_case_3", 0, async () => { + let index = ohos_index.addData(); + index.add("removeAsync_case_3", "removeAsync_case_3"); + let resSearch = index.search("removeAsync_case_3"); + expect("removeAsync_case_3").assertEqual(resSearch[0]); + let res = await index.removeAsync("removeAsync_case_3", () => { + let resSearch0 = res.search("removeAsync_case_3"); + expect(0).not().assertEqual(resSearch0.length); + }) + }) + + it("Index.contain_case_1", 0, () => { + expect(ohos_index.addData().contain(0)).assertTrue(); + }) + + it("Index.contain_case_2", 0, () => { + expect(ohos_index.addData().contain(101)).assertFalse(); + }) + + it("Index.export_case", 0, async () => { + class exportedData { + key: number | string = ''; + data: string = ''; + + constructor(key?: number | string, data?: string) { + if (key != undefined) { + this.key = key; + } + if (data != undefined) { + this.data = data; + } + } + } + + let index: Index = new flexSearch.Index(); + index.add('doc1', 'This is the content of document 1.'); + index.add('doc2', 'Another document with different content.'); + await index.export((key, data) => { + let tmp = new exportedData(key, data); + expect(tmp).not().assertNull(); + }); + }) + + it("Index.import_case", 0, async () => { + let newIndex: Index = new flexSearch.Index(); + const data = `{"doc1":1,"doc2":1}`; + newIndex.import("reg", data) + expect(newIndex.contain("doc1")).assertTrue(); + }) + + it("Index.searchAsync_case1", 0, async () => { + let index: Index = ohos_index.addData(); + await index.searchAsync( + "b bb", + { + limit: 4, + offset: 1, + suggest: true + }, + (ids: Id[]) => { + expect(ids.length).assertEqual(1); + expect(ids[0]).assertEqual(10); + } + ); + }) + + it("Index.searchAsync_case2", 0, () => { + const indexObj: Index = new flexSearch.Index(); + indexObj.add(1, "This is a sample document"); + indexObj.add("document", "document"); + indexObj.add(5, "document"); + const searchOptions: SearchOptions = { + query: "document", + limit: 2 + }; + indexObj.searchAsync("document", searchOptions) + .then((result: IndexSearchResult) => { + console.log("flexSearch Search result : ", JSON.stringify(result)); + expect(index.contain("document")).assertTrue(); + expect(2).assertEqual(result.length); + }) + }) + }) +} diff --git a/ohos/entry/src/ohosTest/ets/test/List.test.ets b/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 00000000..62a84413 --- /dev/null +++ b/ohos/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import documentClassXts from './Document.test'; +import documentClassAsyncMethodXts from './DocumentAsyncMethod.test'; +import indexClassXTS from './Index.test'; + +export default function testsuite() { + indexClassXTS(); + documentClassXts(); + documentClassAsyncMethodXts(); +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 00000000..893f1270 --- /dev/null +++ b/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import hilog from '@ohos.hilog'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; + +export default class TestAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); + hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); + hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); + let abilityDelegator : AbilityDelegatorRegistry.AbilityDelegator; + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + let abilityDelegatorArguments : AbilityDelegatorRegistry.AbilityDelegatorArgs; + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); + windowStage.loadContent('testability/pages/Index', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', + JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); + } + + onForeground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); + } + + onBackground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); + } +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 00000000..5cbcf2a0 --- /dev/null +++ b/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + aboutToAppear() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); + } + @State message: string = 'Hello World' + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + }.type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/module.json5 b/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 00000000..4fc97017 --- /dev/null +++ b/ohos/entry/src/ohosTest/module.json5 @@ -0,0 +1,37 @@ +{ + "module": { + "name": "entry_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "abilities": [ + { + "name": "TestAbility", + "srcEntry": "./ets/testability/TestAbility.ets", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "exported": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/ohos/entry/src/ohosTest/resources/base/element/color.json b/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 00000000..3c712962 --- /dev/null +++ b/ohos/entry/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/resources/base/element/string.json b/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 00000000..65d8fa5a --- /dev/null +++ b/ohos/entry/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_test_desc", + "value": "test ability description" + }, + { + "name": "TestAbility_desc", + "value": "the test ability" + }, + { + "name": "TestAbility_label", + "value": "test label" + } + ] +} \ No newline at end of file diff --git a/ohos/entry/src/ohosTest/resources/base/media/icon.png b/ohos/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 00000000..ce307a88 Binary files /dev/null and b/ohos/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 00000000..b7e7343c --- /dev/null +++ b/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/ohos/hvigor/hvigor-config.json5 b/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 00000000..277e9a1f --- /dev/null +++ b/ohos/hvigor/hvigor-config.json5 @@ -0,0 +1,18 @@ +{ + "hvigorVersion": "3.0.2", + "dependencies": { + "@ohos/hvigor-ohos-plugin": "3.0.2" + }, + "execution": { + // "daemon": true, /* Enable daemon compilation. Default: true */ + // "incremental": true, /* Enable incremental compilation. Default: true */ + // "parallel": true, /* Enable parallel compilation. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Default: false */ + } +} diff --git a/ohos/hvigor/hvigor-wrapper.js b/ohos/hvigor/hvigor-wrapper.js new file mode 100644 index 00000000..2be61f68 --- /dev/null +++ b/ohos/hvigor/hvigor-wrapper.js @@ -0,0 +1 @@ +"use strict";var u=require("path"),D=require("os"),e=require("fs"),t=require("child_process"),r=require("crypto"),n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},i={},C={},F=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(C,"__esModule",{value:!0}),C.maxPathLength=C.isMac=C.isLinux=C.isWindows=void 0;const E=F(D),A="Windows_NT",o="Darwin";function a(){return E.default.type()===A}function c(){return E.default.type()===o}C.isWindows=a,C.isLinux=function(){return"Linux"===E.default.type()},C.isMac=c,C.maxPathLength=function(){return c()?1016:a()?259:4095},function(e){var t=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),r=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),i=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&t(D,u,e);return r(D,u),D};Object.defineProperty(e,"__esModule",{value:!0}),e.WORK_SPACE=e.HVIGOR_PROJECT_WRAPPER_HOME=e.HVIGOR_PROJECT_ROOT_DIR=e.HVIGOR_PROJECT_CACHES_HOME=e.HVIGOR_PNPM_STORE_PATH=e.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=e.PROJECT_CACHES=e.HVIGOR_WRAPPER_TOOLS_HOME=e.HVIGOR_USER_HOME=e.DEFAULT_PACKAGE_JSON=e.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME=e.PNPM=e.HVIGOR=e.NPM_TOOL=e.PNPM_TOOL=e.HVIGOR_ENGINE_PACKAGE_NAME=void 0;const F=i(D),E=i(u),A=C;e.HVIGOR_ENGINE_PACKAGE_NAME="@ohos/hvigor",e.PNPM_TOOL=(0,A.isWindows)()?"pnpm.cmd":"pnpm",e.NPM_TOOL=(0,A.isWindows)()?"npm.cmd":"npm",e.HVIGOR="hvigor",e.PNPM="pnpm",e.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME="hvigor-config.json5",e.DEFAULT_PACKAGE_JSON="package.json",e.HVIGOR_USER_HOME=E.resolve(F.homedir(),".hvigor"),e.HVIGOR_WRAPPER_TOOLS_HOME=E.resolve(e.HVIGOR_USER_HOME,"wrapper","tools"),e.PROJECT_CACHES="project_caches",e.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=E.resolve(e.HVIGOR_WRAPPER_TOOLS_HOME,"node_modules",".bin",e.PNPM_TOOL),e.HVIGOR_PNPM_STORE_PATH=E.resolve(e.HVIGOR_USER_HOME,"caches"),e.HVIGOR_PROJECT_CACHES_HOME=E.resolve(e.HVIGOR_USER_HOME,e.PROJECT_CACHES),e.HVIGOR_PROJECT_ROOT_DIR=process.cwd(),e.HVIGOR_PROJECT_WRAPPER_HOME=E.resolve(e.HVIGOR_PROJECT_ROOT_DIR,e.HVIGOR),e.WORK_SPACE="workspace"}(i);var s={},l={};Object.defineProperty(l,"__esModule",{value:!0}),l.logInfoPrintConsole=l.logErrorAndExit=void 0,l.logErrorAndExit=function(u){u instanceof Error?console.error(u.message):console.error(u),process.exit(-1)},l.logInfoPrintConsole=function(u){console.log(u)};var B=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),d=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),f=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&B(D,u,e);return d(D,u),D};Object.defineProperty(s,"__esModule",{value:!0});var _=s.executeBuild=void 0;const p=f(e),O=f(u),h=l;_=s.executeBuild=function(u){const D=O.resolve(u,"node_modules","@ohos","hvigor","bin","hvigor.js");try{const u=p.realpathSync(D);require(u)}catch(e){(0,h.logErrorAndExit)(`Error: ENOENT: no such file ${D},delete ${u} and retry.`)}};var P={},v={},g={},m={};Object.defineProperty(m,"__esModule",{value:!0}),m.Unicode=void 0;class R{}m.Unicode=R,R.SPACE_SEPARATOR=/[\u1680\u2000-\u200A\u202F\u205F\u3000]/,R.ID_START=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/,R.ID_CONTINUE=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u09FC\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDE00-\uDE3E\uDE47\uDE50-\uDE83\uDE86-\uDE99\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD47\uDD50-\uDD59]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/,Object.defineProperty(g,"__esModule",{value:!0}),g.JudgeUtil=void 0;const y=m;g.JudgeUtil=class{static isIgnoreChar(u){return"string"==typeof u&&("\t"===u||"\v"===u||"\f"===u||" "===u||" "===u||"\ufeff"===u||"\n"===u||"\r"===u||"\u2028"===u||"\u2029"===u)}static isSpaceSeparator(u){return"string"==typeof u&&y.Unicode.SPACE_SEPARATOR.test(u)}static isIdStartChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||"$"===u||"_"===u||y.Unicode.ID_START.test(u))}static isIdContinueChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||u>="0"&&u<="9"||"$"===u||"_"===u||"‌"===u||"‍"===u||y.Unicode.ID_CONTINUE.test(u))}static isDigitWithoutZero(u){return/[1-9]/.test(u)}static isDigit(u){return"string"==typeof u&&/[0-9]/.test(u)}static isHexDigit(u){return"string"==typeof u&&/[0-9A-Fa-f]/.test(u)}};var I=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(v,"__esModule",{value:!0}),v.parseJsonText=v.parseJsonFile=void 0;const N=I(e),b=I(D),S=I(u),w=g;var H;!function(u){u[u.Char=0]="Char",u[u.EOF=1]="EOF",u[u.Identifier=2]="Identifier"}(H||(H={}));let x,M,T,V,G,j,J="start",U=[],W=0,L=1,$=0,k=!1,K="default",z="'",q=1;function Z(u,D=!1){M=String(u),J="start",U=[],W=0,L=1,$=0,V=void 0,k=D;do{x=X(),ru[J]()}while("eof"!==x.type);return V}function X(){for(K="default",G="",z="'",q=1;;){j=Q();const u=uu[K]();if(u)return u}}function Q(){if(M[W])return String.fromCodePoint(M.codePointAt(W))}function Y(){const u=Q();return"\n"===u?(L++,$=0):u?$+=u.length:$++,u&&(W+=u.length),u}v.parseJsonFile=function(u,D=!1,e="utf-8"){const t=N.default.readFileSync(S.default.resolve(u),{encoding:e});try{return Z(t,D)}catch(D){if(D instanceof SyntaxError){const e=D.message.split("at");if(2===e.length)throw new Error(`${e[0].trim()}${b.default.EOL}\t at ${u}:${e[1].trim()}`)}throw new Error(`${u} is not in valid JSON/JSON5 format.`)}},v.parseJsonText=Z;const uu={default(){switch(j){case"/":return Y(),void(K="comment");case void 0:return Y(),Du("eof")}if(!w.JudgeUtil.isIgnoreChar(j)&&!w.JudgeUtil.isSpaceSeparator(j))return uu[J]();Y()},start(){K="value"},beforePropertyName(){switch(j){case"$":case"_":return G=Y(),void(K="identifierName");case"\\":return Y(),void(K="identifierNameStartEscape");case"}":return Du("punctuator",Y());case'"':case"'":return z=j,Y(),void(K="string")}if(w.JudgeUtil.isIdStartChar(j))return G+=Y(),void(K="identifierName");throw Fu(H.Char,Y())},afterPropertyName(){if(":"===j)return Du("punctuator",Y());throw Fu(H.Char,Y())},beforePropertyValue(){K="value"},afterPropertyValue(){switch(j){case",":case"}":return Du("punctuator",Y())}throw Fu(H.Char,Y())},beforeArrayValue(){if("]"===j)return Du("punctuator",Y());K="value"},afterArrayValue(){switch(j){case",":case"]":return Du("punctuator",Y())}throw Fu(H.Char,Y())},end(){throw Fu(H.Char,Y())},comment(){switch(j){case"*":return Y(),void(K="multiLineComment");case"/":return Y(),void(K="singleLineComment")}throw Fu(H.Char,Y())},multiLineComment(){switch(j){case"*":return Y(),void(K="multiLineCommentAsterisk");case void 0:throw Fu(H.Char,Y())}Y()},multiLineCommentAsterisk(){switch(j){case"*":return void Y();case"/":return Y(),void(K="default");case void 0:throw Fu(H.Char,Y())}Y(),K="multiLineComment"},singleLineComment(){switch(j){case"\n":case"\r":case"\u2028":case"\u2029":return Y(),void(K="default");case void 0:return Y(),Du("eof")}Y()},value(){switch(j){case"{":case"[":return Du("punctuator",Y());case"n":return Y(),eu("ull"),Du("null",null);case"t":return Y(),eu("rue"),Du("boolean",!0);case"f":return Y(),eu("alse"),Du("boolean",!1);case"-":case"+":return"-"===Y()&&(q=-1),void(K="numerical");case".":case"0":case"I":case"N":return void(K="numerical");case'"':case"'":return z=j,Y(),G="",void(K="string")}if(void 0===j||!w.JudgeUtil.isDigitWithoutZero(j))throw Fu(H.Char,Y());K="numerical"},numerical(){switch(j){case".":return G=Y(),void(K="decimalPointLeading");case"0":return G=Y(),void(K="zero");case"I":return Y(),eu("nfinity"),Du("numeric",q*(1/0));case"N":return Y(),eu("aN"),Du("numeric",NaN)}if(void 0!==j&&w.JudgeUtil.isDigitWithoutZero(j))return G=Y(),void(K="decimalInteger");throw Fu(H.Char,Y())},zero(){switch(j){case".":case"e":case"E":return void(K="decimal");case"x":case"X":return G+=Y(),void(K="hexadecimal")}return Du("numeric",0)},decimalInteger(){switch(j){case".":case"e":case"E":return void(K="decimal")}if(!w.JudgeUtil.isDigit(j))return Du("numeric",q*Number(G));G+=Y()},decimal(){switch(j){case".":G+=Y(),K="decimalFraction";break;case"e":case"E":G+=Y(),K="decimalExponent"}},decimalPointLeading(){if(w.JudgeUtil.isDigit(j))return G+=Y(),void(K="decimalFraction");throw Fu(H.Char,Y())},decimalFraction(){switch(j){case"e":case"E":return G+=Y(),void(K="decimalExponent")}if(!w.JudgeUtil.isDigit(j))return Du("numeric",q*Number(G));G+=Y()},decimalExponent(){switch(j){case"+":case"-":return G+=Y(),void(K="decimalExponentSign")}if(w.JudgeUtil.isDigit(j))return G+=Y(),void(K="decimalExponentInteger");throw Fu(H.Char,Y())},decimalExponentSign(){if(w.JudgeUtil.isDigit(j))return G+=Y(),void(K="decimalExponentInteger");throw Fu(H.Char,Y())},decimalExponentInteger(){if(!w.JudgeUtil.isDigit(j))return Du("numeric",q*Number(G));G+=Y()},hexadecimal(){if(w.JudgeUtil.isHexDigit(j))return G+=Y(),void(K="hexadecimalInteger");throw Fu(H.Char,Y())},hexadecimalInteger(){if(!w.JudgeUtil.isHexDigit(j))return Du("numeric",q*Number(G));G+=Y()},identifierNameStartEscape(){if("u"!==j)throw Fu(H.Char,Y());Y();const u=tu();switch(u){case"$":case"_":break;default:if(!w.JudgeUtil.isIdStartChar(u))throw Fu(H.Identifier)}G+=u,K="identifierName"},identifierName(){switch(j){case"$":case"_":case"‌":case"‍":return void(G+=Y());case"\\":return Y(),void(K="identifierNameEscape")}if(!w.JudgeUtil.isIdContinueChar(j))return Du("identifier",G);G+=Y()},identifierNameEscape(){if("u"!==j)throw Fu(H.Char,Y());Y();const u=tu();switch(u){case"$":case"_":case"‌":case"‍":break;default:if(!w.JudgeUtil.isIdContinueChar(u))throw Fu(H.Identifier)}G+=u,K="identifierName"},string(){switch(j){case"\\":return Y(),void(G+=function(){const u=Q(),D=function(){switch(Q()){case"b":return Y(),"\b";case"f":return Y(),"\f";case"n":return Y(),"\n";case"r":return Y(),"\r";case"t":return Y(),"\t";case"v":return Y(),"\v"}return}();if(D)return D;switch(u){case"0":if(Y(),w.JudgeUtil.isDigit(Q()))throw Fu(H.Char,Y());return"\0";case"x":return Y(),function(){let u="",D=Q();if(!w.JudgeUtil.isHexDigit(D))throw Fu(H.Char,Y());if(u+=Y(),D=Q(),!w.JudgeUtil.isHexDigit(D))throw Fu(H.Char,Y());return u+=Y(),String.fromCodePoint(parseInt(u,16))}();case"u":return Y(),tu();case"\n":case"\u2028":case"\u2029":return Y(),"";case"\r":return Y(),"\n"===Q()&&Y(),""}if(void 0===u||w.JudgeUtil.isDigitWithoutZero(u))throw Fu(H.Char,Y());return Y()}());case'"':case"'":if(j===z){const u=Du("string",G);return Y(),u}return void(G+=Y());case"\n":case"\r":case void 0:throw Fu(H.Char,Y());case"\u2028":case"\u2029":!function(u){console.warn(`JSON5: '${Cu(u)}' in strings is not valid ECMAScript; consider escaping.`)}(j)}G+=Y()}};function Du(u,D){return{type:u,value:D,line:L,column:$}}function eu(u){for(const D of u){if(Q()!==D)throw Fu(H.Char,Y());Y()}}function tu(){let u="",D=4;for(;D-- >0;){const D=Q();if(!w.JudgeUtil.isHexDigit(D))throw Fu(H.Char,Y());u+=Y()}return String.fromCodePoint(parseInt(u,16))}const ru={start(){if("eof"===x.type)throw Fu(H.EOF);nu()},beforePropertyName(){switch(x.type){case"identifier":case"string":return T=x.value,void(J="afterPropertyName");case"punctuator":return void iu();case"eof":throw Fu(H.EOF)}},afterPropertyName(){if("eof"===x.type)throw Fu(H.EOF);J="beforePropertyValue"},beforePropertyValue(){if("eof"===x.type)throw Fu(H.EOF);nu()},afterPropertyValue(){if("eof"===x.type)throw Fu(H.EOF);switch(x.value){case",":return void(J="beforePropertyName");case"}":iu()}},beforeArrayValue(){if("eof"===x.type)throw Fu(H.EOF);"punctuator"!==x.type||"]"!==x.value?nu():iu()},afterArrayValue(){if("eof"===x.type)throw Fu(H.EOF);switch(x.value){case",":return void(J="beforeArrayValue");case"]":iu()}},end(){}};function nu(){const u=function(){let u;switch(x.type){case"punctuator":switch(x.value){case"{":u={};break;case"[":u=[]}break;case"null":case"boolean":case"numeric":case"string":u=x.value}return u}();if(k&&"object"==typeof u&&(u._line=L,u._column=$),void 0===V)V=u;else{const D=U[U.length-1];Array.isArray(D)?k&&"object"!=typeof u?D.push({value:u,_line:L,_column:$}):D.push(u):D[T]=k&&"object"!=typeof u?{value:u,_line:L,_column:$}:u}!function(u){if(u&&"object"==typeof u)U.push(u),J=Array.isArray(u)?"beforeArrayValue":"beforePropertyName";else{const u=U[U.length-1];J=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}}(u)}function iu(){U.pop();const u=U[U.length-1];J=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}function Cu(u){const D={"'":"\\'",'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\v":"\\v","\0":"\\0","\u2028":"\\u2028","\u2029":"\\u2029"};if(D[u])return D[u];if(u<" "){const D=u.charCodeAt(0).toString(16);return`\\x${`00${D}`.substring(D.length)}`}return u}function Fu(u,D){let e="";switch(u){case H.Char:e=void 0===D?`JSON5: invalid end of input at ${L}:${$}`:`JSON5: invalid character '${Cu(D)}' at ${L}:${$}`;break;case H.EOF:e=`JSON5: invalid end of input at ${L}:${$}`;break;case H.Identifier:$-=5,e=`JSON5: invalid identifier character at ${L}:${$}`}const t=new Eu(e);return t.lineNumber=L,t.columnNumber=$,t}class Eu extends SyntaxError{}var Au={},ou=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),au=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),cu=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&ou(D,u,e);return au(D,u),D},su=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(Au,"__esModule",{value:!0}),Au.isFileExists=Au.offlinePluginConversion=Au.executeCommand=Au.getNpmPath=Au.hasNpmPackInPaths=void 0;const lu=t,Bu=su(e),du=cu(u),fu=i,_u=l;Au.hasNpmPackInPaths=function(u,D){try{return require.resolve(u,{paths:[...D]}),!0}catch(u){return!1}},Au.getNpmPath=function(){const u=process.execPath;return du.join(du.dirname(u),fu.NPM_TOOL)},Au.executeCommand=function(u,D,e){0!==(0,lu.spawnSync)(u,D,e).status&&(0,_u.logErrorAndExit)(`Error: ${u} ${D} execute failed.See above for details.`)},Au.offlinePluginConversion=function(u,D){return D.startsWith("file:")||D.endsWith(".tgz")?du.resolve(u,fu.HVIGOR,D.replace("file:","")):D},Au.isFileExists=function(u){return Bu.default.existsSync(u)&&Bu.default.statSync(u).isFile()};var pu={};!function(u){var D=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(u,"__esModule",{value:!0}),u.hashFile=u.hash=u.createHash=void 0;const t=D(r),i=D(e);u.createHash=(u="MD5")=>t.default.createHash(u);u.hash=(D,e)=>(0,u.createHash)(e).update(D).digest("hex");u.hashFile=(D,e)=>{if(i.default.existsSync(D))return(0,u.hash)(i.default.readFileSync(D,"utf-8"),e)}}(pu);var Ou=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),hu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),Pu=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&Ou(D,u,e);return hu(D,u),D},vu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(P,"__esModule",{value:!0});var gu=P.initProjectWorkSpace=void 0;const mu=Pu(e),Ru=Pu(u),yu=i,Iu=v,Nu=l,bu=Au,Su=vu(D),wu=pu;let Hu,xu,Mu;function Tu(u,D,e){return void 0!==e.dependencies&&(0,bu.offlinePluginConversion)(yu.HVIGOR_PROJECT_ROOT_DIR,D.dependencies[u])===Ru.normalize(e.dependencies[u])}function Vu(){const u=Ru.join(Mu,yu.WORK_SPACE);if((0,Nu.logInfoPrintConsole)("Hvigor cleaning..."),!mu.existsSync(u))return;const D=mu.readdirSync(u);if(!D||0===D.length)return;const e=Ru.resolve(Mu,"node_modules","@ohos","hvigor","bin","hvigor.js");mu.existsSync(e)&&(0,bu.executeCommand)(process.argv[0],[e,"--stop-daemon"],{});try{D.forEach((D=>{mu.rmSync(Ru.resolve(u,D),{recursive:!0})}))}catch(D){(0,Nu.logErrorAndExit)(`The hvigor build tool cannot be installed. Please manually clear the workspace directory and synchronize the project again.\n\n Workspace Path: ${u}.`)}}gu=P.initProjectWorkSpace=function(){if(Hu=function(){const u=Ru.resolve(yu.HVIGOR_PROJECT_WRAPPER_HOME,yu.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME);mu.existsSync(u)||(0,Nu.logErrorAndExit)(`Error: Hvigor config file ${u} does not exist.`);return(0,Iu.parseJsonFile)(u)}(),Mu=function(u){let D,e=u.hvigorVersion;e.endsWith(".tgz")&&(e=function(u){let D=Ru.normalize(u);const e=D.lastIndexOf(Ru.sep);-1!==e&&(D=D.substring(e+1));D=D.replace(".tgz","");let t=0;for(let u=0;u="0"&&D.charAt(u)<="9"){t=u;break}return D=D.substring(t),D}(e));D=e>"2.5.0"?function(u){let D=`${yu.HVIGOR_ENGINE_PACKAGE_NAME}@${u.hvigorVersion}`;const e=u.dependencies;if(e){Object.getOwnPropertyNames(e).sort().forEach((u=>{D+=`,${u}@${e[u]}`}))}return(0,wu.hash)(D)}(u):(0,wu.hash)(process.cwd());return Ru.resolve(Su.default.homedir(),".hvigor","project_caches",D)}(Hu),xu=function(){const u=Ru.resolve(Mu,yu.WORK_SPACE,yu.DEFAULT_PACKAGE_JSON);return mu.existsSync(u)?(0,Iu.parseJsonFile)(u):{dependencies:{}}}(),!(0,bu.hasNpmPackInPaths)(yu.HVIGOR_ENGINE_PACKAGE_NAME,[Ru.join(Mu,yu.WORK_SPACE)])||(0,bu.offlinePluginConversion)(yu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion)!==xu.dependencies[yu.HVIGOR_ENGINE_PACKAGE_NAME]||!function(){function u(u){const D=null==u?void 0:u.dependencies;return void 0===D?0:Object.getOwnPropertyNames(D).length}const D=u(Hu),e=u(xu);if(D+1!==e)return!1;for(const u in null==Hu?void 0:Hu.dependencies)if(!(0,bu.hasNpmPackInPaths)(u,[Ru.join(Mu,yu.WORK_SPACE)])||!Tu(u,Hu,xu))return!1;return!0}()){Vu();try{!function(){(0,Nu.logInfoPrintConsole)("Hvigor installing...");for(const u in Hu.dependencies)Hu.dependencies[u]&&(Hu.dependencies[u]=(0,bu.offlinePluginConversion)(yu.HVIGOR_PROJECT_ROOT_DIR,Hu.dependencies[u]));const u={dependencies:{...Hu.dependencies}};u.dependencies[yu.HVIGOR_ENGINE_PACKAGE_NAME]=(0,bu.offlinePluginConversion)(yu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion);const D=Ru.join(Mu,yu.WORK_SPACE);try{mu.mkdirSync(D,{recursive:!0});const e=Ru.resolve(D,yu.DEFAULT_PACKAGE_JSON);mu.writeFileSync(e,JSON.stringify(u))}catch(u){(0,Nu.logErrorAndExit)(u)}(function(){const u=["config","set","store-dir",yu.HVIGOR_PNPM_STORE_PATH],D={cwd:Ru.join(Mu,yu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,bu.executeCommand)(yu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)})(),function(){const u=["install"],D={cwd:Ru.join(Mu,yu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,bu.executeCommand)(yu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)}(),(0,Nu.logInfoPrintConsole)("Hvigor install success.")}()}catch(u){Vu()}}return Mu};var Gu={};!function(r){var C=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),F=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),E=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&C(D,u,e);return F(D,u),D},A=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(r,"__esModule",{value:!0}),r.executeInstallPnpm=r.isPnpmInstalled=r.environmentHandler=r.checkNpmConifg=r.PNPM_VERSION=void 0;const o=t,a=E(e),c=A(D),s=E(u),B=i,d=l,f=Au;r.PNPM_VERSION="7.30.0",r.checkNpmConifg=function(){const u=s.resolve(B.HVIGOR_PROJECT_ROOT_DIR,".npmrc"),D=s.resolve(c.default.homedir(),".npmrc");if((0,f.isFileExists)(u)||(0,f.isFileExists)(D))return;const e=(0,f.getNpmPath)(),t=(0,o.spawnSync)(e,["config","get","prefix"],{cwd:B.HVIGOR_PROJECT_ROOT_DIR});if(0!==t.status||!t.stdout)return void(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.");const r=s.resolve(`${t.stdout}`.replace(/[\r\n]/gi,""),".npmrc");(0,f.isFileExists)(r)||(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.")},r.environmentHandler=function(){process.env["npm_config_update-notifier"]="false"},r.isPnpmInstalled=function(){return!!a.existsSync(B.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH)&&(0,f.hasNpmPackInPaths)("pnpm",[B.HVIGOR_WRAPPER_TOOLS_HOME])},r.executeInstallPnpm=function(){(0,d.logInfoPrintConsole)(`Installing pnpm@${r.PNPM_VERSION}...`);const u=(0,f.getNpmPath)();!function(){const u=s.resolve(B.HVIGOR_WRAPPER_TOOLS_HOME,B.DEFAULT_PACKAGE_JSON);try{a.existsSync(B.HVIGOR_WRAPPER_TOOLS_HOME)||a.mkdirSync(B.HVIGOR_WRAPPER_TOOLS_HOME,{recursive:!0});const D={dependencies:{}};D.dependencies[B.PNPM]=r.PNPM_VERSION,a.writeFileSync(u,JSON.stringify(D))}catch(D){(0,d.logErrorAndExit)(`Error: EPERM: operation not permitted,create ${u} failed.`)}}(),(0,f.executeCommand)(u,["install","pnpm"],{cwd:B.HVIGOR_WRAPPER_TOOLS_HOME,stdio:["inherit","inherit","inherit"],env:process.env}),(0,d.logInfoPrintConsole)("Pnpm install success.")}}(Gu),function(){Gu.checkNpmConifg(),Gu.environmentHandler(),Gu.isPnpmInstalled()||Gu.executeInstallPnpm();const D=gu();_(u.join(D,i.WORK_SPACE))}(); \ No newline at end of file diff --git a/ohos/hvigorfile.ts b/ohos/hvigorfile.ts new file mode 100644 index 00000000..7e16aa4d --- /dev/null +++ b/ohos/hvigorfile.ts @@ -0,0 +1,6 @@ + import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/ohos/hvigorw b/ohos/hvigorw new file mode 100644 index 00000000..ff6a29a2 --- /dev/null +++ b/ohos/hvigorw @@ -0,0 +1,48 @@ +#!/bin/bash + +# ---------------------------------------------------------------------------- +# Hvigor startup script, version 1.0.0 +# +# Required ENV vars: +# ------------------ +# NODE_HOME - location of a Node home dir +# or +# Add /usr/local/nodejs/bin to the PATH environment variable +# ---------------------------------------------------------------------------- + +HVIGOR_APP_HOME="`pwd -P`" +HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js +warn() { + echo "" + echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +error() { + echo "" + echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +fail() { + error "$@" + exit 1 +} + +# Determine node to start hvigor wrapper script +if [ -n "${NODE_HOME}" ];then + EXECUTABLE_NODE="${NODE_HOME}/bin/node" + if [ ! -x "$EXECUTABLE_NODE" ];then + fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed" + fi +else + EXECUTABLE_NODE="node" + which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path" +fi + +# Check hvigor wrapper script +if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then + fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}" +fi + +# start hvigor-wrapper script +exec "${EXECUTABLE_NODE}" \ + "${HVIGOR_WRAPPER_SCRIPT}" "$@" diff --git a/ohos/hvigorw.bat b/ohos/hvigorw.bat new file mode 100644 index 00000000..d570007e --- /dev/null +++ b/ohos/hvigorw.bat @@ -0,0 +1,64 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Hvigor startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js +set NODE_EXE=node.exe + +goto start + +:start +@rem Find node.exe +if defined NODE_HOME goto findNodeFromNodeHome + +%NODE_EXE% --version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:findNodeFromNodeHome +set NODE_HOME=%NODE_HOME:"=% +set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE% + +if exist "%NODE_EXE_PATH%" goto execute +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:execute +@rem Execute hvigor +"%NODE_EXE%" "%WRAPPER_MODULE_PATH%" %* + +if "%ERRORLEVEL%" == "0" goto hvigorwEnd + +:fail +exit /b 1 + +:hvigorwEnd +if "%OS%" == "Windows_NT" endlocal + +:end diff --git a/ohos/library/.gitignore b/ohos/library/.gitignore new file mode 100644 index 00000000..e2713a27 --- /dev/null +++ b/ohos/library/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/ohos/library/build-profile.json5 b/ohos/library/build-profile.json5 new file mode 100644 index 00000000..08f43dcb --- /dev/null +++ b/ohos/library/build-profile.json5 @@ -0,0 +1,28 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + } + }, + ], + "targets": [ + { + "name": "default" + } + ] +} diff --git a/ohos/library/consumer-rules.txt b/ohos/library/consumer-rules.txt new file mode 100644 index 00000000..e69de29b diff --git a/ohos/library/hvigorfile.ts b/ohos/library/hvigorfile.ts new file mode 100644 index 00000000..42187071 --- /dev/null +++ b/ohos/library/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/ohos/library/index.d.ts b/ohos/library/index.d.ts new file mode 100644 index 00000000..ecde8e79 --- /dev/null +++ b/ohos/library/index.d.ts @@ -0,0 +1,395 @@ +// Type definitions for flexsearch 0.7 +// Project: https://github.com/nextapps-de/flexsearch/ +// Definitions by: LOSSES Don +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/************************************/ +/* Utils */ +/************************************/ +export type Id = number | string; +export type Limit = number; +export type ExportHandler = (id: string | number, value: T) => void; +export type AsyncCallback = T extends undefined ? () => void : (result: T) => void; +export type UnknownFunction = (...x: unknown[]) => unknown; + +export type StoreOption = boolean | string | string[]; +export type EnrichStoreOption = true | string | string[]; + +/************************************/ +/* Common Options */ +/************************************/ + +/** + * **Document:** + * * Presets: https://github.com/nextapps-de/flexsearch#presets + */ +export type Preset = "memory" | "performance" | "match" | "score" | "default"; + +/** + * **Document:** + * * Tokenizer: https://github.com/nextapps-de/flexsearch#tokenizer-prefix-search + * * Add custom tokenizer: https://github.com/nextapps-de/flexsearch#add-custom-tokenizer + */ +export type Tokenizer = + | "strict" + | "forward" + | "reverse" + | "full" + | ((x: string) => string[]); + +/** + * **Document:** + * * Encoders: https://github.com/nextapps-de/flexsearch#encoders + */ +export type Encoders = + | false + | "default" + | "simple" + | "balance" + | "advanced" + | "extra" + | ((x: string) => string[]); + +/** + * **Document:** + * * Contextual search: https://github.com/nextapps-de/flexsearch#contextual + */ +export interface ContextOptions { + resolution: number; + depth: false | number; + bidirectional: boolean; +} + +/** + * **Document:** + * * Search options: https://github.com/nextapps-de/flexsearch#search-options + */ +export interface SearchOptions { + query?: string; + limit?: number; + offset?: number; + suggest?: boolean; +} + +/** + * **Document:** + * * The document descriptor: https://github.com/nextapps-de/flexsearch#the-document-descriptor + */ +export interface Descriptor { + id: string | number; + field: string[] | Array>; +} + +/** + * **Document:** + * * Context Options: https://github.com/nextapps-de/flexsearch#context-options + */ +export interface ContextOptions { + resolution: number; + depth: false | number; + bidirectional: boolean; +} + +/** + * **Document:** + * * Charset options: https://github.com/nextapps-de/flexsearch#charset-options + */ +export interface CharsetOptions { + split: false | string | RegExp; + rtl: boolean; + encode: (x: string) => string[]; +} + +export interface Stemmer { + [key: string]: string; +} + +export interface Matcher { + [key: string]: string; +} + +export type FilterFunction = (x: string) => boolean; +export type FilterArray = string[]; + +/** + * **Document:** + * * Language Options: https://github.com/nextapps-de/flexsearch#language-options + * * Language: https://github.com/nextapps-de/flexsearch#languages + */ +export interface LanguageOptions { + stemmer: false | string | Stemmer | UnknownFunction; + filter: false | string | FilterArray | FilterFunction; + matcher: false | string | Matcher | UnknownFunction; +} + +/** + * These options will determine how the documents be indexed. + * + * **Generic type parameters:** + * + * @template T The type of the document. + * @template Store If store is enabled. + * + * **Document:** + * * Index options: https://github.com/nextapps-de/flexsearch#index-options + * * Language: https://github.com/nextapps-de/flexsearch#languages + */ +export interface IndexOptions { + preset?: Preset; + tokenize?: Tokenizer; + cache?: boolean | number; + resolution?: number; + context?: boolean | IndexOptions | ContextOptions; + optimize?: boolean; + boost?: (words: string[], term: string, index: number) => number; + + // Language-specific Options and Encoding + charset?: CharsetOptions | string; + language?: LanguageOptions | string; + encode?: Encoders; + stemmer?: LanguageOptions['stemmer']; + filter?: LanguageOptions['filter']; + matcher?: LanguageOptions['matcher']; +} + +/************************************/ +/* Index Search */ +/************************************/ + +export type IndexSearchResult = Id[]; + +/** + * **Document:** + * * Basic usage and variants: https://github.com/nextapps-de/flexsearch#basic-usage-and-variants + * * API overview: https://github.com/nextapps-de/flexsearch#api-overview + * * Usage: https://github.com/nextapps-de/flexsearch#usage + */ + +export class Index { + constructor(x?: Preset | IndexOptions); + add(id: Id, item: string): this; + append(id: Id, item: string): this; + update(id: Id, item: string): this; + remove(target: Id): this; + search(query: string, options?: Limit | SearchOptions): IndexSearchResult; + search( + query: string, + limit: number, + options: SearchOptions + ): IndexSearchResult; + search(options: SearchOptions): IndexSearchResult; + + // https://github.com/nextapps-de/flexsearch#check-existence-of-already-indexed-ids + contain(id: Id): boolean; + + export(handler: ExportHandler): Promise; + import(id: Id, item: string): Promise; + + // Async Methods + addAsync(id: Id, item: string, callback?: AsyncCallback): Promise; + appendAsync(id: Id, item: string, callback?: AsyncCallback): Promise; + updateAsync(id: Id, item: string, callback?: AsyncCallback): Promise; + removeAsync(target: Id, callback?: AsyncCallback): Promise; + searchAsync( + query: string, + options?: Limit | SearchOptions, + callback?: AsyncCallback + ): Promise; + searchAsync( + query: string, + limit: number, + options?: Limit | SearchOptions + ): IndexSearchResult; + searchAsync(options: SearchOptions): Promise; +} + +/** + * **Document:** + * * Basic usage and variants: https://github.com/nextapps-de/flexsearch#basic-usage-and-variants + * * API overview: https://github.com/nextapps-de/flexsearch#api-overview + * * Worker index: https://github.com/nextapps-de/flexsearch#worker-index + */ +export class Worker { + constructor(x?: Preset | IndexOptions); + + add(id: Id, item: string, callback?: AsyncCallback): Promise; + append(id: Id, item: string, callback?: AsyncCallback): Promise; + update(id: Id, item: string, callback?: AsyncCallback): Promise; + remove(target: Id, callback?: AsyncCallback): Promise; + search( + query: string, + options?: Limit | SearchOptions, + callback?: AsyncCallback + ): Promise; + search( + query: string, + limit: number, + options?: Limit | SearchOptions + ): IndexSearchResult; + search(options: SearchOptions): Promise; + + // Async Methods + addAsync(id: Id, item: string, callback?: AsyncCallback): Promise; + appendAsync(id: Id, item: string, callback?: AsyncCallback): Promise; + updateAsync(id: Id, item: string, callback?: AsyncCallback): Promise; + removeAsync(target: Id, callback?: AsyncCallback): Promise; + searchAsync( + query: string, + options?: Limit | SearchOptions, + callback?: AsyncCallback + ): Promise; + searchAsync( + query: string, + limit: number, + options?: Limit | SearchOptions + ): IndexSearchResult; + searchAsync(options: SearchOptions): Promise; +} + +/************************************/ +/* Document Search */ +/************************************/ + +/* +* **Document:** +* * Document options: https://github.com/nextapps-de/flexsearch#document-options +*/ +export interface DocumentOptions { + id: string; + tag?: false | string; + index: string | string[] | Array & { field: string }>; + store?: Store; +} + +/* +* **Document:** +* * Index options: https://github.com/nextapps-de/flexsearch#index-options +*/ +export interface IndexOptionsForDocumentSearch< + T, + Store extends StoreOption = false + > extends IndexOptions { + // Additional Options for Document Indexes + worker?: boolean; + document?: DocumentOptions | Descriptor; +} + +export interface SimpleDocumentSearchResultSetUnit { + field: string; + result: Id[]; +} + +export interface EnrichedDocumentSearchResultSetUnitResultUnit { + id: Id[]; + doc: T; +} + +export interface EnrichedDocumentSearchResultSetUnit { + field: string; + result: Array>; +} + +/** + * # Document Search Result + * + * To make your result return the full document: + * * set `store` to `true` while creating the document; + * * set `enrich` to `true` while searching. + * + * If neither of these conditions is met, then the returned result will be a `ISimpleDocumentSearchResult`. + */ +export type DocumentSearchResult< + T, + Store extends StoreOption = false, + Enrich extends boolean = false + > = [Store, Enrich] extends [EnrichStoreOption, true] + ? Array> + : SimpleDocumentSearchResultSetUnit[]; + +/** + * **Document:** + * * Document search options: https://github.com/nextapps-de/flexsearch#document-search-options + */ +export interface DocumentSearchOptions + extends SearchOptions { + index?: string | string[] | SearchOptions[]; + tag?: string | string[]; + enrich?: T; + bool?: "and" | "or"; +} + +/** + * **Document:** + * * Basic usage and variants: https://github.com/nextapps-de/flexsearch#basic-usage-and-variants + * * API overview: https://github.com/nextapps-de/flexsearch#api-overview + * * Document store: https://github.com/nextapps-de/flexsearch#document-store + */ +export class Document { + constructor( + options: IndexOptionsForDocumentSearch, + typeHack?: T + ); + add(document: T): this; + add(id: Id, document: T): this; + append(document: T): this; + append(id: Id, document: T): this; + update(document: T): this; + update(id: Id, document: T): this; + remove(target: Id | T): this; + search(query: string, limit?: number): SimpleDocumentSearchResultSetUnit[]; + + // https://github.com/nextapps-de/flexsearch#field-search + search(query: string, options: string[] | Partial>): SimpleDocumentSearchResultSetUnit[]; + + search( + query: string, + limit?: number, + options?: Partial> + ): DocumentSearchResult; + search( + options: Partial> + ): SimpleDocumentSearchResultSetUnit[]; + export(handler: ExportHandler): Promise; + import(id: Id, document: T): Promise; + + // Async Methods + addAsync(id: Id, document: T, callback?: AsyncCallback): Promise; + appendAsync(id: Id, document: T, callback?: AsyncCallback): Promise; + updateAsync(id: Id, document: T, callback?: AsyncCallback): Promise; + removeAsync(target: Id | T, callback?: AsyncCallback): Promise; + searchAsync( + query: string, + options: string[] | Partial> + ): Promise>; + searchAsync( + query: string, + limit?: number, + ): Promise; + searchAsync( + query: string, + limit: number, + callback: AsyncCallback + ): Promise; + searchAsync( + query: string, + options: Partial>, + callback: AsyncCallback> + ): Promise; + searchAsync( + options: Partial> + ): Promise>; + searchAsync( + options: Partial>, + callback: AsyncCallback> + ): Promise; +} + +/************************************/ +/* Miscellaneous */ +/************************************/ +export function create(options: IndexOptions): Index; +export function registerCharset(name: string, charset: CharsetOptions): void; +export function registerLanguage( + name: string, + language: LanguageOptions +): void; diff --git a/ohos/library/obfuscation-rules.txt b/ohos/library/obfuscation-rules.txt new file mode 100644 index 00000000..985b2aeb --- /dev/null +++ b/ohos/library/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/ohos/library/oh-package.json5 b/ohos/library/oh-package.json5 new file mode 100644 index 00000000..dfa66f1a --- /dev/null +++ b/ohos/library/oh-package.json5 @@ -0,0 +1,25 @@ +{ + "types": "./index.d.ts", + "keywords": [ + "fulltext search", + "elastic search", + "contextual search", + "document search", + "OpenHarmony" + ], + "author": "ohos_tpc", + "description": "Next-Generation full text search library with zero dependencies for ohos.", + "ohos": { + "org": "opensource" + }, + "main": "./src/flexsearch.js", + "repository": "https://gitcode.com/openharmony-tpc/flexsearch-ohos", + "version": "2.0.1", + "tags": [ + "Tool" + ], + "dependencies": {}, + "license": "Apache-2.0", + "devDependencies": {}, + "name": "@ohos/flexsearch" +} diff --git a/ohos/library/src/async.js b/ohos/library/src/async.js new file mode 100644 index 00000000..48eaba6b --- /dev/null +++ b/ohos/library/src/async.js @@ -0,0 +1,51 @@ +import { IndexInterface, DocumentInterface } from "./type.js"; +//import { promise as Promise } from "./polyfill.js"; +import {is_function} from "./common.js"; + +export default function(prototype){ + + register(prototype, "add"); + register(prototype, "append"); + register(prototype, "search"); + register(prototype, "update"); + register(prototype, "remove"); +} + +function register(prototype, key){ + + prototype[key + "Async"] = function(){ + + /** @type {IndexInterface|DocumentInterface} */ + const self = this; + const args = arguments; + const arg = args[args.length - 1]; + let callback; + + if(is_function(arg)){ + + callback = arg; + delete args[args.length - 1]; + } + + const promise = new Promise(function(resolve){ + + setTimeout(function(){ + + self.async = true; + const res = self[key].apply(self, args); + self.async = false; + resolve(res); + }, 0); + }); + + if(callback){ + + promise.then(callback); + return this; + } + else{ + + return promise; + } + }; +} diff --git a/ohos/library/src/cache.js b/ohos/library/src/cache.js new file mode 100644 index 00000000..4514947d --- /dev/null +++ b/ohos/library/src/cache.js @@ -0,0 +1,169 @@ +import { IndexInterface, DocumentInterface } from "./type.js"; +import { create_object, is_object } from "./common.js"; + +/** + * @param {boolean|number=} limit + * @constructor + */ + +function CacheClass(limit){ + + /** @private */ + this.limit = (limit !== true) && limit; + + /** @private */ + this.cache = create_object(); + + /** @private */ + this.queue = []; + + //this.clear(); +} + +export default CacheClass; + +/** + * @param {string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @this {IndexInterface} + * @returns {Array} + */ + +export function searchCache(query, limit, options){ + + if(is_object(query)){ + + query = query["query"]; + } + + let cache = this.cache.get(query); + + if(!cache){ + + cache = this.search(query, limit, options); + this.cache.set(query, cache); + } + + return cache; +} + +// CacheClass.prototype.clear = function(){ +// +// /** @private */ +// this.cache = create_object(); +// +// /** @private */ +// this.queue = []; +// }; + +CacheClass.prototype.set = function(key, value){ + + if(!this.cache[key]){ + + // it is just a shame that native function array.shift() performs so bad + + // const length = this.queue.length; + // + // this.queue[length] = key; + // + // if(length === this.limit){ + // + // delete this.cache[this.queue.shift()]; + // } + + // the same bad performance + + // this.queue.unshift(key); + // + // if(this.queue.length === this.limit){ + // + // this.queue.pop(); + // } + + // fast implementation variant + + // let length = this.queue.length; + // + // if(length === this.limit){ + // + // length--; + // + // delete this.cache[this.queue[0]]; + // + // for(let x = 0; x < length; x++){ + // + // this.queue[x] = this.queue[x + 1]; + // } + // } + // + // this.queue[length] = key; + + // current fastest implementation variant + // theoretically that should not perform better compared to the example above + + let length = this.queue.length; + + if(length === this.limit){ + + delete this.cache[this.queue[length - 1]]; + } + else{ + + length++; + } + + for(let x = length - 1; x > 0; x--){ + + this.queue[x] = this.queue[x - 1]; + } + + this.queue[0] = key; + } + + this.cache[key] = value; +}; + +CacheClass.prototype.get = function(key){ + + const cache = this.cache[key]; + + if(this.limit && cache){ + + // probably the indexOf() method performs faster when matched content is on front (left-to-right) + // using lastIndexOf() does not help, it performs almost slower + + const pos = this.queue.indexOf(key); + + // if(pos < this.queue.length - 1){ + // + // const tmp = this.queue[pos]; + // this.queue[pos] = this.queue[pos + 1]; + // this.queue[pos + 1] = tmp; + // } + + if(pos){ + + const tmp = this.queue[pos - 1]; + this.queue[pos - 1] = this.queue[pos]; + this.queue[pos] = tmp; + } + } + + return cache; +}; + +CacheClass.prototype.del = function(id){ + + for(let i = 0, item, key; i < this.queue.length; i++){ + + key = this.queue[i]; + item = this.cache[key]; + + if(item.indexOf(id) !== -1){ + + this.queue.splice(i--, 1); + delete this.cache[key]; + } + } +}; diff --git a/ohos/library/src/common.js b/ohos/library/src/common.js new file mode 100644 index 00000000..878eb1f0 --- /dev/null +++ b/ohos/library/src/common.js @@ -0,0 +1,78 @@ +export function parse_option(value, default_value){ + + return typeof value !== "undefined" ? value : default_value; +} + +/** + * @param {!number} count + * @returns {Array} + */ + +export function create_object_array(count){ + + const array = new Array(count); + + for(let i = 0; i < count; i++){ + + array[i] = create_object(); + } + + return array; +} + +export function create_arrays(count){ + + const array = new Array(count); + + for(let i = 0; i < count; i++){ + + array[i] = []; + } + + return array; +} + +/** + * @param {!Object} obj + * @returns {Array} + */ + +export function get_keys(obj){ + + return Object.keys(obj); +} + +export function create_object(){ + + return Object.create(null); +} + +export function concat(arrays){ + + return [].concat.apply([], arrays); +} + +export function sort_by_length_down(a, b){ + + return b.length - a.length; +} + +export function is_array(val){ + + return val.constructor === Array; +} + +export function is_string(val){ + + return typeof val === "string"; +} + +export function is_object(val){ + + return typeof val === "object"; +} + +export function is_function(val){ + + return typeof val === "function"; +} diff --git a/ohos/library/src/config.js b/ohos/library/src/config.js new file mode 100644 index 00000000..99b93e69 --- /dev/null +++ b/ohos/library/src/config.js @@ -0,0 +1,32 @@ +/** @define {boolean} */ +export const DEBUG = false; + +/** @define {boolean} */ +export const POLYFILL = true; + +/** @define {boolean} */ +export const SUPPORT_WORKER = true; + +/** @define {boolean|string} */ +export const SUPPORT_ENCODER = true; + +/** @define {boolean} */ +export const SUPPORT_CACHE = true; + +/** @define {boolean} */ +export const SUPPORT_ASYNC = true; + +/** @define {boolean} */ +export const SUPPORT_STORE = true; + +/** @define {boolean} */ +export const SUPPORT_TAGS = true; + +/** @define {boolean} */ +export const SUPPORT_SUGGESTION = true; + +/** @define {boolean} */ +export const SUPPORT_SERIALIZE = true; + +/** @define {boolean} */ +export const SUPPORT_DOCUMENT = true; diff --git a/ohos/library/src/document.js b/ohos/library/src/document.js new file mode 100644 index 00000000..02cde931 --- /dev/null +++ b/ohos/library/src/document.js @@ -0,0 +1,779 @@ +/**! + * FlexSearch.js + * Copyright 2018-2021 Nextapps GmbH + * Author: Thomas Wilkerling + * Licence: Apache-2.0 + * https://github.com/nextapps-de/flexsearch + */ + +import { + + SUPPORT_ASYNC, + SUPPORT_CACHE, + SUPPORT_SERIALIZE, + SUPPORT_STORE, + SUPPORT_TAGS, + SUPPORT_WORKER + +} from "./config.js"; + +import Index from "./index.js"; +import { DocumentInterface } from "./type.js"; +import Cache, { searchCache } from "./cache.js"; +import { create_object, is_array, is_string, is_object, parse_option, get_keys } from "./common.js"; +import apply_async from "./async.js"; +import { intersect, intersect_union } from "./intersect.js"; +import { exportDocument, importDocument } from "./serialize.js"; +import WorkerIndex from "./worker/index.js"; + +/** + * @constructor + * @implements DocumentInterface + * @param {Object=} options + * @return {Document} + */ + +function Document(options){ + + if(!(this instanceof Document)) { + + return new Document(options); + } + + const document = options["document"] || options["doc"] || options; + let opt; + + this.tree = []; + this.field = []; + this.marker = []; + this.register = create_object(); + this.key = ((opt = document["key"] || document["id"]) && parse_tree(opt, this.marker)) || "id"; + this.fastupdate = parse_option(options["fastupdate"], true); + + if(SUPPORT_STORE){ + + this.storetree = (opt = document["store"]) && (opt !== true) && []; + this.store = opt && create_object(); + } + + if(SUPPORT_TAGS){ + + this.tag = ((opt = document["tag"]) && parse_tree(opt, this.marker)); + this.tagindex = opt && create_object(); + } + + if(SUPPORT_CACHE){ + + this.cache = (opt = options["cache"]) && new Cache(opt); + + // do not apply cache again for the indexes + + options["cache"] = false; + } + + if(SUPPORT_WORKER){ + + this.worker = options["worker"]; + } + + if(SUPPORT_ASYNC){ + + // this switch is used by recall of promise callbacks + + this.async = false; + } + + /** @export */ + this.index = parse_descriptor.call(this, options, document); +} + +export default Document; + +/** + * @this Document + */ + +function parse_descriptor(options, document){ + + const index = create_object(); + let field = document["index"] || document["field"] || document; + + if(is_string(field)){ + + field = [field]; + } + + for(let i = 0, key, opt; i < field.length; i++){ + + key = field[i]; + + if(!is_string(key)){ + + opt = key; + key = key["field"]; + } + + opt = is_object(opt) ? Object.assign({}, options, opt) : options; + + if(SUPPORT_WORKER && this.worker){ + + index[key] = new WorkerIndex(opt); + + if(!index[key].worker){ + + this.worker = false; + } + } + + if(!this.worker){ + + index[key] = new Index(opt, this.register); + } + + this.tree[i] = parse_tree(key, this.marker); + this.field[i] = key; + } + + if(SUPPORT_STORE && this.storetree){ + + let store = document["store"]; + + if(is_string(store)){ + + store = [store]; + } + + for(let i = 0; i < store.length; i++){ + + this.storetree[i] = parse_tree(store[i], this.marker); + } + } + + return index; +} + +function parse_tree(key, marker){ + + const tree = key.split(":"); + let count = 0; + + for(let i = 0; i < tree.length; i++){ + + key = tree[i]; + + if(key.indexOf("[]") >= 0){ + + key = key.substring(0, key.length - 2); + + if(key){ + + marker[count] = true; + } + } + + if(key){ + + tree[count++] = key; + } + } + + if(count < tree.length){ + + tree.length = count; + } + + return count > 1 ? tree : tree[0]; +} + +function parse_simple(obj, tree){ + + if(is_string(tree)){ + + obj = obj[tree]; + } + else{ + + for(let i = 0; obj && (i < tree.length); i++){ + + obj = obj[tree[i]]; + } + } + + return obj; +} + +function store_value(obj, store, tree, pos, key){ + + obj = obj[key]; + + // reached target field + + if(pos === (tree.length - 1)){ + + // store target value + + store[key] = obj; + } + else if(obj){ + + if(is_array(obj)){ + + store = store[key] = new Array(obj.length); + + for(let i = 0; i < obj.length; i++){ + + // do not increase pos (an array is not a field) + store_value(obj, store, tree, pos, i); + } + } + else{ + + store = store[key] || (store[key] = create_object()); + key = tree[++pos]; + + store_value(obj, store, tree, pos, key); + } + } +} + +function add_index(obj, tree, marker, pos, index, id, key, _append){ + + obj = obj[key]; + + if(obj){ + + // reached target field + + if(pos === (tree.length - 1)){ + + // handle target value + + if(is_array(obj)){ + + // append array contents so each entry gets a new scoring context + + if(marker[pos]){ + + for(let i = 0; i < obj.length; i++){ + + index.add(id, obj[i], /* append: */ true, /* skip update: */ true); + } + + return; + } + + // or join array contents and use one scoring context + + obj = obj.join(" "); + } + + index.add(id, obj, _append, /* skip_update: */ true); + } + else{ + + if(is_array(obj)){ + + for(let i = 0; i < obj.length; i++){ + + // do not increase index, an array is not a field + + add_index(obj, tree, marker, pos, index, id, i, _append); + } + } + else{ + + key = tree[++pos]; + + add_index(obj, tree, marker, pos, index, id, key, _append); + } + } + } +} + +/** + * + * @param id + * @param content + * @param {boolean=} _append + * @returns {Document|Promise} + */ + +Document.prototype.add = function(id, content, _append){ + + if(is_object(id)){ + + content = id; + id = parse_simple(content, this.key); + } + + if(content && (id || (id === 0))){ + + if(!_append && this.register[id]){ + + return this.update(id, content); + } + + for(let i = 0, tree, field; i < this.field.length; i++){ + + field = this.field[i]; + tree = this.tree[i]; + + if(is_string(tree)){ + + tree = [tree]; + } + + add_index(content, tree, this.marker, 0, this.index[field], id, tree[0], _append); + } + + if(SUPPORT_TAGS && this.tag){ + + let tag = parse_simple(content, this.tag); + let dupes = create_object(); + + if(is_string(tag)){ + + tag = [tag]; + } + + for(let i = 0, key, arr; i < tag.length; i++){ + + key = tag[i]; + + if(!dupes[key]){ + + dupes[key] = 1; + arr = this.tagindex[key] || (this.tagindex[key] = []); + + if(!_append || (arr.indexOf(id) === -1)){ + + arr[arr.length] = id; + + // add a reference to the register for fast updates + + if(this.fastupdate){ + + const tmp = this.register[id] || (this.register[id] = []); + tmp[tmp.length] = arr; + } + } + } + } + } + + // TODO: how to handle store when appending contents? + + if(SUPPORT_STORE && this.store && (!_append || !this.store[id])){ + + let store; + + if(this.storetree){ + + store = create_object(); + + for(let i = 0, tree; i < this.storetree.length; i++){ + + tree = this.storetree[i]; + + if(is_string(tree)){ + + store[tree] = content[tree]; + } + else{ + + store_value(content, store, tree, 0, tree[0]); + } + } + } + + this.store[id] = store || content; + } + } + + return this; +}; + +Document.prototype.append = function(id, content){ + + return this.add(id, content, true); +}; + +Document.prototype.update = function(id, content){ + + return this.remove(id).add(id, content); +}; + +Document.prototype.remove = function(id){ + + if(is_object(id)){ + + id = parse_simple(id, this.key); + } + + if(this.register[id]){ + + for(let i = 0; i < this.field.length; i++){ + + // workers does not share the register + + this.index[this.field[i]].remove(id, !this.worker); + + if(this.fastupdate){ + + // when fastupdate was enabled all ids are removed + + break; + } + } + + if(SUPPORT_TAGS && this.tag){ + + // when fastupdate was enabled all ids are already removed + + if(!this.fastupdate){ + + for(let key in this.tagindex){ + + const tag = this.tagindex[key]; + const pos = tag.indexOf(id); + + if(pos !== -1){ + + if(tag.length > 1){ + + tag.splice(pos, 1); + } + else{ + + delete this.tagindex[key]; + } + } + } + } + } + + if(SUPPORT_STORE && this.store){ + + delete this.store[id]; + } + + delete this.register[id]; + } + + return this; +}; + +/** + * @param {!string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @param {Array=} _resolve For internal use only. + * @returns {Promise|Array} + */ + +Document.prototype.search = function(query, limit, options, _resolve){ + + if(!options){ + + if(!limit && is_object(query)){ + + options = /** @type {Object} */ (query); + query = options["query"]; + } + else if(is_object(limit)){ + + options = /** @type {Object} */ (limit); + limit = 0; + } + } + + let result = [], result_field = []; + let pluck, enrich; + let field, tag, bool, offset, count = 0; + + if(options){ + + if(is_array(options)){ + + field = options; + options = null; + } + else{ + + pluck = options["pluck"]; + field = pluck || options["index"] || options["field"] /*|| (is_string(options) && [options])*/; + tag = SUPPORT_TAGS && options["tag"]; + enrich = SUPPORT_STORE && this.store && options["enrich"]; + bool = options["bool"] === "and"; + limit = options["limit"] || 100; + offset = options["offset"] || 0; + + if(tag){ + + if(is_string(tag)){ + + tag = [tag]; + } + + // when tags is used and no query was set, + // then just return the tag indexes + + if(!query){ + + for(let i = 0, res; i < tag.length; i++){ + + res = get_tag.call(this, tag[i], limit, offset, enrich); + + if(res){ + + result[result.length] = res; + count++; + } + } + + return count ? result : []; + } + } + + if(is_string(field)){ + + field = [field]; + } + } + } + + field || (field = this.field); + bool = bool && ((field.length > 1) || (tag && (tag.length > 1))); + + const promises = !_resolve && (this.worker || this.async) && []; + + // TODO solve this in one loop below + + for(let i = 0, res, key, len; i < field.length; i++){ + + let opt; + + key = field[i]; + + if(!is_string(key)){ + + opt = key; + key = key["field"]; + } + + if(promises){ + + promises[i] = this.index[key].searchAsync(query, limit, opt || options); + + // just collect and continue + + // Code access control CodeCheck failed and error message was reported: disallow `continue` statements (no-continue) + // continue; + } + else if(_resolve){ + + res = _resolve[i]; + } + else{ + + // inherit options also when search? it is just for laziness, Object.assign() has a cost + + res = this.index[key].search(query, limit, opt || options); + } + + len = res && res.length; + + if(tag && len){ + + const arr = []; + let count = 0; + + if(bool){ + + // prepare for intersection + + arr[0] = [res]; + } + + for(let y = 0, key, res; y < tag.length; y++){ + + key = tag[y]; + res = this.tagindex[key]; + len = res && res.length; + + if(len){ + + count++; + arr[arr.length] = bool ? [res] : res; + } + } + + if(count){ + + if(bool){ + + res = intersect(arr, limit || 100, offset || 0); + } + else{ + + res = intersect_union(res, arr); + } + + len = res.length; + } + } + + if(len){ + + result_field[count] = key; + result[count++] = res; + } + else if(bool){ + + return []; + } + } + + if(promises){ + + const self = this; + + // anyone knows a better workaround of optionally having async promises? + // the promise.all() needs to be wrapped into additional promise, + // otherwise the recursive callback wouldn't run before return + + return new Promise(function(resolve){ + + Promise.all(promises).then(function(result){ + + resolve(self.search(query, limit, options, result)); + }); + }); + } + + if(!count){ + + // fast path "not found" + + return []; + } + + if(pluck && (!enrich || !this.store)){ + + // fast path optimization + + return result[0]; + } + + for(let i = 0, res; i < result_field.length; i++){ + + res = result[i]; + + if(res.length){ + + if(enrich){ + + res = apply_enrich.call(this, res); + } + } + + if(pluck){ + + return res; + } + + result[i] = { + + "field": result_field[i], + "result": res + }; + } + + return result; +}; + +/** + * @this Document + */ + +function get_tag(key, limit, offset, enrich){ + + let res = this.tagindex[key]; + let len = res && (res.length - offset); + + if(len && (len > 0)){ + + if((len > limit) || offset){ + + res = res.slice(offset, offset + limit); + } + + if(enrich){ + + res = apply_enrich.call(this, res); + } + + return { + + "tag": key, + "result": res + }; + } +} + +/** + * @this Document + */ + +function apply_enrich(res){ + + const arr = new Array(res.length); + + for(let x = 0, id; x < res.length; x++){ + + id = res[x]; + + arr[x] = { + + "id": id, + "doc": this.store[id] + }; + } + + return arr; +} + +Document.prototype.contain = function(id){ + + return !!this.register[id]; +}; + +if(SUPPORT_STORE){ + + Document.prototype.get = function(id){ + + return this.store[id]; + }; + + Document.prototype.set = function(id, data){ + + this.store[id] = data; + return this; + }; +} + +if(SUPPORT_CACHE){ + + Document.prototype.searchCache = searchCache; +} + +if(SUPPORT_SERIALIZE){ + + Document.prototype.export = exportDocument; + Document.prototype.import = importDocument; +} + +if(SUPPORT_ASYNC){ + + apply_async(Document.prototype); +} diff --git a/ohos/library/src/flexsearch.js b/ohos/library/src/flexsearch.js new file mode 100644 index 00000000..58c328d6 --- /dev/null +++ b/ohos/library/src/flexsearch.js @@ -0,0 +1,77 @@ +import { SUPPORT_ASYNC, SUPPORT_DOCUMENT, SUPPORT_CACHE, SUPPORT_SERIALIZE, SUPPORT_WORKER, SUPPORT_ENCODER } from "./config.js"; +import Document from "./document.js"; +import Index from "./index.js"; +import WorkerIndex from "./worker/index.js"; +import { registerCharset, registerLanguage } from "./global.js"; +import charset_default from "./lang/latin/default.js" +import charset_simple from "./lang/latin/simple.js" +import charset_balance from "./lang/latin/balance.js" +import charset_advanced from "./lang/latin/advanced.js" +import charset_extra from "./lang/latin/extra.js" +import charset_cjk from "./lang/cjk/default.js" + +/** @export */ Document.prototype.add; +/** @export */ Document.prototype.append; +/** @export */ Document.prototype.search; +/** @export */ Document.prototype.update; +/** @export */ Document.prototype.remove; +/** @export */ Document.prototype.contain; +/** @export */ Document.prototype.get; +/** @export */ Document.prototype.set; + +/** @export */ Index.prototype.add; +/** @export */ Index.prototype.append; +/** @export */ Index.prototype.search; +/** @export */ Index.prototype.update; +/** @export */ Index.prototype.remove; +/** @export */ Index.prototype.contain; + +if(SUPPORT_CACHE){ + +/** @export */ Index.prototype.searchCache; +/** @export */ Document.prototype.searchCache; +} + +if(SUPPORT_ASYNC){ + +/** @export */ Document.prototype.addAsync; +/** @export */ Document.prototype.appendAsync; +/** @export */ Document.prototype.searchAsync; +/** @export */ Document.prototype.updateAsync; +/** @export */ Document.prototype.removeAsync; + +/** @export */ Index.prototype.addAsync; +/** @export */ Index.prototype.appendAsync; +/** @export */ Index.prototype.searchAsync; +/** @export */ Index.prototype.updateAsync; +/** @export */ Index.prototype.removeAsync; +} + +if(SUPPORT_SERIALIZE){ + +/** @export */ Index.prototype.export; +/** @export */ Index.prototype.import; +/** @export */ Document.prototype.export; +/** @export */ Document.prototype.import; +} + +if(SUPPORT_ENCODER){ + + registerCharset("latin:default", charset_default); + registerCharset("latin:simple", charset_simple); + registerCharset("latin:balance", charset_balance); + registerCharset("latin:advanced", charset_advanced); + registerCharset("latin:extra", charset_extra); + registerCharset("cjk:default", charset_cjk); +} + +const FlexSearch = { + + "Index": Index, + "Document": SUPPORT_DOCUMENT ? Document : null, + "Worker": SUPPORT_WORKER ? WorkerIndex : null, + "registerCharset": registerCharset, + "registerLanguage": registerLanguage +}; + +export default FlexSearch; \ No newline at end of file diff --git a/ohos/library/src/global.js b/ohos/library/src/global.js new file mode 100644 index 00000000..96357044 --- /dev/null +++ b/ohos/library/src/global.js @@ -0,0 +1,22 @@ +export const global_lang = {}; +export const global_charset = {}; + +/** + * @param {!string} name + * @param {Object} charset + */ + +export function registerCharset(name, charset){ + + global_charset[name] = charset; +} + +/** + * @param {!string} name + * @param {Object} lang + */ + +export function registerLanguage(name, lang){ + + global_lang[name] = lang; +} diff --git a/ohos/library/src/index.js b/ohos/library/src/index.js new file mode 100644 index 00000000..d359cf21 --- /dev/null +++ b/ohos/library/src/index.js @@ -0,0 +1,817 @@ +/**! + * FlexSearch.js + * Copyright 2018-2021 Nextapps GmbH + * Author: Thomas Wilkerling + * Licence: Apache-2.0 + * https://github.com/nextapps-de/flexsearch + */ + +import { + + SUPPORT_ENCODER, + SUPPORT_CACHE, + SUPPORT_ASYNC, + SUPPORT_SUGGESTION, + SUPPORT_SERIALIZE + +} from "./config.js"; + +import { IndexInterface } from "./type.js"; +import { encode as default_encoder } from "./lang/latin/default.js"; +import { create_object, create_object_array, concat, sort_by_length_down, is_array, is_string, is_object, parse_option } from "./common.js"; +import { pipeline, init_stemmer_or_matcher, init_filter } from "./lang.js"; +import { global_lang, global_charset } from "./global.js"; +import apply_async from "./async.js"; +import { intersect } from "./intersect.js"; +import Cache, { searchCache } from "./cache.js"; +import apply_preset from "./preset.js"; +import { exportIndex, importIndex } from "./serialize.js"; + +/** + * @constructor + * @implements IndexInterface + * @param {Object=} options + * @param {Object=} _register + * @return {Index} + */ + +function Index(options, _register){ + + if(!(this instanceof Index)) { + + return new Index(options); + } + + let charset, lang, tmp; + + if(options){ + + if(SUPPORT_ENCODER){ + + options = apply_preset(options); + } + + charset = options["charset"]; + lang = options["lang"]; + + if(is_string(charset)){ + + if(charset.indexOf(":") === -1){ + + charset += ":default"; + } + + charset = global_charset[charset]; + } + + if(is_string(lang)){ + + lang = global_lang[lang]; + } + } + else{ + + options = {}; + } + + let resolution, optimize, context = options["context"] || {}; + + this.encode = options["encode"] || (charset && charset.encode) || default_encoder; + this.register = _register || create_object(); + this.resolution = resolution = options["resolution"] || 9; + this.tokenize = tmp = (charset && charset.tokenize) || options["tokenize"] || "strict"; + this.depth = (tmp === "strict") && context["depth"]; + this.bidirectional = parse_option(context["bidirectional"], true); + this.optimize = optimize = parse_option(options["optimize"], true); + this.fastupdate = parse_option(options["fastupdate"], true); + this.minlength = options["minlength"] || 1; + this.boost = options["boost"]; + + // when not using the memory strategy the score array should not pre-allocated to its full length + + this.map = optimize ? create_object_array(resolution) : create_object(); + this.resolution_ctx = resolution = context["resolution"] || 1; + this.ctx = optimize ? create_object_array(resolution) : create_object(); + this.rtl = (charset && charset.rtl) || options["rtl"]; + this.matcher = (tmp = options["matcher"] || (lang && lang.matcher)) && init_stemmer_or_matcher(tmp, false); + this.stemmer = (tmp = options["stemmer"] || (lang && lang.stemmer)) && init_stemmer_or_matcher(tmp, true); + this.filter = (tmp = options["filter"] || (lang && lang.filter)) && init_filter(tmp); + + if(SUPPORT_CACHE){ + + this.cache = (tmp = options["cache"]) && new Cache(tmp); + } +} + +export default Index; + +//Index.prototype.pipeline = pipeline; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +Index.prototype.append = function(id, content){ + + return this.add(id, content, true); +}; + +/** + * @param {!number|string} id + * @param {!string} content + * @param {boolean=} _append + * @param {boolean=} _skip_update + */ + +Index.prototype.add = function(id, content, _append, _skip_update){ + + if(content && (id || (id === 0))){ + + if(!_skip_update && !_append && this.register[id]){ + + return this.update(id, content); + } + + content = this.encode(content); + const length = content.length; + + if(length){ + + // check context dupes to skip all contextual redundancy along a document + + const dupes_ctx = create_object(); + const dupes = create_object(); + const depth = this.depth; + const resolution = this.resolution; + + for(let i = 0; i < length; i++){ + + let term = content[this.rtl ? length - 1 - i : i]; + let term_length = term.length; + + // skip dupes will break the context chain + + if(term && (term_length >= this.minlength) && (depth || !dupes[term])){ + + let score = get_score(resolution, length, i); + let token = ""; + + switch(this.tokenize){ + + case "full": + + if(term_length > 3){ + + for(let x = 0; x < term_length; x++){ + + for(let y = term_length; y > x; y--){ + + if((y - x) >= this.minlength){ + + const partial_score = get_score(resolution, length, i, term_length, x); + token = term.substring(x, y); + this.push_index(dupes, token, partial_score, id, _append); + } + } + } + + break; + } + + // fallthrough to next case when term length < 4 + + case "reverse": + + // skip last round (this token exist already in "forward") + + if(term_length > 2){ + + for(let x = term_length - 1; x > 0; x--){ + + token = term[x] + token; + + if(token.length >= this.minlength){ + + const partial_score = get_score(resolution, length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + } + } + + token = ""; + } + + // fallthrough to next case to apply forward also + + case "forward": + + if(term_length > 1){ + + for(let x = 0; x < term_length; x++){ + + token += term[x]; + + if(token.length >= this.minlength){ + + this.push_index(dupes, token, score, id, _append); + } + } + + break; + } + + // fallthrough to next case when token has a length of 1 + + default: + // case "strict": + + if(this.boost){ + + score = Math.min((score / this.boost(content, term, i)) | 0, resolution - 1); + } + + this.push_index(dupes, term, score, id, _append); + + // context is just supported by tokenizer "strict" + + if(depth){ + + if((length > 1) && (i < (length - 1))){ + + // check inner dupes to skip repeating words in the current context + + const dupes_inner = create_object(); + const resolution = this.resolution_ctx; + const keyword = term; + const size = Math.min(depth + 1, length - i); + + dupes_inner[keyword] = 1; + + for(let x = 1; x < size; x++){ + + term = content[this.rtl ? length - 1 - i - x : i + x]; + + if(term && (term.length >= this.minlength) && !dupes_inner[term]){ + + dupes_inner[term] = 1; + + const context_score = get_score(resolution + ((length / 2) > resolution ? 0 : 1), length, i, size - 1, x - 1); + const swap = this.bidirectional && (term > keyword); + this.push_index(dupes_ctx, swap ? keyword : term, context_score, id, _append, swap ? term : keyword); + } + } + } + } + } + } + } + + this.fastupdate || (this.register[id] = 1); + } + } + + return this; +}; + +/** + * @param {number} resolution + * @param {number} length + * @param {number} i + * @param {number=} term_length + * @param {number=} x + * @returns {number} + */ + +function get_score(resolution, length, i, term_length, x){ + + // console.log("resolution", resolution); + // console.log("length", length); + // console.log("term_length", term_length); + // console.log("i", i); + // console.log("x", x); + // console.log((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1); + + // the first resolution slot is reserved for the best match, + // when a query matches the first word(s). + + // also to stretch score to the whole range of resolution, the + // calculation is shift by one and cut the floating point. + // this needs the resolution "1" to be handled additionally. + + // do not stretch the resolution more than the term length will + // improve performance and memory, also it improves scoring in + // most cases between a short document and a long document + + return i && (resolution > 1) ? ( + + (length + (term_length || 0)) <= resolution ? + + i + (x || 0) + : + ((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1) | 0 + ): + 0; +} + +/** + * @private + * @param dupes + * @param value + * @param score + * @param id + * @param {boolean=} append + * @param {string=} keyword + */ + +Index.prototype.push_index = function(dupes, value, score, id, append, keyword){ + + let arr = keyword ? this.ctx : this.map; + + if(!dupes[value] || (keyword && !dupes[value][keyword])){ + + if(this.optimize){ + + arr = arr[score]; + } + + if(keyword){ + + dupes = dupes[value] || (dupes[value] = create_object()); + dupes[keyword] = 1; + + arr = arr[keyword] || (arr[keyword] = create_object()); + } + else{ + + dupes[value] = 1; + } + + arr = arr[value] || (arr[value] = []); + + if(!this.optimize){ + + arr = arr[score] || (arr[score] = []); + } + + if(!append || (arr.indexOf(id) === -1)){ + + arr[arr.length] = id; + + // add a reference to the register for fast updates + + if(this.fastupdate){ + + const tmp = this.register[id] || (this.register[id] = []); + tmp[tmp.length] = arr; + } + } + } +} + +/** + * @param {string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @returns {Array} + */ + +Index.prototype.search = function(query, limit, options){ + + if(!options){ + + if(!limit && is_object(query)){ + + options = /** @type {Object} */ (query); + query = options["query"]; + } + else if(is_object(limit)){ + + options = /** @type {Object} */ (limit); + } + } + + let result = []; + let length; + let context, suggest, offset = 0; + + if(options){ + + limit = options["limit"]; + offset = options["offset"] || 0; + context = options["context"]; + suggest = SUPPORT_SUGGESTION && options["suggest"]; + } + + if(query){ + + query = /** @type {Array} */ (this.encode(query)); + length = query.length; + + // TODO: solve this in one single loop below + + if(length > 1){ + + const dupes = create_object(); + const query_new = []; + + for(let i = 0, count = 0, term; i < length; i++){ + + term = query[i]; + + if(term && (term.length >= this.minlength) && !dupes[term]){ + + // this fast path just could applied when not in memory-optimized mode + + if(!this.optimize && !suggest && !this.map[term]){ + + // fast path "not found" + + return result; + } + else{ + + query_new[count++] = term; + dupes[term] = 1; + } + } + } + + query = query_new; + length = query.length; + } + } + + if(!length){ + + return result; + } + + limit || (limit = 100); + + let depth = this.depth && (length > 1) && (context !== false); + let index = 0, keyword; + + if(depth){ + + keyword = query[0]; + index = 1; + } + else{ + + if(length > 1){ + + query.sort(sort_by_length_down); + } + } + + for(let arr, term; index < length; index++){ + + term = query[index]; + + // console.log(keyword); + // console.log(term); + // console.log(""); + + if(depth){ + + arr = this.add_result(result, suggest, limit, offset, length === 2, term, keyword); + + // console.log(arr); + // console.log(result); + + // when suggestion enabled just forward keyword if term was found + // as long as the result is empty forward the pointer also + + if(!suggest || (arr !== false) || !result.length){ + + keyword = term; + } + } + else{ + + arr = this.add_result(result, suggest, limit, offset, length === 1, term); + } + + if(arr){ + + return /** @type {Array} */ (arr); + } + + // apply suggestions on last loop or fallback + + if(suggest && (index === length - 1)){ + + let length = result.length; + + if(!length){ + + if(depth){ + + // fallback to non-contextual search when no result was found + + depth = 0; + index = -1; + + // Code access control CodeCheck failed and error message was reported: disallow `continue` statements (no-continue) + // continue; + } + + return result; + } + else if(length === 1){ + + // fast path optimization + + return single_result(result[0], limit, offset); + } + } + } + + return intersect(result, limit, offset, suggest); +}; + +/** + * Returns an array when the result is done (to stop the process immediately), + * returns false when suggestions is enabled and no result was found, + * or returns nothing when a set was pushed successfully to the results + * + * @private + * @param {Array} result + * @param {Array} suggest + * @param {number} limit + * @param {number} offset + * @param {boolean} single_term + * @param {string} term + * @param {string=} keyword + * @return {Array>|boolean|undefined} + */ + +Index.prototype.add_result = function(result, suggest, limit, offset, single_term, term, keyword){ + + let word_arr = []; + let arr = keyword ? this.ctx : this.map; + + if(!this.optimize){ + + arr = get_array(arr, term, keyword, this.bidirectional); + } + + if(arr){ + + let count = 0; + const arr_len = Math.min(arr.length, keyword ? this.resolution_ctx : this.resolution); + + // relevance: + for(let x = 0, size = 0, tmp, len; x < arr_len; x++){ + + tmp = arr[x]; + + if(tmp){ + + if(this.optimize){ + + tmp = get_array(tmp, term, keyword, this.bidirectional); + } + + if(offset){ + + if(tmp && single_term){ + + len = tmp.length; + + if(len <= offset){ + + offset -= len; + tmp = null; + } + else{ + + tmp = tmp.slice(offset); + offset = 0; + } + } + } + + if(tmp){ + + // keep score (sparse array): + //word_arr[x] = tmp; + + // simplified score order: + word_arr[count++] = tmp; + + if(single_term){ + + size += tmp.length; + + if(size >= limit){ + + // fast path optimization + + break; + } + } + } + } + } + + if(count){ + + if(single_term){ + + // fast path optimization + // offset was already applied at this point + + return single_result(word_arr, limit, 0); + } + + result[result.length] = word_arr; + return; + } + } + + // return an empty array will stop the loop, + // to prevent stop when using suggestions return a false value + + return !suggest && word_arr; +}; + +function single_result(result, limit, offset){ + + if(result.length === 1){ + + result = result[0]; + } + else{ + + result = concat(result); + } + + return offset || (result.length > limit) ? + + result.slice(offset, offset + limit) + : + result; +} + +function get_array(arr, term, keyword, bidirectional){ + + if(keyword){ + + // the frequency of the starting letter is slightly less + // on the last half of the alphabet (m-z) in almost every latin language, + // so we sort downwards (https://en.wikipedia.org/wiki/Letter_frequency) + + const swap = bidirectional && (term > keyword); + + arr = arr[swap ? term : keyword]; + arr = arr && arr[swap ? keyword : term]; + } + else{ + + arr = arr[term]; + } + + return arr; +} + +Index.prototype.contain = function(id){ + + return !!this.register[id]; +}; + +Index.prototype.update = function(id, content){ + + return this.remove(id).add(id, content); +}; + +/** + * @param {boolean=} _skip_deletion + */ + +Index.prototype.remove = function(id, _skip_deletion){ + + const refs = this.register[id]; + + if(refs){ + + if(this.fastupdate){ + + // fast updates performs really fast but did not fully cleanup the key entries + + for(let i = 0, tmp; i < refs.length; i++){ + + tmp = refs[i]; + tmp.splice(tmp.indexOf(id), 1); + } + } + else{ + + remove_index(this.map, id, this.resolution, this.optimize); + + if(this.depth){ + + remove_index(this.ctx, id, this.resolution_ctx, this.optimize); + } + } + + _skip_deletion || delete this.register[id]; + + if(SUPPORT_CACHE && this.cache){ + + this.cache.del(id); + } + } + + return this; +}; + +/** + * @param map + * @param id + * @param res + * @param optimize + * @param {number=} resolution + * @return {number} + */ + +function remove_index(map, id, res, optimize, resolution){ + + let count = 0; + + if(is_array(map)){ + + // the first array is the score array in both strategies + + if(!resolution){ + + resolution = Math.min(map.length, res); + + for(let x = 0, arr; x < resolution; x++){ + + arr = map[x]; + + if(arr){ + + count = remove_index(arr, id, res, optimize, resolution); + + if(!optimize && !count){ + + // when not memory optimized the score index should removed + + delete map[x]; + } + } + } + } + else{ + + const pos = map.indexOf(id); + + if(pos !== -1){ + + // fast path, when length is 1 or lower then the whole field gets deleted + + if(map.length > 1){ + + map.splice(pos, 1); + count++; + } + } + else{ + + count++; + } + } + } + else{ + + for(let key in map){ + + count = remove_index(map[key], id, res, optimize, resolution); + + if(!count){ + + delete map[key]; + } + } + } + + return count; +} + +if(SUPPORT_CACHE){ + + Index.prototype.searchCache = searchCache; +} + +if(SUPPORT_SERIALIZE){ + + Index.prototype.export = exportIndex; + Index.prototype.import = importIndex; +} + +if(SUPPORT_ASYNC){ + + apply_async(Index.prototype); +} diff --git a/ohos/library/src/intersect.js b/ohos/library/src/intersect.js new file mode 100644 index 00000000..1336cba5 --- /dev/null +++ b/ohos/library/src/intersect.js @@ -0,0 +1,396 @@ +import { create_object, concat } from "./common.js"; + +/** + * Implementation based on Array.indexOf() provides better performance, + * but it needs at least one word in the query which is less frequent. + * Also on large indexes it does not scale well performance-wise. + * This strategy also lacks of suggestion capabilities (matching & sorting). + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @returns {Array} + */ + +// export function intersect(arrays, limit, offset, suggest) { +// +// const length = arrays.length; +// let result = []; +// let check; +// +// // determine shortest array and collect results +// // from the sparse relevance arrays +// +// let smallest_size; +// let smallest_arr; +// let smallest_index; +// +// for(let x = 0; x < length; x++){ +// +// const arr = arrays[x]; +// const len = arr.length; +// +// let size = 0; +// +// for(let y = 0, tmp; y < len; y++){ +// +// tmp = arr[y]; +// +// if(tmp){ +// +// size += tmp.length; +// } +// } +// +// if(!smallest_size || (size < smallest_size)){ +// +// smallest_size = size; +// smallest_arr = arr; +// smallest_index = x; +// } +// } +// +// smallest_arr = smallest_arr.length === 1 ? +// +// smallest_arr[0] +// : +// concat(smallest_arr); +// +// if(suggest){ +// +// suggest = [smallest_arr]; +// check = create_object(); +// } +// +// let size = 0; +// let steps = 0; +// +// // process terms in reversed order often results in better performance. +// // the outer loop must be the words array, using the +// // smallest array here disables the "fast fail" optimization. +// +// for(let x = length - 1; x >= 0; x--){ +// +// if(x !== smallest_index){ +// +// steps++; +// +// const word_arr = arrays[x]; +// const word_arr_len = word_arr.length; +// const new_arr = []; +// +// let count = 0; +// +// for(let z = 0, id; z < smallest_arr.length; z++){ +// +// id = smallest_arr[z]; +// +// let found; +// +// // process relevance in forward order (direction is +// // important for adding IDs during the last round) +// +// for(let y = 0; y < word_arr_len; y++){ +// +// const arr = word_arr[y]; +// +// if(arr.length){ +// +// found = arr.indexOf(id) !== -1; +// +// if(found){ +// +// // check if in last round +// +// if(steps === length - 1){ +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// +// if(suggest){ +// +// check[id] = 1; +// } +// } +// +// break; +// } +// } +// } +// +// if(found){ +// +// new_arr[count++] = id; +// } +// } +// +// if(suggest){ +// +// suggest[steps] = new_arr; +// } +// else if(!count){ +// +// return []; +// } +// +// smallest_arr = new_arr; +// } +// } +// +// if(suggest){ +// +// // needs to iterate in reverse direction +// +// for(let x = suggest.length - 1, arr, len; x >= 0; x--){ +// +// arr = suggest[x]; +// len = arr && arr.length; +// +// if(len){ +// +// for(let y = 0, id; y < len; y++){ +// +// id = arr[y]; +// +// if(!check[id]){ +// +// check[id] = 1; +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// } +// } +// } +// } +// } +// +// return result; +// } + +/** + * Implementation based on Object[key] provides better suggestions + * capabilities and has less performance scaling issues on large indexes. + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @returns {Array} + */ + +export function intersect(arrays, limit, offset, suggest) { + + const length = arrays.length; + let result = []; + let check; + let check_suggest; + let size = 0; + + if(suggest){ + + suggest = []; + } + + // process terms in reversed order often has advantage for the fast path "end reached". + // also a reversed order prioritize the order of words from a query. + + for(let x = length - 1; x >= 0; x--){ + + const word_arr = arrays[x]; + const word_arr_len = word_arr.length; + const check_new = create_object(); + + let found = !check; + + // process relevance in forward order (direction is + // important for adding IDs during the last round) + + for(let y = 0; y < word_arr_len; y++){ + + const arr = word_arr[y]; + const arr_len = arr.length; + + if(arr_len){ + + // loop through IDs + + for(let z = 0, check_idx, id; z < arr_len; z++){ + + id = arr[z]; + + if(check){ + + if(check[id]){ + + // check if in last round + + if(!x){ + + if(offset){ + + offset--; + } + else{ + + result[size++] = id; + + if(size === limit){ + + // fast path "end reached" + + return result; + } + } + } + + if(x || suggest){ + + check_new[id] = 1; + } + + found = true; + } + + if(suggest){ + + check_suggest[id] = (check_idx = check_suggest[id]) ? ++check_idx : check_idx = 1; + + // do not adding IDs which are already included in the result (saves one loop) + // the first intersection match has the check index 2, so shift by -2 + + if(check_idx < length){ + + const tmp = suggest[check_idx - 2] || (suggest[check_idx - 2] = []); + tmp[tmp.length] = id; + } + } + } + else{ + + // pre-fill in first round + + check_new[id] = 1; + } + } + } + } + + if(suggest){ + + // re-use the first pre-filled check for suggestions + + check || (check_suggest = check_new); + } + else if(!found){ + + return []; + } + + check = check_new; + } + + if(suggest){ + + // needs to iterate in reverse direction + + for(let x = suggest.length - 1, arr, len; x >= 0; x--){ + + arr = suggest[x]; + len = arr.length; + + for(let y = 0, id; y < len; y++){ + + id = arr[y]; + + if(!check[id]){ + + if(offset){ + + offset--; + } + else{ + + result[size++] = id; + + if(size === limit){ + + // fast path "end reached" + + return result; + } + } + + check[id] = 1; + } + } + } + } + + return result; +} + +/** + * @param mandatory + * @param arrays + * @returns {Array} + */ + +export function intersect_union(mandatory, arrays) { + + const check = create_object(); + const union = create_object(); + const result = []; + + for(let x = 0; x < mandatory.length; x++){ + + check[mandatory[x]] = 1; + } + + for(let x = 0, arr; x < arrays.length; x++){ + + arr = arrays[x]; + + for(let y = 0, id; y < arr.length; y++){ + + id = arr[y]; + + if(check[id]){ + + if(!union[id]){ + + union[id] = 1; + result[result.length] = id; + } + } + } + } + + return result; +} \ No newline at end of file diff --git a/ohos/library/src/lang.js b/ohos/library/src/lang.js new file mode 100644 index 00000000..70d7fdaf --- /dev/null +++ b/ohos/library/src/lang.js @@ -0,0 +1,317 @@ +import { IndexInterface } from "./type.js"; +import { create_object, get_keys } from "./common.js"; + +/** + * @param {!string} str + * @param {boolean|Array=} normalize + * @param {boolean|string|RegExp=} split + * @param {boolean=} _collapse + * @returns {string|Array} + * @this IndexInterface + */ + +export function pipeline(str, normalize, split, _collapse){ + + if(str){ + + if(normalize){ + + str = replace(str, /** @type {Array} */ (normalize)); + } + + if(this.matcher){ + + str = replace(str, this.matcher); + } + + if(this.stemmer && (str.length > 1)){ + + str = replace(str, this.stemmer); + } + + if(_collapse && (str.length > 1)){ + + str = collapse(str); + } + + if(split || (split === "")){ + + const words = str.split(/** @type {string|RegExp} */ (split)); + + return this.filter ? filter(words, this.filter) : words; + } + } + + return str; +} + +export const regex_whitespace = /[CPSZp\{\}]+/; +const regex_normalize = /[\u0300-\u036f]/g; + +export function normalize(str){ + + if(str.normalize){ + + str = str.normalize("NFD").replace(regex_normalize, ""); + } + + return str; +} + +/** + * @param {!string} str + * @param {boolean|Array=} normalize + * @param {boolean|string|RegExp=} split + * @param {boolean=} _collapse + * @returns {string|Array} + */ + +// FlexSearch.prototype.pipeline = function(str, normalize, split, _collapse){ +// +// if(str){ +// +// if(normalize && str){ +// +// str = replace(str, /** @type {Array} */ (normalize)); +// } +// +// if(str && this.matcher){ +// +// str = replace(str, this.matcher); +// } +// +// if(this.stemmer && str.length > 1){ +// +// str = replace(str, this.stemmer); +// } +// +// if(_collapse && str.length > 1){ +// +// str = collapse(str); +// } +// +// if(str){ +// +// if(split || (split === "")){ +// +// const words = str.split(/** @type {string|RegExp} */ (split)); +// +// return this.filter ? filter(words, this.filter) : words; +// } +// } +// } +// +// return str; +// }; + +// export function pipeline(str, normalize, matcher, stemmer, split, _filter, _collapse){ +// +// if(str){ +// +// if(normalize && str){ +// +// str = replace(str, normalize); +// } +// +// if(matcher && str){ +// +// str = replace(str, matcher); +// } +// +// if(stemmer && str.length > 1){ +// +// str = replace(str, stemmer); +// } +// +// if(_collapse && str.length > 1){ +// +// str = collapse(str); +// } +// +// if(str){ +// +// if(split !== false){ +// +// str = str.split(split); +// +// if(_filter){ +// +// str = filter(str, _filter); +// } +// } +// } +// } +// +// return str; +// } + + +/** + * @param {Array} words + * @returns {Object} + */ + +export function init_filter(words){ + + const filter = create_object(); + + for(let i = 0, length = words.length; i < length; i++){ + + filter[words[i]] = 1; + } + + return filter; +} + +/** + * @param {!Object} obj + * @param {boolean} is_stemmer + * @returns {Array} + */ + +export function init_stemmer_or_matcher(obj, is_stemmer){ + + const keys = get_keys(obj); + const length = keys.length; + const final = []; + + let removal = "", count = 0; + + for(let i = 0, key, tmp; i < length; i++){ + + key = keys[i]; + tmp = obj[key]; + + if(tmp){ + + final[count++] = regex(is_stemmer ? "(?!\\b)" + key + "(\\b|_)" : key); + final[count++] = tmp; + } + else{ + + removal += (removal ? "|" : "") + key; + } + } + + if(removal){ + + final[count++] = regex(is_stemmer ? "(?!\\b)(" + removal + ")(\\b|_)" : "(" + removal + ")"); + final[count] = ""; + } + + return final; +} + + +/** + * @param {!string} str + * @param {Array} regexp + * @returns {string} + */ + +export function replace(str, regexp){ + + for(let i = 0, len = regexp.length; i < len; i += 2){ + + str = str.replace(regexp[i], regexp[i + 1]); + + if(!str){ + + break; + } + } + + return str; +} + +/** + * @param {!string} str + * @returns {RegExp} + */ + +export function regex(str){ + + return new RegExp(str, "g"); +} + +/** + * Regex: replace(/(?:(\w)(?:\1)*)/g, "$1") + * @param {!string} string + * @returns {string} + */ + +export function collapse(string){ + + let final = "", prev = ""; + + for(let i = 0, len = string.length, char; i < len; i++){ + + if((char = string[i]) !== prev){ + + final += (prev = char); + } + } + + return final; +} + +// TODO using fast-swap +export function filter(words, map){ + + const length = words.length; + const filtered = []; + + for(let i = 0, count = 0; i < length; i++){ + + const word = words[i]; + + if(word && !map[word]){ + + filtered[count++] = word; + } + } + + return filtered; +} + +// const chars = {a:1, e:1, i:1, o:1, u:1, y:1}; +// +// function collapse_repeating_chars(string){ +// +// let collapsed_string = "", +// char_prev = "", +// char_next = ""; +// +// for(let i = 0; i < string.length; i++){ +// +// const char = string[i]; +// +// if(char !== char_prev){ +// +// if(i && (char === "h")){ +// +// if((chars[char_prev] && chars[char_next]) || (char_prev === " ")){ +// +// collapsed_string += char; +// } +// } +// else{ +// +// collapsed_string += char; +// } +// } +// +// char_next = ( +// +// (i === (string.length - 1)) ? +// +// "" +// : +// string[i + 1] +// ); +// +// char_prev = char; +// } +// +// return collapsed_string; +// } diff --git a/ohos/library/src/lang/arabic/default.js b/ohos/library/src/lang/arabic/default.js new file mode 100644 index 00000000..1a501ed9 --- /dev/null +++ b/ohos/library/src/lang/arabic/default.js @@ -0,0 +1,28 @@ +import { IndexInterface } from "../../type.js"; +import { pipeline } from "../../lang.js"; + +export const rtl = true; +export const tokenize = ""; +export default { + encode: encode, + rtl: rtl +} + +const regex = /[\x00-\x7F]+/g; +const split = /\s+/; + +/** + * @this IndexInterface + */ + +export function encode(str){ + + return pipeline.call( + + this, + /* string: */ str.replace(regex, " "), + /* normalize: */ false, + /* split: */ split, + /* collapse: */ false + ); +} diff --git a/ohos/library/src/lang/at.js b/ohos/library/src/lang/at.js new file mode 100644 index 00000000..0fecf05d --- /dev/null +++ b/ohos/library/src/lang/at.js @@ -0,0 +1,172 @@ +/** + * http://www.ranks.nl/stopwords + * @type {Array} + */ + +export const filter = [ + + "aber", + "als", + "am", + "an", + "auch", + "auf", + "aus", + "bei", + "bin", + "bis", + "bist", + "da", + "dadurch", + "daher", + "darum", + "das", + "daß", + "dass", + "dein", + "deine", + "dem", + "den", + "der", + "des", + "dessen", + "deshalb", + "die", + "dies", + "dieser", + "dieses", + "doch", + "dort", + "du", + "durch", + "ein", + "eine", + "einem", + "einen", + "einer", + "eines", + "er", + "es", + "euer", + "eure", + "für", + "hatte", + "hatten", + "hattest", + "hattet", + "hier", + "hinter", + "ich", + "ihr", + "ihre", + "im", + "in", + "ist", + "ja", + "jede", + "jedem", + "jeden", + "jeder", + "jedes", + "jener", + "jenes", + "jetzt", + "kann", + "kannst", + "können", + "könnt", + "machen", + "mein", + "meine", + "mit", + "muß", + "mußt", + "musst", + "müssen", + "müßt", + "nach", + "nachdem", + "nein", + "nicht", + "nun", + "oder", + "seid", + "sein", + "seine", + "sich", + "sie", + "sind", + "soll", + "sollen", + "sollst", + "sollt", + "sonst", + "soweit", + "sowie", + "und", + "unser", + "unsere", + "unter", + "vom", + "von", + "vor", + "wann", + "warum", + "was", + "weiter", + "weitere", + "wenn", + "wer", + "werde", + "werden", + "werdet", + "weshalb", + "wie", + "wieder", + "wieso", + "wir", + "wird", + "wirst", + "wo", + "woher", + "wohin", + "zu", + "zum", + "zur", + "über" +]; + +/** + * @type {Object} + */ + +export const stemmer = { + + "niss": "", + "isch": "", + "lich": "", + "heit": "", + "keit": "", + "end": "", + "ung": "", + "est": "", + "ern": "", + "em": "", + "er": "", + "en": "", + "es": "", + "st": "", + "ig": "", + "ik": "", + "e": "", + "s": "" +}; + +export const matcher = {}; + +export default { + + filter: filter, + stemmer: stemmer, + matcher: matcher +} diff --git a/ohos/library/src/lang/cjk/default.js b/ohos/library/src/lang/cjk/default.js new file mode 100644 index 00000000..5c54d891 --- /dev/null +++ b/ohos/library/src/lang/cjk/default.js @@ -0,0 +1,28 @@ +import { IndexInterface } from "../../type.js"; +import { pipeline } from "../../lang.js"; + +export const rtl = false; +export const tokenize = "strict"; +export default { + encode: encode, + rtl: rtl, + tokenize: tokenize +} + +const regex = /[\x00-\x7F]+/g; + +/** + * @this IndexInterface + */ + +export function encode(str){ + + return pipeline.call( + + this, + /* string: */ str.replace(regex, ""), + /* normalize: */ false, + /* split: */ "", + /* collapse: */ false + ); +} diff --git a/ohos/library/src/lang/cyrillic/default.js b/ohos/library/src/lang/cyrillic/default.js new file mode 100644 index 00000000..7612c9d1 --- /dev/null +++ b/ohos/library/src/lang/cyrillic/default.js @@ -0,0 +1,28 @@ +import { IndexInterface } from "../../type.js"; +import { pipeline } from "../../lang.js"; + +export const rtl = false; +export const tokenize = ""; +export default { + encode: encode, + rtl: rtl +} + +const regex = /[\x00-\x7F]+/g; +const split = /\s+/; + +/** + * @this IndexInterface + */ + +export function encode(str){ + + return pipeline.call( + + this, + /* string: */ str.replace(regex, " "), + /* normalize: */ false, + /* split: */ split, + /* collapse: */ false + ); +} diff --git a/ohos/library/src/lang/de.js b/ohos/library/src/lang/de.js new file mode 100644 index 00000000..029a6bdf --- /dev/null +++ b/ohos/library/src/lang/de.js @@ -0,0 +1,185 @@ +/** + * Filter are also known as "stopwords", they completely filter out words from being indexed. + * Source: http://www.ranks.nl/stopwords + * Object Definition: Just provide an array of words. + * @type {Array} + */ + +export const filter = [ + + "aber", + "als", + "am", + "an", + "auch", + "auf", + "aus", + "bei", + "bin", + "bis", + "bist", + "da", + "dadurch", + "daher", + "darum", + "das", + "daß", + "dass", + "dein", + "deine", + "dem", + "den", + "der", + "des", + "dessen", + "deshalb", + "die", + "dies", + "dieser", + "dieses", + "doch", + "dort", + "du", + "durch", + "ein", + "eine", + "einem", + "einen", + "einer", + "eines", + "er", + "es", + "euer", + "eure", + "für", + "hatte", + "hatten", + "hattest", + "hattet", + "hier", + "hinter", + "ich", + "ihr", + "ihre", + "im", + "in", + "ist", + "ja", + "jede", + "jedem", + "jeden", + "jeder", + "jedes", + "jener", + "jenes", + "jetzt", + "kann", + "kannst", + "können", + "könnt", + "machen", + "mein", + "meine", + "mit", + "muß", + "mußt", + "musst", + "müssen", + "müßt", + "nach", + "nachdem", + "nein", + "nicht", + "nun", + "oder", + "seid", + "sein", + "seine", + "sich", + "sie", + "sind", + "soll", + "sollen", + "sollst", + "sollt", + "sonst", + "soweit", + "sowie", + "und", + "unser", + "unsere", + "unter", + "vom", + "von", + "vor", + "wann", + "warum", + "was", + "weiter", + "weitere", + "wenn", + "wer", + "werde", + "werden", + "werdet", + "weshalb", + "wie", + "wieder", + "wieso", + "wir", + "wird", + "wirst", + "wo", + "woher", + "wohin", + "zu", + "zum", + "zur", + "über" +]; + +/** + * Stemmer removes word endings and is a kind of "partial normalization". A word ending just matched when the word length is bigger than the matched partial. + * Example: The word "correct" and "correctness" could be the same word, so you can define {"ness": ""} to normalize the ending. + * Object Definition: the key represents the word ending, the value contains the replacement (or empty string for removal). + * @type {Object} + */ + +export const stemmer = { + + "niss": "", + "isch": "", + "lich": "", + "heit": "", + "keit": "", + "ell": "", + "bar": "", + "end": "", + "ung": "", + "est": "", + "ern": "", + "em": "", + "er": "", + "en": "", + "es": "", + "st": "", + "ig": "", + "ik": "", + "e": "", + "s": "" +}; + +/** + * Matcher replaces all occurrences of a given string regardless of its position and is also a kind of "partial normalization". + * Object Definition: the key represents the target term, the value contains the search string which should be replaced (could also be an array of multiple terms). + * @type {Object|string>} + */ + +export const matcher = {}; + +export default { + + filter: filter, + stemmer: stemmer, + matcher: matcher +} \ No newline at end of file diff --git a/ohos/library/src/lang/en.js b/ohos/library/src/lang/en.js new file mode 100644 index 00000000..c7e9870d --- /dev/null +++ b/ohos/library/src/lang/en.js @@ -0,0 +1,276 @@ +/** + * http://www.ranks.nl/stopwords + * @type {Array} + */ + +export const filter = [ + + "a", + "about", + "above", + "after", + "again", + "against", + "all", + "also", + "am", + "an", + "and", + "any", + "are", + "aren't", + "as", + "at", + //"back", + "be", + "because", + "been", + "before", + "being", + "below", + //"between", + "both", + "but", + "by", + "can", + "cannot", + "can't", + "come", + "could", + "couldn't", + //"day", + "did", + "didn't", + "do", + "does", + "doesn't", + "doing", + "dont", + "down", + "during", + "each", + "even", + "few", + "first", + "for", + "from", + "further", + "get", + //"give", + "go", + //"good", + "had", + "hadn't", + "has", + "hasn't", + "have", + "haven't", + "having", + "he", + "hed", + //"hell", + "her", + "here", + "here's", + "hers", + "herself", + "hes", + "him", + "himself", + "his", + "how", + "how's", + "i", + "id", + "if", + "ill", + "im", + "in", + "into", + "is", + "isn't", + "it", + "it's", + "itself", + "i've", + "just", + "know", + "let's", + "like", + //"look", + "make", + "me", + "more", + "most", + "mustn't", + "my", + "myself", + "new", + "no", + "nor", + "not", + "now", + "of", + "off", + "on", + "once", + //"one", + "only", + "or", + "other", + "ought", + "our", + "our's", + "ourselves", + "out", + "over", + "own", + //"people", + "same", + "say", + "see", + "shan't", + "she", + "she'd", + "shell", + "shes", + "should", + "shouldn't", + "so", + "some", + "such", + //"take", + "than", + "that", + "that's", + "the", + "their", + "theirs", + "them", + "themselves", + "then", + "there", + "there's", + "these", + "they", + "they'd", + "they'll", + "they're", + "they've", + //"think", + "this", + "those", + "through", + "time", + "to", + "too", + //"two", + //"under", + "until", + "up", + "us", + //"use", + "very", + "want", + "was", + "wasn't", + "way", + "we", + "wed", + "well", + "were", + "weren't", + "we've", + "what", + "what's", + "when", + "when's", + "where", + "where's", + "which", + "while", + "who", + "whom", + "who's", + "why", + "why's", + "will", + "with", + "won't", + //"work", + "would", + "wouldn't", + //"year", + "you", + "you'd", + "you'll", + "your", + "you're", + "your's", + "yourself", + "yourselves", + "you've" +]; + +/** + * @type {Object} + */ + +export const stemmer = { + + "ational": "ate", + "iveness": "ive", + "fulness": "ful", + "ousness": "ous", + "ization": "ize", + "tional": "tion", + "biliti": "ble", + "icate": "ic", + "ative": "", + "alize": "al", + "iciti": "ic", + "entli": "ent", + "ousli": "ous", + "alism": "al", + "ation": "ate", + "aliti": "al", + "iviti": "ive", + "ement": "", + "enci": "ence", + "anci": "ance", + "izer": "ize", + "alli": "al", + "ator": "ate", + "logi": "log", + "ical": "ic", + "ance": "", + "ence": "", + "ness": "", + "able": "", + "ible": "", + "ment": "", + "eli": "e", + "bli": "ble", + "ful": "", + "ant": "", + "ent": "", + "ism": "", + "ate": "", + "iti": "", + "ous": "", + "ive": "", + "ize": "", + "al": "", + "ou": "", + "er": "", + "ic": "" +}; + +export const matcher = {}; + +export default { + + filter: filter, + stemmer: stemmer, + matcher: matcher +} diff --git a/ohos/library/src/lang/latin/advanced.js b/ohos/library/src/lang/latin/advanced.js new file mode 100644 index 00000000..fcab3e8d --- /dev/null +++ b/ohos/library/src/lang/latin/advanced.js @@ -0,0 +1,90 @@ +import { IndexInterface } from "../../type.js"; +import { regex, replace, collapse } from "../../lang.js"; +import { encode as encode_balance } from "./balance.js"; + +export const rtl = false; +export const tokenize = ""; +export default { + encode: encode, + rtl: rtl, + tokenize: tokenize +} + +// Phonetic Normalization + +const regex_ae = regex("ae"), + //regex_ai = regex("ai"), + //regex_ay = regex("ay"), + //regex_ey = regex("ey"), + regex_oe = regex("oe"), + //regex_ue = regex("ue"), + //regex_ie = regex("ie"), + //regex_sz = regex("sz"), + //regex_zs = regex("zs"), + //regex_ck = regex("ck"), + //regex_cc = regex("cc"), + regex_sh = regex("sh"), + regex_th = regex("th"), + //regex_dt = regex("dt"), + regex_ph = regex("ph"), + regex_pf = regex("pf"); + //regex_ou = regex("ou"), + //regex_uo = regex("uo"); + +const pairs = [ + regex_ae, "a", + // regex_ai, "ei", + // regex_ay, "ei", + // regex_ey, "ei", + regex_oe, "o", + // regex_ue, "u", + // regex_ie, "i", + // regex_sz, "s", + // regex_zs, "s", + regex_sh, "s", + // regex_ck, "k", + // regex_cc, "k", + regex_th, "t", + // regex_dt, "t", + regex_ph, "f", + regex_pf, "f", + // regex_ou, "o", + // regex_uo, "u" + + // regex("(?![aeiouy])h(?![aeiouy])"), "", + // regex("(?!^[aeiouy])h(?!^[aeiouy])"), "" + regex("(?![aeo])h(?![aeo])"), "", + regex("(?!^[aeo])h(?!^[aeo])"), "" +]; + +/** + * @this IndexInterface + */ + +export function encode(str, _skip_postprocessing){ + + if(str){ + + str = encode_balance.call(this, str).join(" "); + + if(str.length > 2){ + + str = replace(str, pairs); + } + + if(!_skip_postprocessing){ + + if(str.length > 1){ + + str = collapse(str); + } + + if(str){ + + str = str.split(" "); + } + } + } + + return str; +} diff --git a/ohos/library/src/lang/latin/balance.js b/ohos/library/src/lang/latin/balance.js new file mode 100644 index 00000000..48483b3d --- /dev/null +++ b/ohos/library/src/lang/latin/balance.js @@ -0,0 +1,118 @@ +import { IndexInterface } from "../../type.js"; +import { encode as encode_simple } from "./simple.js"; + +// custom soundex implementation + +export const rtl = false; +export const tokenize = "strict"; +export default { + encode: encode, + rtl: rtl, + tokenize: tokenize +} + +//const regex_whitespace = /[\W_]+/g; +const regex_strip = /[^a-z0-9]+/; + +// const pairs = [ +// regex_whitespace, " ", +// regex_strip, "" +// ]; + +// modified + +const soundex = { + + "b": "p", + //"p": "p", + + //"f": "f", + "v": "f", + "w": "f", + + //"s": "s", + "z": "s", + "x": "s", + "ß": "s", + + "d": "t", + //"t": "t", + + //"l": "l", + + //"m": "m", + "n": "m", + + "c": "k", + "g": "k", + "j": "k", + //"k": "k", + "q": "k", + + //"r": "r", + //"h": "h", + //"a": "a", + + //"e": "e", + "i": "e", + "y": "e", + + //"o": "o", + "u": "o" +}; + +/** + * @this IndexInterface + */ + +export function encode(str){ + + str = encode_simple.call(this, str).join(" "); + + // str = this.pipeline( + // + // /* string: */ normalize("" + str).toLowerCase(), + // /* normalize: */ false, + // /* split: */ false, + // /* collapse: */ false + // ); + + const result = []; + + if(str){ + + const words = str.split(regex_strip); + const length = words.length; + + for(let x = 0, tmp, count = 0; x < length; x++){ + + if((str = words[x]) /*&& (str.length > 2)*/ && (!this.filter || !this.filter[str])){ + + tmp = str[0]; + let code = soundex[tmp] || tmp; //str[0]; + let previous = code; //soundex[code] || code; + + for(let i = 1; i < str.length; i++){ + + tmp = str[i]; + const current = soundex[tmp] || tmp; + + if(current && (current !== previous)){ + + code += current; + previous = current; + + // if(code.length === 7){ + // + // break; + // } + } + } + + result[count++] = code; //(code + "0000").substring(0, 4); + } + } + } + + return result; +} diff --git a/ohos/library/src/lang/latin/default.js b/ohos/library/src/lang/latin/default.js new file mode 100644 index 00000000..8c5ce893 --- /dev/null +++ b/ohos/library/src/lang/latin/default.js @@ -0,0 +1,26 @@ +import { IndexInterface } from "../../type.js"; +import { pipeline, normalize, regex_whitespace } from "../../lang.js"; + +export const rtl = false; +export const tokenize = ""; +export default { + encode: encode, + rtl: rtl, + tokenize: tokenize +} + +/** + * @this IndexInterface + */ + +export function encode(str){ + + return pipeline.call( + + this, + /* string: */ ("" + str).toLowerCase(), + /* normalize: */ false, + /* split: */ regex_whitespace, + /* collapse: */ false + ); +} diff --git a/ohos/library/src/lang/latin/extra.js b/ohos/library/src/lang/latin/extra.js new file mode 100644 index 00000000..2428b97b --- /dev/null +++ b/ohos/library/src/lang/latin/extra.js @@ -0,0 +1,66 @@ +import { IndexInterface } from "../../type.js"; +import { regex, replace, collapse } from "../../lang.js"; +import { encode as encode_advanced } from "./advanced.js"; + +export const rtl = false; +export const tokenize = ""; +export default { + encode: encode, + rtl: rtl, + tokenize: tokenize +} + +// Soundex Normalization + +const prefix = "(?!\\b)"; +const //soundex_b = regex(prefix + "p"), + // soundex_s = regex(prefix + "z"), + // soundex_k = regex(prefix + "[cgq]"), + // soundex_m = regex(prefix + "n"), + // soundex_t = regex(prefix + "d"), + // soundex_f = regex(prefix + "[vw]"), + //regex_vowel = regex(prefix + "[aeiouy]"); + regex_vowel = regex(prefix + "[aeo]"); + +const pairs = [ + + // soundex_b, "b", + // soundex_s, "s", + // soundex_k, "k", + // soundex_m, "m", + // soundex_t, "t", + // soundex_f, "f", + // regex("(?![aeiouy])h(?![aeiouy])"), "", + // regex("(?!^[aeiouy])h(?!^[aeiouy])"), "", + regex_vowel, "" +]; + +/** + * @this IndexInterface + */ + +export function encode(str){ + + if(str){ + + str = encode_advanced.call(this, str, /* skip post-processing: */ true); + + if(str.length > 1){ + + //str = replace(str, pairs); + str = str.replace(regex_vowel, ""); + } + + if(str.length > 1){ + + str = collapse(str); + } + + if(str){ + + str = str.split(" "); + } + } + + return str; +} diff --git a/ohos/library/src/lang/latin/simple.js b/ohos/library/src/lang/latin/simple.js new file mode 100644 index 00000000..38de37a5 --- /dev/null +++ b/ohos/library/src/lang/latin/simple.js @@ -0,0 +1,60 @@ +import { IndexInterface } from "../../type.js"; +import { pipeline, normalize, regex_whitespace, regex } from "../../lang.js"; + +export const rtl = false; +export const tokenize = ""; +export default { + encode: encode, + rtl: rtl, + tokenize: tokenize +} + +// Charset Normalization + +const //regex_whitespace = /\W+/, + //regex_strip = regex("[^a-z0-9 ]"), + regex_a = regex("[àáâãäå]"), + regex_e = regex("[èéêë]"), + regex_i = regex("[ìíîï]"), + regex_o = regex("[òóôõöő]"), + regex_u = regex("[ùúûüű]"), + regex_y = regex("[ýŷÿ]"), + regex_n = regex("ñ"), + regex_c = regex("[çc]"), + regex_s = regex("ß"), + regex_and = regex(" & "); + +const pairs = [ + + regex_a, "a", + regex_e, "e", + regex_i, "i", + regex_o, "o", + regex_u, "u", + regex_y, "y", + regex_n, "n", + regex_c, "k", + regex_s, "s", + regex_and, " and " + //regex_whitespace, " " + //regex_strip, "" +]; + +/** + * @param {string} str + * @this IndexInterface + */ + +export function encode(str){ + + str = "" + str; + + return pipeline.call( + + this, + /* string: */ normalize(str).toLowerCase(), + /* normalize: */ !str.normalize && pairs, + /* split: */ regex_whitespace, + /* collapse: */ false + ); +} diff --git a/ohos/library/src/lang/us.js b/ohos/library/src/lang/us.js new file mode 100644 index 00000000..c7e9870d --- /dev/null +++ b/ohos/library/src/lang/us.js @@ -0,0 +1,276 @@ +/** + * http://www.ranks.nl/stopwords + * @type {Array} + */ + +export const filter = [ + + "a", + "about", + "above", + "after", + "again", + "against", + "all", + "also", + "am", + "an", + "and", + "any", + "are", + "aren't", + "as", + "at", + //"back", + "be", + "because", + "been", + "before", + "being", + "below", + //"between", + "both", + "but", + "by", + "can", + "cannot", + "can't", + "come", + "could", + "couldn't", + //"day", + "did", + "didn't", + "do", + "does", + "doesn't", + "doing", + "dont", + "down", + "during", + "each", + "even", + "few", + "first", + "for", + "from", + "further", + "get", + //"give", + "go", + //"good", + "had", + "hadn't", + "has", + "hasn't", + "have", + "haven't", + "having", + "he", + "hed", + //"hell", + "her", + "here", + "here's", + "hers", + "herself", + "hes", + "him", + "himself", + "his", + "how", + "how's", + "i", + "id", + "if", + "ill", + "im", + "in", + "into", + "is", + "isn't", + "it", + "it's", + "itself", + "i've", + "just", + "know", + "let's", + "like", + //"look", + "make", + "me", + "more", + "most", + "mustn't", + "my", + "myself", + "new", + "no", + "nor", + "not", + "now", + "of", + "off", + "on", + "once", + //"one", + "only", + "or", + "other", + "ought", + "our", + "our's", + "ourselves", + "out", + "over", + "own", + //"people", + "same", + "say", + "see", + "shan't", + "she", + "she'd", + "shell", + "shes", + "should", + "shouldn't", + "so", + "some", + "such", + //"take", + "than", + "that", + "that's", + "the", + "their", + "theirs", + "them", + "themselves", + "then", + "there", + "there's", + "these", + "they", + "they'd", + "they'll", + "they're", + "they've", + //"think", + "this", + "those", + "through", + "time", + "to", + "too", + //"two", + //"under", + "until", + "up", + "us", + //"use", + "very", + "want", + "was", + "wasn't", + "way", + "we", + "wed", + "well", + "were", + "weren't", + "we've", + "what", + "what's", + "when", + "when's", + "where", + "where's", + "which", + "while", + "who", + "whom", + "who's", + "why", + "why's", + "will", + "with", + "won't", + //"work", + "would", + "wouldn't", + //"year", + "you", + "you'd", + "you'll", + "your", + "you're", + "your's", + "yourself", + "yourselves", + "you've" +]; + +/** + * @type {Object} + */ + +export const stemmer = { + + "ational": "ate", + "iveness": "ive", + "fulness": "ful", + "ousness": "ous", + "ization": "ize", + "tional": "tion", + "biliti": "ble", + "icate": "ic", + "ative": "", + "alize": "al", + "iciti": "ic", + "entli": "ent", + "ousli": "ous", + "alism": "al", + "ation": "ate", + "aliti": "al", + "iviti": "ive", + "ement": "", + "enci": "ence", + "anci": "ance", + "izer": "ize", + "alli": "al", + "ator": "ate", + "logi": "log", + "ical": "ic", + "ance": "", + "ence": "", + "ness": "", + "able": "", + "ible": "", + "ment": "", + "eli": "e", + "bli": "ble", + "ful": "", + "ant": "", + "ent": "", + "ism": "", + "ate": "", + "iti": "", + "ous": "", + "ive": "", + "ize": "", + "al": "", + "ou": "", + "er": "", + "ic": "" +}; + +export const matcher = {}; + +export default { + + filter: filter, + stemmer: stemmer, + matcher: matcher +} diff --git a/ohos/library/src/main/module.json5 b/ohos/library/src/main/module.json5 new file mode 100644 index 00000000..0204bcbe --- /dev/null +++ b/ohos/library/src/main/module.json5 @@ -0,0 +1,10 @@ +{ + "module": { + "name": "library", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ] + } +} \ No newline at end of file diff --git a/ohos/library/src/preset.js b/ohos/library/src/preset.js new file mode 100644 index 00000000..e2a6038c --- /dev/null +++ b/ohos/library/src/preset.js @@ -0,0 +1,90 @@ +import { DEBUG } from "./config.js"; +import { is_string } from "./common.js"; + +/** + * @enum {Object} + * @const + */ + +const preset = { + + "memory": { + charset: "latin:extra", + //tokenize: "strict", + resolution: 3, + //threshold: 0, + minlength: 4, + fastupdate: false + }, + + "performance": { + //charset: "latin", + //tokenize: "strict", + resolution: 3, + minlength: 3, + //fastupdate: true, + optimize: false, + //fastupdate: true, + context: { + depth: 2, + resolution: 1 + //bidirectional: false + } + }, + + "match": { + charset: "latin:extra", + tokenize: "reverse", + //resolution: 9, + //threshold: 0 + }, + + "score": { + charset: "latin:advanced", + //tokenize: "strict", + resolution: 20, + minlength: 3, + context: { + depth: 3, + resolution: 9, + //bidirectional: true + } + }, + + "default": { + // charset: "latin:default", + // tokenize: "strict", + // resolution: 3, + // threshold: 0, + // depth: 3 + }, + + // "fast": { + // //charset: "latin", + // //tokenize: "strict", + // threshold: 8, + // resolution: 9, + // depth: 1 + // } +}; + +export default function apply_preset(options){ + + if(is_string(options)){ + + + options = preset[options]; + } + else{ + + const preset = options["preset"]; + + if(preset){ + + + options = Object.assign({}, preset[preset], /** @type {Object} */ (options)); + } + } + + return options; +} \ No newline at end of file diff --git a/ohos/library/src/serialize.js b/ohos/library/src/serialize.js new file mode 100644 index 00000000..17eae4ac --- /dev/null +++ b/ohos/library/src/serialize.js @@ -0,0 +1,251 @@ +import { IndexInterface, DocumentInterface } from "./type.js"; +import { create_object, is_string } from "./common.js"; + +function async(callback, self, key, index_doc, index, data){ + + setTimeout(function(){ + + const res = callback(key, JSON.stringify(data)); + + // await isn't supported by ES5 + + if(res && res["then"]){ + + res["then"](function(){ + + self.export(callback, self, key, index_doc, index + 1); + }) + } + else{ + + self.export(callback, self, key, index_doc, index + 1); + } + }, 0); +} + +/** + * @this IndexInterface + */ + +export function exportIndex(callback, self, field, index_doc, index){ + + let key, data; + + switch(index || (index = 0)){ + + case 0: + + key = "reg"; + + // fastupdate isn't supported by export + + if(this.fastupdate){ + + data = create_object(); + + for(let key in this.register){ + + data[key] = 1; + } + } + else{ + + data = this.register; + } + + break; + + case 1: + + key = "cfg"; + data = { + "doc": 0, + "opt": this.optimize ? 1 : 0 + }; + + break; + + case 2: + + key = "map"; + data = this.map; + break; + + case 3: + + key = "ctx"; + data = this.ctx; + break; + + default: + + return; + } + + async(callback, self || this, field ? field + "." + key : key, index_doc, index, data); + + return true; +} + +/** + * @this IndexInterface + */ + +export function importIndex(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + let keys = key.split("."); + key = keys[keys.length -1]; + + switch(key){ + + case "cfg": + + this.optimize = !!data["opt"]; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.register = data; + break; + + case "map": + + this.map = data; + break; + + case "ctx": + + this.ctx = data; + break; + } +} + +/** + * @this DocumentInterface + */ + +export function exportDocument(callback, self, field, index_doc, index){ + + index || (index = 0); + index_doc || (index_doc = 0); + + if(index_doc < this.field.length){ + + const field = this.field[index_doc]; + const idx = this.index[field]; + + self = this; + + setTimeout(function(){ + + if(!idx.export(callback, self, index ? field.replace(":", "-") : "", index_doc, index++)){ + + index_doc++; + index = 1; + + self.export(callback, self, field, index_doc, index); + } + }, 0); + } + else{ + + let key, data; + + switch(index){ + + case 1: + + key = "tag"; + data = this.tagindex; + break; + + case 2: + + key = "store"; + data = this.store; + break; + + // case 3: + // + // key = "reg"; + // data = this.register; + // break; + + default: + + return; + } + + async(callback, this, key, index_doc, index, data); + } +} + +/** + * @this DocumentInterface + */ + +export function importDocument(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "tag": + + this.tagindex = data; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.register = data; + + for(let i = 0, index; i < this.field.length; i++){ + + index = this.index[this.field[i]]; + index.register = data; + index.fastupdate = false; + } + + break; + + case "store": + + this.store = data; + break; + + default: + + key = key.split("."); + const field = key[0]; + key = key[1]; + + if(field && key){ + + this.index[field].import(key, data); + } + } +} diff --git a/ohos/library/src/type.js b/ohos/library/src/type.js new file mode 100644 index 00000000..f6acd1bc --- /dev/null +++ b/ohos/library/src/type.js @@ -0,0 +1,69 @@ +/** + * @interface + */ + +export function IndexInterface(){ + + this.cache = null; + this.matcher = null; + this.stemmer = null; + this.filter = null; +} + +/** + * @param {!string} str + * @param {boolean|Array=} normalize + * @param {boolean|string|RegExp=} split + * @param {boolean=} collapse + * @returns {string|Array} + */ + +//IndexInterface.prototype.pipeline; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +IndexInterface.prototype.add; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +IndexInterface.prototype.append; + +/** + * @param {!string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @returns {Array} + */ + +IndexInterface.prototype.search; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +IndexInterface.prototype.update; + +/** + * @param {!number|string} id + */ + +IndexInterface.prototype.remove; + +/** + * @interface + */ + +export function DocumentInterface(){ + + this.field = null; + + /** @type IndexInterface */ + this.index = null; +} diff --git a/ohos/library/src/worker/ark.js b/ohos/library/src/worker/ark.js new file mode 100644 index 00000000..2ec272e3 --- /dev/null +++ b/ohos/library/src/worker/ark.js @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import arkWorker from "@ohos.worker"; +import Index from "../index.js"; + +let index; +let encode; + +export function setEncode(_encode) { + encode = _encode; +} + +export const parentPort = arkWorker.parentPort; + +export function handler(msg) { + + msg = msg["data"]; + + /** @type Index */ + const args = msg["args"]; + const task = msg["task"]; + const id = msg["id"]; + + if (task === "init") { + const options = msg["options"] || {}; + options["cache"] = false; + if (options["encode"] === encode.name) { + options["encode"] = encode; + } + index = new Index(options); + } else { + const message = index[task].apply(index, args); + parentPort.postMessage(task === "search" ? { "id": id, "msg": message } : { "id": id }); + } +} + + diff --git a/ohos/library/src/worker/index.js b/ohos/library/src/worker/index.js new file mode 100644 index 00000000..d52b5e73 --- /dev/null +++ b/ohos/library/src/worker/index.js @@ -0,0 +1,99 @@ +//import { promise as Promise } from "../polyfill.js"; +import {create_object, is_function, is_string} from "../common.js"; +import arkWorker from "@ohos.worker"; + +let pid = 0; + +/** + * @param {Object=} options + * @constructor + */ + +function WorkerIndex(options) { + + if (!(this instanceof WorkerIndex)) { + + return new WorkerIndex(options); + } + + if (!options) { + options = {}; + } + + const _self = this; + + this.worker = create(options["worker"]); + this.resolver = create_object(); + + if (!this.worker) { + + return; + } + + this.worker.onmessage = function (e) { + let msg = e["data"]; + _self.resolver[msg["id"]](msg["msg"]); + delete _self.resolver[msg["id"]]; + }; + + let msg = { + "task": "init", + "options": options + }; + + // postMessage方法接收的参数中不能带有function类型数据,否则报错 + this.worker.postMessage(msg); +} + +export default WorkerIndex; + +register("add"); +register("append"); +register("search"); +register("update"); +register("remove"); + +function register(key) { + + WorkerIndex.prototype[key] = + WorkerIndex.prototype[key + "Async"] = function () { + + const self = this; + const args = [].slice.call(arguments); + const arg = args[args.length - 1]; + let callback; + + if (is_function(arg)) { + callback = arg; + args.splice(args.length - 1, 1); + } + + const promise = new Promise(function (resolve) { + + setTimeout(function () { + self.resolver[++pid] = resolve; + const msg = { + "task": key, + "id": pid, + "args": args + }; + self.worker.postMessage(msg); + }, 0); + }); + + if (callback) { + + promise.then(callback); + return this; + } + else { + + return promise; + } + }; +} + +function create(workerPath) { + + return new arkWorker.Worker(is_string(workerPath) ? workerPath : "entry/ets/workers/worker.js"); +} \ No newline at end of file diff --git a/ohos/library/task/fileCopy.js b/ohos/library/task/fileCopy.js new file mode 100644 index 00000000..787b4196 --- /dev/null +++ b/ohos/library/task/fileCopy.js @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fs = require('fs'); +const path = require('path'); + +const folderName = './'; + +mkDirsSync(folderName + '/dist/module/worker'); +fs.copyFile(folderName + '/src/worker/ark.js', folderName + '/dist/module/worker/ark.js', function (err) { + +}); +fs.copyFile(folderName + '/src/worker/index.js', folderName + '/dist/module/worker/index.js', function (err) { + +}); + +function mkDirsSync(dir) { + if (fs.existsSync(dir)) { + return true; + } else if (mkDirsSync(path.dirname(dir))) { + fs.mkdirSync(dir); + return true; + } else { + return false; + } +} diff --git a/ohos/oh-package.json5 b/ohos/oh-package.json5 new file mode 100644 index 00000000..c242d39c --- /dev/null +++ b/ohos/oh-package.json5 @@ -0,0 +1,15 @@ +{ + "license": "ISC", + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "name": "flexsearch-ohos", + "ohos": { + "org": "huawei", + "directoryLevel": "project", + "buildTool": "hvigor" + }, + "description": "example description", + "version": "2.0.1", + "dependencies": {} +}