Fuse.js:检索与多字搜索完全匹配的记录

时间:2018-07-05 09:02:29

标签: javascript fuse.js

我使用stty raw -echo试图在JS对象中进行“多个单词”搜索,以获取包含所要查找的每个单词的记录。

我的数据结构如下(来自fuse.js):

Fuse.js

我的问题是我的设置适用于单字搜索(例如[{ title: "The Lost Symbol", author: { firstName: "Dan", lastName: "Brown" } }, ...] ),而不适用于更多单词(BrownDan Brown)。

保险丝选项:

Dan Brown Vinci

{
    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
}

2 个答案:

答案 0 :(得分:3)

不幸的是,fuse.js 没有查看所有字段,而是一个匹配的字段。我通过将所有字段放入一个带有字符串数组的字段来解决这个问题。

示例:

[{
    title: "The Lost Symbol",
    author: {
      firstName: "Dan",
      lastName: "Brown"
    },
    keywords: ["The Lost Symbol", "Dan", "Brown"] //values of title, firstname & lastname
 }, ...]

并且只需指定 keywords Fuse 选项的 keys 字段

{
    shouldSort: true,
    matchAllTokens: true,
    findAllMatches: true,
    includeScore: true,
    threshold: 0,
    location: 0,
    distance: 100,
    maxPatternLength: 32,
    minMatchCharLength: 1,
    keys: ["keywords"]  //just put keywords alone
}

这对我有用。希望它也适用于您。

答案 1 :(得分:1)

我们也有类似的需求,最终解决如下:

(注意:我最初是在 https://github.com/krisk/Fuse/issues/235#issuecomment-850269634 上分享的)


<块引用>

对于通过谷歌搜索或其他方式最终到达这里的任何人,我们最终在 https://github.com/sparkletown/sparkle/pull/1460 中采用了不同的方法(感谢@yarikoptic > 出色的调试、探索和改进工作)

我们基本上使用正则表达式 (tokeniseStringWithQuotesBySpaces) 拆分搜索查询,以标记每个单词,但将 "" 之间的单词保留为单个标记):

/**
 * Split the provided string by spaces (ignoring spaces within "quoted text") into an array of tokens.
 *
 * @param string
 *
 * @see https://stackoverflow.com/a/16261693/1265472
 *
 * @debt Depending on the outcome of https://github.com/github/codeql/issues/5964 we may end up needing to change
 *   this regex for performance reasons.
 */
export const tokeniseStringWithQuotesBySpaces = (string: string): string[] =>
  string.match(/("[^"]*?"|[^"\s]+)+(?=\s*|\s*$)/g) ?? [];

注意:请检查 https://github.com/github/codeql/issues/5964,因为正则表达式可能存在 ReDoS 漏洞,但也可能只是 CodeQL 扫描程序中的误报)

使用我们的标准保险丝配置:

      new Fuse(filteredPosterVenues, {
        keys: [
          "name",
          "poster.title",
          "poster.authorName",
          "poster.categories",
        ],
        threshold: 0.2, // 0.1 seems to be exact, default 0.6: brings too distant if anyhow related hits
        ignoreLocation: true, // default False: True - to search ignoring location of the words.
        findAllMatches: true,
      }),

然后使用我们的 tokeniseStringWithQuotesBySpaces tokeniser + 自定义 Fuse 查询(使用 $and 连接我们的每个标记,然后使用 $or 连接不同的字段)进行搜索:

const tokenisedSearchQuery = tokeniseStringWithQuotesBySpaces(
  normalizedSearchQuery
);

if (tokenisedSearchQuery.length === 0) return filteredPosterVenues;

return fuseVenues
  .search({
    $and: tokenisedSearchQuery.map((searchToken: string) => {
      const orFields: Fuse.Expression[] = [
        { name: searchToken },
        { "poster.title": searchToken },
        { "poster.authorName": searchToken },
        { "poster.categories": searchToken },
      ];

      return {
        $or: orFields,
      };
    }),
  })
  .map((fuseResult) => fuseResult.item);

从我今天的测试来看,这似乎非常有效地满足了我们的需求。>