Pace search powered by Firesearch.

Mat Ryer · 17 Nov 2020

Pace search powered by Firesearch.

Mat Ryer · 17 Nov 2020

Pace customers search through their cards and conversations using the built-in search box in the app.

This powerful search is powered by Firesearch.dev, which is now available for you to use in your own apps.

This post will explain how Pace uses Firesearch to achieve its search experience.

Indexes

It’s important for customer data to remain siloed, so Pace uses an index per organisation.

When a new organisation is created, Pace uses the IndexService.CreateIndex method to programmatically create a dedicated index for that org.

We create multiple indexes per customer to serve different use cases. For example, Firesearch has a dedicated Autocomplete API, which we use to complete names in mentions and invite boxes.

Pace also distinguishes between card and conversation search, so we use an index for each.

Building the indexes

Whenever a new card is added or updated, Pace kicks off a background task (using a pubsub service) which loads the updated card, and uses the IndexService.PutDoc method to update its entry in the index.

The work is done in the background so it doesn’t impact the user experience. The card search is therefore eventually consistent.

If a card is deleted, the background task will go and remove it from the index using the IndexService.DeleteDoc method.

Documents

Each card gets its own document in our customer’s index.

Identifying documents

In Firesearch, each document has a unique ID. This allows us to update or delete them.

In Pace, we use the card ID as the document ID. Whenever a card changes, we know which document to update (since the IDs are the same).

What goes into a document?

A typical search document looks like this:

{
	"id": "card-123",
	"title": "Title of the card",
	"searchFields": [
		{
			"key": "title",
			"value": "Title of the card",
			"store": true
		},
		{
			"key": "body",
			"value": "This is the body of the card."
		}
	],
	"fields": [
		{
			"key": "teamID",
			"value": "playground-team-id"
		},
		{
			"key": "cardStatus",
			"value": "done"
		},
		{
			"key": "isOpen",
			"value": true
		}
	]
}

We include the title and body of the card in the searchFields. We set store: true on the title field because we want to get it back in the search results later.

We also include some additional fields to drive our user experience. These are explained below.

Pay attention to how documents are updated

Whenever source data changes (in our case, cards), we want to update the search index document to reflect the changes.

In Firesearch, you do not update individual fields within a document (like when a card’s status changes), instead you have to put the entire document each time.

This is worth bearing in mind when you come to design your system.

In our case, a pubsub driven background task is perfect. Whenever the title, body or status of a card changes, we fire an event which indicates that we want the search document to be updated.

This means that our search is eventually consistent, although things happen pretty quickly so you don’t ever notice that as a user.

Filtering

Search is best when the user actively or passively provides additional context about what they’re looking for.

In our case, the user is likely searching for open cards (i.e. future work, up next, or in progress) by default. We allow them to change this selection in the UI, which we oblige by tweaking the filters in our search query.

Let’s take another look at those fields:

"fields": [
	{
		"key": "teamID",
		"value": "playground-team-id"
	},
	{
		"key": "cardStatus",
		"value": "done"
	},
	{
		"key": "isOpen",
		"value": true
	}
]
  • The teamID filter field allows us to scope search to an individual team - although this is optional at query time
  • The cardStatus filter field lets users search within specific statues (or by omitting this field from the query to search all cards)
  • The isOpen boolean filter lets us specify whether a card is open or not (i.e. status != "done")—omitting a value altogether in the query has the result of not filtering the results, so they would include both open and closed cards

Equality matching only

For performance reasons, Firesearch provides equality checking in filters rather than greater than, less than, not equal to, etc.

Instead of writing queries against a large selection of fields like you might in a document store, it is recommended to do the business logic work up-front, and store the results in the document.

Notice in Pace how we set the isOpen value when we write the document (we aren’t doing an IN query on the status field, we’re just doing a true or false on a simple boolean).

An ecommerce example might show that product price ranges might have a priceGroup string field that has one of a possible range of values, depending on which group the item is in: 0-99, 100-199, 200-plus, etc.

Conclusion

We covered how we use Firesearch at Pace to power all our customers’ card search, conversation search, and name autocomplete.

  • Multiple indexes per customer keeps data seperated and safe
  • Machine learning brings the most relevant hits to the top
  • Filtering allows us to provide even more targeted results, delivering a great user experience at high performance

Want to try Firesearch?


Learn more about what we're doing at Pace.

A lot of our blog posts come out of the technical work behind a project we're working on called Pace.

We were frustrated by communication and project management tools that interrupt your flow and overly complicated workflows turn simple tasks, hard. So we decided to build Pace.

Pace is a new minimalist project management tool for tech teams. We promote asynchronous communication by default, while allowing for those times when you really need to chat.

We shift the way work is assigned by allowing only self-assignment, creating a more empowered team and protecting the attention and focus of devs.

We're currently live and would love you to try it and share your opinions on what project management tools should and shouldn't do.

What next? Start your 14 day free trial to see if Pace is right for your team


First published on 17 Nov 2020 by Mat Ryer
#search #saas #sveltejs #javascript #typescript

or you can share the URL directly:

https://pace.dev/blog/2020/11/17/pace-search-with-firesearch.dev.html

Thank you, we don't do ads so we rely on you to spread the word.

https://pace.dev/blog/2020/11/17/pace-search-with-firesearch.dev.html


You might also like:

Batching operations in Go #Golang #Patterns

Evil UX patterns for attention seeking apps #UX #Design #Productivity

How code generation wrote our API and CLI #codegen #api #cli #golang #javascript #typescript

Subscribe:
Atom RSS JSON