Skip to content

Instantly share code, notes, and snippets.

@eigilsagafos
Created March 17, 2020 13:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save eigilsagafos/c4ab8c2dd8e8a47d017d7e543fa5887e to your computer and use it in GitHub Desktop.
Save eigilsagafos/c4ab8c2dd8e8a47d017d7e543fa5887e to your computer and use it in GitHub Desktop.
Fauna Ngram search example

WARNING! This has only been tested on collections with a few k items. Don't know how this performs on "large" collections or large chunks of text.

Create an index on one or multiple values

q.CreateIndex({
    name: "Foos_ngram",
    active: true,
    source: {
        class: q.Collection("Foos"),
        fields: {
            ngram: q.Query(
                q.Lambda(
                    "instance",
                    q.NGram(
                        q.LowerCase(
                            q.Concat(
                                [
                                    q.Select(
                                        ["data", "email"],
                                        q.Var("instance")
                                    ),
                                    q.Select(
                                        ["data", "firstName"],
                                        q.Var("instance"),
                                        ""
                                    ),
                                    q.Select(
                                        ["data", "lastName"],
                                        q.Var("instance"),
                                        ""
                                    ),
                                ],
                                ""
                            )
                        ),
                        2,
                        3
                    )
                )
            ),
        },
    },
    terms: [{ binding: "ngram" }],
})

Search example

const searchString = "Foo Bar"

if (searchString.length < 2)
    throw new Error("Search needs at least two characters")

const words = searchString.trim().split(/\s/g)

const searchStrings = words
    .map(word => {
        const items = word.match(/.{1,3}/g)
        const length = items.length
        if (items.length > 1 && items[length - 1].length === 1) {
            const last = items[length - 1]
            const beforeLast = items[length - 2]
            items[length - 2] = beforeLast.slice(0, 2)
            items[length - 1] = beforeLast.slice(2, 3) + last
            beforeLast
        }
        return items
    })
    .flat()
    .filter(item => item.length > 1)
 
const matchers = searchStrings.map(part => q.Match(q.Index("Foos_ngram"), q.LowerCase(part)))

const query = q.Intersection(...matchers)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment