Back to Blog
4 min read

How We Built a Resilient Venue Matching System Using Fuzzy Logic and Scoring in Next.js

The Problem: Messy Addresses, Broken Matches

If you’ve ever tried to match user-generated addresses, you know the pain. At AustinsElite, we were migrating from a legacy Laravel system to a modern Laravel 12 stack, and one of the thorniest issues was linking events to venues based on location data. Our old approach? Exact string matching. It failed constantly.

"123 Main St" didn’t match "123 Main Street". "The Venue, Austin, TX" was treated as unrelated to "123 Main St, Austin"—even when they were the same place. With hundreds of events and venues, manual cleanup wasn’t scalable. We needed a system that could understand addresses, not just compare them.

The result was missed associations, duplicated venues, and a brittle data pipeline. It wasn’t just inconvenient—it eroded trust in the system. So we rebuilt the matching engine from the ground up using fuzzy logic, scoring, and smart normalization.

From Exact to Fuzzy: Building a Scoring Engine

We replaced exact matching with a weighted scoring algorithm that evaluates multiple attributes: normalized address components, venue names, and geographic proximity. Instead of asking "Do these strings match?", we now ask "How likely is this event at this venue?" and assign a confidence score.

The core of the system lives in our matchEventToVenue utility, which calculates a composite score across several dimensions:

  • Name similarity (using string distance algorithms like Jaro-Winkler)
  • Street match (after parsing and normalizing)
  • City and state alignment
  • Postal code proximity

Each factor gets a dynamic weight. For example, in dense urban areas, street and number precision matter more. In rural regions, we lean heavier on city and postal code. These weights are configurable, making the system adaptable without code changes.

But the real game-changer was address normalization.

Normalizing Chaos with libpostal

We integrated libpostal, an open-source address parser trained on global geospatial data. When an event or venue is created, its address is parsed into structured components: street number, street name, city, state, postal code, etc.

This meant "123 Main St, Austin, TX 78701" and "Austin, Texas, 123 Main Street" both become:

{
  "street_number": "123",
  "street": "Main",
  "city": "Austin",
  "state": "TX",
  "postcode": "78701"
}

Now, comparisons happen on structured data, not raw strings. This single change reduced false negatives by over 60% in early testing.

We wrapped libpostal in a Node.js service using the node-libpostal binding, running it server-side in API routes. It’s not lightweight—it requires ~2GB of memory to load the model—but for our use case, the trade-off was worth it. We cache parsed results to minimize redundant calls.

The normalized output also improved slug generation. Previously, slight address variations created inconsistent URLs like /venues/123-main-st and /venues/123-main-street. Now, slugs are based on normalized components, ensuring consistent, predictable URLs.

Results and Lessons Learned

After deploying the new matching engine in the Next.js app, we saw:

  • 85% reduction in unassigned events due to address mismatch
  • 40% fewer duplicate venue entries
  • Admin time spent on manual corrections dropped from hours to minutes per week

More importantly, the system became adaptable. When we noticed music venues often had informal names ("The Ballroom" vs "Stubb's BBQ – The Ballroom"), we tweaked the name similarity threshold and reweighted street precision. The scoring model made tuning intuitive.

One surprise: libpostal sometimes over-normalizes. "The Domain, Austin" was parsed as a city and state, losing the venue name. We added a fallback layer that preserves the original input when parsing confidence is low—a reminder that no tool is perfect.

This refactor wasn’t just about better matches. It shifted our mindset from data enforcement to data interpretation. Instead of demanding clean input, we built resilience into the system. That’s the real win.

If you're wrestling with inconsistent location data, don’t reach for exact matches. Normalize, score, and embrace the fuzziness. Your future self—and your users—will thank you.

Newer post

Migrating to Motia: How We Scaled Venue-Event Matching with AI-Powered Address Normalization

Older post

Building a Scalable Sitemap Architecture in Next.js: From Monolith to Modular