Using Fuse.js
I'm trying to make a "multiple word" search in a JS object to get the records which contain exactly each word looked for.
My data structure is the following (from fuse.js):
[{
title: "The Lost Symbol",
author: {
firstName: "Dan",
lastName: "Brown"
}
}, ...]
My issue is that my setup works for a one word search (Brown
for example) but not for more (Dan Brown
or Dan Brown Vinci
).
Fuse options:
{
shouldSort: true,
matchAllTokens: true,
findAllMatches: true,
includeScore: true,
threshold: 0,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
"title",
"author.firstName",
"author.lastName"
]
}
new Vue({
el: "#app",
data: {
Fuse: null,
searchText: '',
result : [],
fuseOptions: {
shouldSort: true,
matchAllTokens: true,
findAllMatches: true,
includeScore: true,
threshold: 0,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
"title",
"author.firstName",
"author.lastName"
]
},
list: [{
title: "Old Man's War",
author: {
firstName: "John",
lastName: "Scalzi"
}
},
{
title: "The Lock Artist",
author: {
firstName: "Steve",
lastName: "Hamilton"
}
},
{
title: "HTML5",
author: {
firstName: "Remy",
lastName: "Sharp"
}
},
{
title: "Right Ho Jeeves",
author: {
firstName: "P.D",
lastName: "Woodhouse"
}
},
{
title: "The Code of the Wooster",
author: {
firstName: "P.D",
lastName: "Woodhouse"
}
},
{
title: "Thank You Jeeves",
author: {
firstName: "P.D",
lastName: "Woodhouse"
}
},
{
title: "The DaVinci Code",
author: {
firstName: "Dan",
lastName: "Brown"
}
},
{
title: "Angels & Demons",
author: {
firstName: "Dan",
lastName: "Brown"
}
},
{
title: "The Silmarillion",
author: {
firstName: "J.R.R",
lastName: "Tolkien"
}
},
{
title: "Syrup",
author: {
firstName: "Max",
lastName: "Barry"
}
},
{
title: "The Lost Symbol",
author: {
firstName: "Dan",
lastName: "Brown"
}
},
{
title: "The Book of Lies",
author: {
firstName: "Brad",
lastName: "Meltzer"
}
},
{
title: "Lamb",
author: {
firstName: "Christopher",
lastName: "Moore"
}
},
{
title: "Fool",
author: {
firstName: "Christopher",
lastName: "Moore"
}
},
{
title: "Incompetence",
author: {
firstName: "Rob",
lastName: "Grant"
}
},
{
title: "Fat",
author: {
firstName: "Rob",
lastName: "Grant"
}
},
{
title: "Colony",
author: {
firstName: "Rob",
lastName: "Grant"
}
},
{
title: "Backwards, Red Dwarf",
author: {
firstName: "Rob",
lastName: "Grant"
}
},
{
title: "The Grand Design",
author: {
firstName: "Stephen",
lastName: "Hawking"
}
},
{
title: "The Book of Samson",
author: {
firstName: "David",
lastName: "Maine"
}
},
{
title: "The Preservationist",
author: {
firstName: "David",
lastName: "Maine"
}
},
{
title: "Fallen",
author: {
firstName: "David",
lastName: "Maine"
}
},
{
title: "Monster 1959",
author: {
firstName: "David",
lastName: "Maine"
}
}
]
},
methods: {
fuseSearch: function() {
let self = this;
this.result = this.Fuse.search(self.searchText)
}
},
mounted() {
let self = this
this.Fuse = new window.Fuse(self.list, self.fuseOptions);
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
table {
width: 100%;
margin-top:20px
}
table th{
font-weight:bold
}
table td{
padding-top:5px
}
input{
height:30px;
width:200px;
font-size:14px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.2.1/fuse.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<input type="text" v-model="searchText" @keyup="fuseSearch()" placeholder="search for text">
<div v-if="result.length == 0" style="margin-top:10px">No matching results, here is the full list</div>
<div v-else style="margin-top:10px">{{result.length}} records found</div>
<table>
<tr>
<th>Title</th>
<th>FistName</th>
<th>LastName</th>
<th>Score</th>
</tr>
<tr v-if="result.length >0" v-for="data in result" >
<td>{{data.item.title}}</td>
<td>{{data.item.author.firstName}}</td>
<td>{{data.item.author.lastName}}</td>
<td>{{Math.round(data.score*100,2)/100}}</td>
</tr>
<tr v-if="result.length == 0" v-for="data in list">
<td>{{data.title}}</td>
<td>{{data.author.firstName}}</td>
<td>{{data.author.lastName}}</td>
<td></td>
</tr>
</table>
</div>
Unfortunately, fuse.js does not look into all fields, rather one matching field. I solved this problem by putting all the fields into one field with array of strings.
Example:
[{
title: "The Lost Symbol",
author: {
firstName: "Dan",
lastName: "Brown"
},
keywords: ["The Lost Symbol", "Dan", "Brown"] //values of title, firstname & lastname
}, ...]
And just specify keywords Fuse option's keys field
{
shouldSort: true,
matchAllTokens: true,
findAllMatches: true,
includeScore: true,
threshold: 0,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: ["keywords"] //just put keywords alone
}
This worked for me. Hope it works for you too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With