← Back to work

Search / Information Retrieval · 2026

Internal Document Search — Elasticsearch

Company knowledge tends to end up spread across SharePoint, Teams and GitHub, and finding the current version of a policy means knowing where to look. I built a search app that indexes those documents into Elasticsearch and lets people search them in one place, with results ranked by relevance, filters typed straight into the search box, and live counts by source and year.

One query returns ranked results, category counts and year counts together

PythonFlaskElasticsearch 8DockerBootstrap 5

Role

Developer — self-directed build to go deep on Elasticsearch query design, from indexing to facets.

What I Built

  • Bulk indexing of the whole document set into Elasticsearch with a single CLI command (flask reindex)
  • Full-text search across title, summary and body with a multi_match query, ranked by BM25 relevance
  • Inline filter syntax parsed from the search box: category:sharepoint, year:2023, or both combined with free text
  • Filters applied in Elasticsearch's filter context, so they narrow results without changing relevance scores and can be cached
  • Faceted navigation from terms and date_histogram aggregations, returned in the same request as the results
  • Paginated results and a full document view

Search in Action

How It Works

The Flask app turns each search into a single bool query. The free text becomes a multi_match "must" clause across the name, summary and content fields, so a match in the title naturally outranks one buried in the body. Before that, a small parser pulls category: and year: tokens out of the query string and turns them into a term filter on the category keyword and a date range on updated_at, using Elasticsearch date math to cover the whole year. Those go into the bool query's filter clause rather than "must", which keeps them out of scoring. The same request also carries two aggregations: a terms aggregation on category and a yearly date_histogram on updated_at. That gives the sidebar its counts (sharepoint 7, teams 3, and so on) without a second round trip, and each facet is a one-click link that adds the matching filter to the current query. Elasticsearch runs locally in Docker as a single 8.x node, and the official Python client handles indexing, search and document retrieval behind a small wrapper class.

Tech Used

PythonFlaskElasticsearch 8elasticsearch-pyQuery DSLAggregationsDockerJinjaBootstrap 5

Have a similar problem?

I help startups and product teams build fast, scalable web applications and move manual operations into software. Let's talk about what you're working on.

← All work