@dalencatt/strapi-plugin-fuzzy-search-private 0.0.0-development

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 @DomDew
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,579 @@
1
+ <img src="assets/logo.png" alt="fuzzy search logo" width="200"/>
2
+
3
+ # Strapi-plugin-fuzzy-search <!-- omit from toc -->
4
+
5
+ <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
6
+ [![All Contributors](https://img.shields.io/badge/all_contributors-17-orange.svg?style=flat-square)](#contributors-)
7
+ <!-- ALL-CONTRIBUTORS-BADGE:END -->
8
+
9
+ ![Github CI](https://img.shields.io/github/actions/workflow/status/domdew/strapi-plugin-fuzzy-search/release.yml) ![Npm release](https://img.shields.io/npm/v/strapi-plugin-fuzzy-search?label=release) ![Npm monthly downloads](https://img.shields.io/npm/dm/strapi-plugin-fuzzy-search) ![License](https://img.shields.io/github/license/domdew/strapi-plugin-fuzzy-search) ![Coverage](https://img.shields.io/badge/Coverage-80.45%25-brightgreen.svg)
10
+
11
+ Register a weighted fuzzy search endpoint for Strapi Headless CMS you can add your content types to in no time.
12
+
13
+ Uses [fuzzysort](https://github.com/farzher/fuzzysort) under the hood: Simple, quick and easy. No need to worry about setting up an instance for a complex search engine.
14
+
15
+ ## Table of Contents <!-- omit from toc -->
16
+
17
+ - [Roadmap 🏗️](#roadmap-️)
18
+ - [Requirements](#requirements)
19
+ - [Installation](#installation)
20
+ - [Options/Config](#optionsconfig)
21
+ - [General Options](#general-options)
22
+ - [Fuzzysort Options](#fuzzysort-options)
23
+ - [Full Example config](#full-example-config)
24
+ - [A note on performance:](#a-note-on-performance)
25
+ - [Usage](#usage)
26
+ - [Basic Search](#basic-search)
27
+ - [Example Requests](#example-requests)
28
+ - [REST](#rest)
29
+ - [GraphQl](#graphql)
30
+ - [Example Responses](#example-responses)
31
+ - [REST](#rest-1)
32
+ - [GraphQl](#graphql-1)
33
+ - [Pagination](#pagination)
34
+ - [REST](#rest-2)
35
+ - [GraphQL](#graphql-2)
36
+ - [Population](#population)
37
+ - [REST](#rest-3)
38
+ - [GraphQL](#graphql-3)
39
+ - [Filters](#filters)
40
+ - [REST](#rest-4)
41
+ - [GraphQL](#graphql-4)
42
+ - [Filter by Content Type (REST)](#filter-by-content-type-rest)
43
+ - [Why use fuzzysort and not something like Fuse.js?](#why-use-fuzzysort-and-not-something-like-fusejs)
44
+ - [Contributing](#contributing)
45
+ - [Found a bug?](#found-a-bug)
46
+ - [Contributors ✨](#contributors-)
47
+
48
+ # Roadmap 🏗️
49
+
50
+ - Return indices/highlights of matches
51
+ - Improve response performance
52
+ - Include option to hide unpublished content by default
53
+ - Allow single types as content types
54
+ - Pass configuration as query params/args
55
+ - Configure fuzzysort through params/args per content type
56
+
57
+ # Requirements
58
+
59
+ Strapi Version `>= v5.1.1`
60
+
61
+ For compatibility with older Strapi versions please use older Versions of this package (<= 1.11.2 and <= 2.0.3)
62
+
63
+ # Installation
64
+
65
+ Enable the fuzzy-search plugin in the `./config/plugins.js` of your Strapi project.
66
+
67
+ Make sure to set the appropriate permissions for the `search` route in the `Permissions` tab of the `Users & Permission Plugin` for the role to be able to access the search route.
68
+
69
+ ## Options/Config
70
+
71
+ Mandatory settings are marked with `*`.
72
+
73
+ ### General Options
74
+
75
+ The plugin requires several configurations to be set in the `.config/plugins.js` file of your Strapi project to work.
76
+
77
+ | Key | Type | Notes                                               |
78
+ | -------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
79
+ | contentTypes\* | Array of Objects | List the content types you want to register for fuzzysort. Each object requires the `uid: string` and `modelName: string` to be set for a content type |
80
+ | transliterate | boolean | If this is set to true the search will additionally run against transliterated versions of the content for the keys specified in the keys array for a given content type. E.g. `你好` will match for `ni hao`. Note that activating this feature for a content type comes at a performance cost and may increase the response time. |
81
+
82
+ **IMPORTANT:** Please note that as of now, only collectionTypes are supported as contentTypes.
83
+
84
+ ### Fuzzysort Options
85
+
86
+ The `fuzzysortOptions` allow for some finetuning of fuzzysorts searching algorithm to your needs.
87
+
88
+ | Key | Type | Notes |
89
+ | -------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
90
+ | characterLimit | int (positive) | Limits the length of characters the algorithm is searching through for any string of the content type |
91
+ | threshold | float (positive) | Sets the threshold for the score of the entries that will be returned. The lower, the "fuzzier" the results. |
92
+ | limit | int (positive) | Limits the amount of entries returned from the search |
93
+ | keys\* | array of objects | Lists the fields of the models the algorithm should search `(name: string)` and a factor to weight them by `weight: float`. The higher the weight, the higher a match for a given field will be evaluated for a content type. |
94
+
95
+ ### Full Example config
96
+
97
+ ```JavaScript
98
+ module.exports = ({ env }) => ({
99
+ // ...
100
+
101
+ "fuzzy-search": {
102
+ enabled: true,
103
+ config: {
104
+ contentTypes: [
105
+ {
106
+ uid: "api::author.author",
107
+ modelName: "author",
108
+ transliterate: true,
109
+ fuzzysortOptions: {
110
+ characterLimit: 300,
111
+ threshold: 0.6,
112
+ limit: 10,
113
+ keys: [
114
+ {
115
+ name: "name",
116
+ weight: 0.1,
117
+ },
118
+ {
119
+ name: "description",
120
+ weight: -0.1,
121
+ },
122
+ ],
123
+ },
124
+ },
125
+ {
126
+ uid: "api::book.book",
127
+ modelName: "book",
128
+ fuzzysortOptions: {
129
+ characterLimit: 500,
130
+ keys: [
131
+ {
132
+ name: "title",
133
+ weight: 0.2,
134
+ },
135
+ {
136
+ name: "description",
137
+ weight: -0.2,
138
+ },
139
+ ],
140
+ },
141
+ },
142
+ ],
143
+ },
144
+ },
145
+
146
+ // ...
147
+ });
148
+ ```
149
+
150
+ ## A note on performance:
151
+
152
+ A high `characterLimit`, `limit`, a low `threshold` (the lower the value the more matches) as well as setting `transliterate: true` all hamper the performance of the search algorithm. We recommend that you start out with a `characterLimit: 500`, `threshold: 0.5`, `limit: 15` and work your way from there. The characterLimit especially can be quite delicate, so make sure to test every scenario when dialing in it's value.
153
+
154
+ # Usage
155
+
156
+ ## Basic Search
157
+
158
+ Hitting the `/api/fuzzy-search/search?query=<your-query-string>` will return an array of matched entries for each content type registered in the config. If no match could be found an empty array will be returned. The endpoint accepts an optional `locale=<your-locale>` query as well.
159
+
160
+ Alternatively (and if the graphql plugin is installed), a search query is registered that accepts `query: String!` and `locale: String` (optional) as arguments. If set, the locale parameter will be used as a default for all queried content types. If you'd rather filter the content types by local on an individual level, pass the locale to the content type as a [filter](#filters) parameter.
161
+
162
+ **IMPORTANT:** Please note that in order to query for the locale of the content types via the locale parameter, localization must be enabled for the content types.
163
+
164
+ ### Example Requests
165
+
166
+ #### REST
167
+
168
+ ```JavaScript
169
+ await fetch(`${API_URL}/api/fuzzy-search/search?query=deresh&locale=en`);
170
+ // GET /api/fuzzy-search/search?query=deresh&locale=en
171
+ ```
172
+
173
+ #### GraphQl
174
+
175
+ ```graphql
176
+ query {
177
+ search(query: "deresh", locale: "en") {
178
+ authors {
179
+ data {
180
+ attributes {
181
+ name
182
+ }
183
+ }
184
+ }
185
+ books {
186
+ data {
187
+ attributes {
188
+ title
189
+ description
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### Example Responses
198
+
199
+ **IMPORTANT:** Please note that as of now published as well as unpublished entries will be returned by default. Modify this behavior by passing a [filter](#filters) to the query.
200
+
201
+ #### REST
202
+
203
+ ```json
204
+ {
205
+ "authors": [
206
+ {
207
+ "id": 1,
208
+ "name": "Любко Дереш",
209
+ "description": "As an author, Lyubko has had somewhat of a cult-like following among the younger generation in Ukraine since the appearance of his novel Cult at age eighteen, which was followed almost immediately by the publication of another novel, written in early high school.",
210
+ "createdAt": "2022-05-05T13:08:19.312Z",
211
+ "updatedAt": "2022-05-05T13:34:46.488Z",
212
+ "publishedAt": "2022-05-05T13:22:17.310Z"
213
+ }
214
+ ],
215
+ "books": [
216
+ {
217
+ "id": 1,
218
+ "title": "Jacob's Head",
219
+ "description": "Jacob’s Head by Lyubko Deresh is scheduled to be adapted into a movie in Ukraine in the near future.",
220
+ "createdAt": "2022-05-05T13:08:43.816Z",
221
+ "updatedAt": "2022-05-05T13:24:07.107Z",
222
+ "publishedAt": "2022-05-05T13:22:23.764Z"
223
+ }
224
+ ]
225
+ }
226
+ ```
227
+
228
+ #### GraphQl
229
+
230
+ ```json
231
+ {
232
+ "data": {
233
+ "search": {
234
+ "authors": {
235
+ "data": [
236
+ {
237
+ "attributes": {
238
+ "name": "Любко Дереш"
239
+ }
240
+ }
241
+ ]
242
+ },
243
+ "books": {
244
+ "data": [
245
+ {
246
+ "attributes": {
247
+ "title": "Jacob's Head",
248
+ "description": "Jacob’s Head by Lyubko Deresh is scheduled to be adapted into a movie in Ukraine in the near future."
249
+ }
250
+ }
251
+ ]
252
+ }
253
+ }
254
+ }
255
+ }
256
+ ```
257
+
258
+ ## Pagination
259
+
260
+ ### REST
261
+
262
+ The endpoint accepts query parameters in line with Strapis [pagination by page](https://docs.strapi.io/dev-docs/api/rest/sort-pagination#pagination-by-page) parameters. The difference being that the pagination is scoped for the content types individually.
263
+
264
+ **Important**: Please note that if pagination parameters are passed for a content type, it's data will now be available under the key `data`, whereas the pagination meta data can be found under the key `meta`.
265
+
266
+ | Parameter | Type | Description | Default |
267
+ | ------------------------------------ | ------- | ------------------------------------------------------------------------- | ------- |
268
+ | pagination[myContentType][page] | Integer | Page number | 1 |
269
+ | pagination[myContentType][pageSize] | Integer | Page size | 25 |
270
+ | pagination[myContentType][withCount] | Boolean | Adds the total numbers of entries and the number of pages to the response | True |
271
+
272
+ Please note that [myContentType] needs to be in the plural form. For example, for the content type "author," the correct parameter is "authors".
273
+
274
+
275
+ **Request:**
276
+
277
+ ```JavaScript
278
+ await fetch(`${API_URL}/api/fuzzy-search/search?query=deresh&pagination[authors][pageSize]=2&pagination[authors][page]=3`);
279
+ // GET /api/fuzzy-search/search?query=deresh&pagination[authors][pageSize]=2&pagination[authors][page]=3
280
+ ```
281
+
282
+ **Response:**
283
+
284
+ ```json
285
+ {
286
+ "authors": [
287
+ // ...
288
+ ],
289
+ "books": {
290
+ "data": [
291
+ // ...
292
+ ],
293
+ "meta": {
294
+ "pagination": {
295
+ "page": 3,
296
+ "pageSize": 2,
297
+ "total": 6,
298
+ "pageCount": 3
299
+ }
300
+ }
301
+ }
302
+ }
303
+ ```
304
+
305
+ ### GraphQL
306
+
307
+ The endpoint accepts pagination arguments in line with Strapis [pagination by page](https://docs.strapi.io/dev-docs/api/graphql#pagination-by-page) and [pagination by offset](https://docs.strapi.io/dev-docs/api/graphql#pagination-by-offset) parameters.
308
+
309
+ | Parameter | Description | Default |
310
+ | -------------------- | ---------------------------- | ------- |
311
+ | pagination[page] | Page number | 1 |
312
+ | pagination[pageSize] | Page size | 10 |
313
+ | pagination[start] | Start value | 0 |
314
+ | pagination[limit] | Number of entities to return | 10 |
315
+
316
+ **IMPORTANT:** Please note that in line with Strapis defaults, pagination methods can not be mixed. Always use either page with pageSize or start with limit.
317
+
318
+ **Request:**
319
+
320
+ ```graphql
321
+ search(query: "deresh") {
322
+ books(pagination: { page: 3, pageSize: 2 }) {
323
+ data {
324
+ attributes {
325
+ title
326
+ }
327
+ }
328
+ meta {
329
+ pagination {
330
+ page
331
+ pageSize
332
+ pageCount
333
+ total
334
+ }
335
+ }
336
+ }
337
+ }
338
+ ```
339
+
340
+ **Response:**
341
+
342
+ ```json
343
+ {
344
+ "data": {
345
+ "search": {
346
+ "books": {
347
+ "data": [
348
+ // ...
349
+ ],
350
+ "meta": {
351
+ "pagination": {
352
+ "page": 3,
353
+ "pageSize": 2,
354
+ "pageCount": 3,
355
+ "total": 6
356
+ }
357
+ }
358
+ }
359
+ }
360
+ }
361
+ }
362
+ ```
363
+
364
+ ## Population
365
+
366
+ ### REST
367
+
368
+ The endpoint accepts query parameters in line with Strapis [populate](https://docs.strapi.io/dev-docs/api/rest/populate-select#population) parameters. The difference being that the population is scoped for the content types individually.
369
+
370
+ | Parameter | Type | Description |
371
+ | ------------------------------------ | ------- | ------------------------------------------------------------------------- |
372
+ | population[myContentType] | String/PopulationParams | Either pass a string for the relation to populate directly, or build more complex population queries in line with the [official documentation on population](https://docs.strapi.io/dev-docs/api/rest/populate-select#population) like in the example below. |
373
+
374
+
375
+ **Request:**
376
+
377
+ ```JavaScript
378
+ await fetch(`${API_URL}/api/fuzzy-search/search?query=deresh&populate[books][author][populate][0]=city`);
379
+ // GET /api/fuzzy-search/search?query=deresh&populate[books][author][populate][0]=city
380
+ ```
381
+
382
+ **Response:**
383
+
384
+ ```json
385
+ {
386
+ "authors": [
387
+ // ...
388
+ ],
389
+ "books": [
390
+ {
391
+ "id": 1,
392
+ "title": "A good book",
393
+ "description": "Written by Lyubko Deresh",
394
+ "createdAt": "2022-09-21T16:16:07.365Z",
395
+ "updatedAt": "2023-07-24T12:00:51.624Z",
396
+ "publishedAt": "2022-09-21T16:16:09.973Z",
397
+ "locale": "en",
398
+ "author": {
399
+ "id": 2,
400
+ "name": "Любко Дереш",
401
+ "description": "A poet, novelist, essayist, and translator.",
402
+ "createdAt": "2022-09-24T12:39:34.669Z",
403
+ "updatedAt": "2023-11-10T12:28:22.828Z",
404
+ "publishedAt": "2022-09-24T12:39:53.945Z",
405
+ "locale": "en",
406
+ "city": {
407
+ "id": 1,
408
+ "name": "Pustomyty",
409
+ "createdAt": "2023-11-10T12:26:29.981Z",
410
+ "updatedAt": "2023-11-10T12:26:32.254Z",
411
+ "publishedAt": "2023-11-10T12:26:32.252Z"
412
+ }
413
+ }
414
+ }
415
+ ]
416
+ }
417
+ ```
418
+
419
+ ### GraphQL
420
+
421
+ See [Strapis official GraphQL documentation for queries](https://docs.strapi.io/dev-docs/api/graphql#queries).
422
+
423
+ ## Filters
424
+
425
+ ### REST
426
+
427
+ The endpoint accepts query parameters in line with Strapis [filter parameters](https://docs.strapi.io/dev-docs/api/rest/filters-locale-publication#filtering) parameters. The difference being that the filters are scoped for the content types individually.
428
+
429
+ **Request:**
430
+
431
+ ```JavaScript
432
+ await fetch(`${API_URL}/api/fuzzy-search/search?query=deresh&filters[books][publishedAt][$notNull]=true`);
433
+ // GET /api/fuzzy-search/search?query=deresh&filters[books][publishedAt][$notNull]=true
434
+ ```
435
+
436
+ **Response:**
437
+
438
+ ```json
439
+ {
440
+ "authors": [
441
+ // ...
442
+ ],
443
+ "books": {
444
+ "data": [
445
+ {
446
+ "id": 3,
447
+ "title": "A good book",
448
+ "description": "Written by Lyubko Deresh",
449
+ "createdAt": "2023-02-27T08:11:11.771Z",
450
+ "updatedAt": "2023-02-27T08:11:12.208Z",
451
+ "publishedAt": "2023-02-27T08:11:12.207Z",
452
+ "locale": "en"
453
+ }
454
+ ]
455
+ }
456
+ }
457
+ ```
458
+
459
+ ### GraphQL
460
+
461
+ The endpoint accepts filter arguments in line with Strapis [filters parameter](https://docs.strapi.io/dev-docs/api/graphql#filters) .
462
+
463
+ **Request:**
464
+
465
+ ```graphql
466
+ search(query: "deresh") {
467
+ books(filters: { publishedAt: { notNull: true } }) {
468
+ data {
469
+ attributes {
470
+ title
471
+ }
472
+ }
473
+ }
474
+ }
475
+ ```
476
+
477
+ **Response:**
478
+
479
+ ```json
480
+ {
481
+ "data": {
482
+ "search": {
483
+ "books": {
484
+ "data": [
485
+ {
486
+ "attributes": {
487
+ "title": "A good book"
488
+ }
489
+ }
490
+ ]
491
+ }
492
+ }
493
+ }
494
+ }
495
+ ```
496
+
497
+ ## Filter by Content Type (REST)
498
+
499
+ The REST-endpoint accepts an optional parameter to select only some content types the fuzzy search should run for.
500
+
501
+ | Parameter | Type | Description |
502
+ | --------------------- | ------ | --------------------------------------------------------------- |
503
+ | filters[contentTypes] | string | Comma seperated list of the content types to run the search for |
504
+
505
+ **Request:**
506
+
507
+ ```JavaScript
508
+ await fetch(`${API_URL}/api/fuzzy-search/search?query=deresh&filters[contentTypes]=books,authors`);
509
+ // GET /api/fuzzy-search/search?query=deresh&filters[contentTypes]=books
510
+ ```
511
+
512
+ **Response:**
513
+
514
+ ```json
515
+ {
516
+ "authors": [
517
+ // ...
518
+ ],
519
+ "books": [
520
+ // ...
521
+ ]
522
+ }
523
+ ```
524
+
525
+ # Why use fuzzysort and not something like Fuse.js?
526
+
527
+ While [Fuse.js](https://github.com/krisk/Fuse) proofs to be an amazing library, it can yield unexpected results and what is to be perceived as "false positives" (by a human) when searching through longer strings. Fuzzysort aims to solve this problem by introducing the evaluation and scoring of exact matches. Since we had issues with Fuse.js and it's underlying algorithm, we opted for fuzzysearch to do the heavy lifting instead.
528
+
529
+ # Contributing
530
+
531
+ Feel free to contribute, PRs are always welcome!
532
+
533
+ Please see the [CONTRIBUTING.md](CONTRIBUTING.md) for reference.
534
+
535
+ # Found a bug?
536
+
537
+ If you found a bug or have any questions please [submit an issue](https://github.com/DomDew/strapi-plugin-fuzzy-search/issues). If you think you found a way how to fix it, please feel free to create a pull request!
538
+
539
+ # Contributors ✨
540
+
541
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
542
+
543
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
544
+ <!-- prettier-ignore-start -->
545
+ <!-- markdownlint-disable -->
546
+ <table>
547
+ <tbody>
548
+ <tr>
549
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/felex0982"><img src="https://avatars.githubusercontent.com/u/14078988?v=4?s=100" width="100px;" alt="Felix Wagner"/><br /><sub><b>Felix Wagner</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=felex0982" title="Documentation">📖</a> <a href="#design-felex0982" title="Design">🎨</a></td>
550
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/wuestefeld"><img src="https://avatars.githubusercontent.com/u/33458141?v=4?s=100" width="100px;" alt="wuestefeld"/><br /><sub><b>wuestefeld</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=wuestefeld" title="Code">💻</a></td>
551
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/DomDew"><img src="https://avatars.githubusercontent.com/u/72755955?v=4?s=100" width="100px;" alt="Dominik"/><br /><sub><b>Dominik</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=DomDew" title="Code">💻</a> <a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=DomDew" title="Documentation">📖</a> <a href="#ideas-DomDew" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/pulls?q=is%3Apr+reviewed-by%3ADomDew" title="Reviewed Pull Requests">👀</a></td>
552
+ <td align="center" valign="top" width="14.28%"><a href="http://www.me.com"><img src="https://avatars.githubusercontent.com/u/17861353?v=4?s=100" width="100px;" alt="Rotar Rares"/><br /><sub><b>Rotar Rares</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=rotarrares" title="Code">💻</a></td>
553
+ <td align="center" valign="top" width="14.28%"><a href="https://nhvu1988.com/"><img src="https://avatars.githubusercontent.com/u/13212737?v=4?s=100" width="100px;" alt="Nguyễn Hoàng Vũ"/><br /><sub><b>Nguyễn Hoàng Vũ</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=nhvu1988" title="Code">💻</a></td>
554
+ <td align="center" valign="top" width="14.28%"><a href="https://chrismps.netlify.app/"><img src="https://avatars.githubusercontent.com/u/21962584?v=4?s=100" width="100px;" alt="Chris Michael"/><br /><sub><b>Chris Michael</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3AChrisMichaelPerezSantiago" title="Bug reports">🐛</a></td>
555
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/Sunny-Pirate"><img src="https://avatars.githubusercontent.com/u/109725967?v=4?s=100" width="100px;" alt="Luca Faccio"/><br /><sub><b>Luca Faccio</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3ASunny-Pirate" title="Bug reports">🐛</a></td>
556
+ </tr>
557
+ <tr>
558
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/aomalik"><img src="https://avatars.githubusercontent.com/u/44109070?v=4?s=100" width="100px;" alt="aomalik"/><br /><sub><b>aomalik</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3Aaomalik" title="Bug reports">🐛</a></td>
559
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/signorekai"><img src="https://avatars.githubusercontent.com/u/12168407?v=4?s=100" width="100px;" alt="Alfred Lau"/><br /><sub><b>Alfred Lau</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3Asignorekai" title="Bug reports">🐛</a></td>
560
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/philgran"><img src="https://avatars.githubusercontent.com/u/12853?v=4?s=100" width="100px;" alt="Phil Gran"/><br /><sub><b>Phil Gran</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3Aphilgran" title="Bug reports">🐛</a></td>
561
+ <td align="center" valign="top" width="14.28%"><a href="https://www.ddazal.com"><img src="https://avatars.githubusercontent.com/u/21014225?v=4?s=100" width="100px;" alt="David Daza"/><br /><sub><b>David Daza</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=ddazal" title="Documentation">📖</a></td>
562
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/aadelgrossi"><img src="https://avatars.githubusercontent.com/u/6358947?v=4?s=100" width="100px;" alt="Andre Grossi"/><br /><sub><b>Andre Grossi</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3Aaadelgrossi" title="Bug reports">🐛</a></td>
563
+ <td align="center" valign="top" width="14.28%"><a href="http://www.linkedin.com/in/jesúsr00"><img src="https://avatars.githubusercontent.com/u/68043863?v=4?s=100" width="100px;" alt="Jesús Reikel López Martín"/><br /><sub><b>Jesús Reikel López Martín</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3Ajesusr00" title="Bug reports">🐛</a></td>
564
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/prborisov17"><img src="https://avatars.githubusercontent.com/u/131133955?v=4?s=100" width="100px;" alt="prborisov17"/><br /><sub><b>prborisov17</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3Aprborisov17" title="Bug reports">🐛</a></td>
565
+ </tr>
566
+ <tr>
567
+ <td align="center" valign="top" width="14.28%"><a href="https://www.amityweb.co.uk"><img src="https://avatars.githubusercontent.com/u/1562777?v=4?s=100" width="100px;" alt="Laurence Cope"/><br /><sub><b>Laurence Cope</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/issues?q=author%3Aamityweb" title="Bug reports">🐛</a></td>
568
+ <td align="center" valign="top" width="14.28%"><a href="https://www.pavelglac.com/"><img src="https://avatars.githubusercontent.com/u/42679661?v=4?s=100" width="100px;" alt="Pavel Glac"/><br /><sub><b>Pavel Glac</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=pavelglac" title="Code">💻</a></td>
569
+ <td align="center" valign="top" width="14.28%"><a href="http://sauer.dev"><img src="https://avatars.githubusercontent.com/u/126874219?v=4?s=100" width="100px;" alt="Leon Sauer"/><br /><sub><b>Leon Sauer</b></sub></a><br /><a href="https://github.com/DomDew/strapi-plugin-fuzzy-search/commits?author=Lon3035" title="Documentation">📖</a></td>
570
+ </tr>
571
+ </tbody>
572
+ </table>
573
+
574
+ <!-- markdownlint-restore -->
575
+ <!-- prettier-ignore-end -->
576
+
577
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
578
+
579
+ This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!