barathvasan-dev commited on
Commit
ec39753
·
1 Parent(s): ae48b27

Fix: NLP-to-SQL now handles multiple vehicles and multiple locations with proper OR logic

Browse files
Files changed (1) hide show
  1. database.py +94 -12
database.py CHANGED
@@ -363,7 +363,7 @@ class FilterExtractor:
363
  return None
364
 
365
  def extract_location(self, query):
366
- """Extract location with variant matching"""
367
  q = query.lower()
368
  # Sort by length (longest first) to match longer variants first
369
  for canonical, variants in sorted(
@@ -376,8 +376,34 @@ class FilterExtractor:
376
  return canonical
377
  return None
378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  def extract_vehicle_type(self, query):
380
- """Extract vehicle type with synonym resolution"""
381
  q = query.lower()
382
  # Sort by length (longest first) to match longer synonyms first
383
  for synonym in sorted(self.vehicle_synonyms.keys(), key=len, reverse=True):
@@ -385,6 +411,27 @@ class FilterExtractor:
385
  return self.vehicle_synonyms[synonym]
386
  return None
387
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  def extract_date_range(self, query):
389
  """Extract date range (from X to Y, between X and Y)"""
390
  # Pattern: "from DD-MM-YYYY to DD-MM-YYYY" or "between DD-MM-YYYY and DD-MM-YYYY"
@@ -520,8 +567,8 @@ class FilterExtractor:
520
  return {
521
  "plate": self.extract_plate(query),
522
  "state": self.extract_state(query),
523
- "location": self.extract_location(query),
524
- "vehicle_type": self.extract_vehicle_type(query),
525
  "date": self.extract_date(query),
526
  "date_range": self.extract_date_range(query),
527
  "day": self.extract_day(query),
@@ -549,7 +596,7 @@ class FilterExtractor:
549
  def build_sql(self, filters, intents):
550
  """
551
  Build production-grade SQL from filters and intents.
552
- Handles complex aggregations, date ranges, time ranges, and conditions.
553
  """
554
 
555
  # =========================================================
@@ -596,7 +643,7 @@ class FilterExtractor:
596
  """)
597
 
598
  # =========================================================
599
- # BUILD WHERE CLAUSE FROM FILTERS
600
  # =========================================================
601
 
602
  where_conditions = []
@@ -609,13 +656,31 @@ class FilterExtractor:
609
  if filters["state"]:
610
  where_conditions.append(f"state = '{filters['state']}'")
611
 
612
- # Location filter
613
  if filters["location"]:
614
- where_conditions.append(f"LOWER(location) LIKE '%{filters['location'].lower()}%'")
 
 
 
 
 
 
 
 
 
615
 
616
- # Vehicle type filter
617
  if filters["vehicle_type"]:
618
- where_conditions.append(f"LOWER(vehicle_type) LIKE '%{filters['vehicle_type'].lower()}%'")
 
 
 
 
 
 
 
 
 
619
 
620
  # Date range filter
621
  if filters["date_range"]:
@@ -752,8 +817,25 @@ def ask_llm(user_query):
752
  print(f" Extracted Filters:")
753
  print(f" - Plate: {filters['plate']}")
754
  print(f" - State: {filters['state']}")
755
- print(f" - Location: {filters['location']}")
756
- print(f" - Vehicle Type: {filters['vehicle_type']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
757
  print(f" - Date: {filters['date']}")
758
  print(f" - Date Range: {filters['date_range']}")
759
  print(f" - Day: {filters['day']}")
 
363
  return None
364
 
365
  def extract_location(self, query):
366
+ """Extract SINGLE location with variant matching (legacy method)"""
367
  q = query.lower()
368
  # Sort by length (longest first) to match longer variants first
369
  for canonical, variants in sorted(
 
376
  return canonical
377
  return None
378
 
379
+ def extract_locations(self, query):
380
+ """Extract MULTIPLE locations from query (e.g., 'adyar and kottupuram')"""
381
+ q = query.lower()
382
+ locations = []
383
+
384
+ # Sort by length (longest first) to match longer variants first
385
+ for canonical, variants in sorted(
386
+ self.location_variants.items(),
387
+ key=lambda x: max(len(v) for v in x[1]),
388
+ reverse=True
389
+ ):
390
+ for variant in variants:
391
+ if variant in q:
392
+ locations.append(canonical)
393
+ break
394
+
395
+ # Remove duplicates while preserving order
396
+ seen = set()
397
+ unique_locations = []
398
+ for loc in locations:
399
+ if loc not in seen:
400
+ seen.add(loc)
401
+ unique_locations.append(loc)
402
+
403
+ return unique_locations if unique_locations else None
404
+
405
  def extract_vehicle_type(self, query):
406
+ """Extract SINGLE vehicle type with synonym resolution (legacy method)"""
407
  q = query.lower()
408
  # Sort by length (longest first) to match longer synonyms first
409
  for synonym in sorted(self.vehicle_synonyms.keys(), key=len, reverse=True):
 
411
  return self.vehicle_synonyms[synonym]
412
  return None
413
 
414
+ def extract_vehicle_types(self, query):
415
+ """Extract MULTIPLE vehicle types from query (e.g., 'bike and mini_truck')"""
416
+ q = query.lower()
417
+ vehicle_types = []
418
+
419
+ # Sort by length (longest first) to match longer synonyms first
420
+ for synonym in sorted(self.vehicle_synonyms.keys(), key=len, reverse=True):
421
+ if re.search(r'\b' + synonym + r'\b', q):
422
+ normalized = self.vehicle_synonyms[synonym]
423
+ vehicle_types.append(normalized)
424
+
425
+ # Remove duplicates while preserving order
426
+ seen = set()
427
+ unique_types = []
428
+ for vtype in vehicle_types:
429
+ if vtype not in seen:
430
+ seen.add(vtype)
431
+ unique_types.append(vtype)
432
+
433
+ return unique_types if unique_types else None
434
+
435
  def extract_date_range(self, query):
436
  """Extract date range (from X to Y, between X and Y)"""
437
  # Pattern: "from DD-MM-YYYY to DD-MM-YYYY" or "between DD-MM-YYYY and DD-MM-YYYY"
 
567
  return {
568
  "plate": self.extract_plate(query),
569
  "state": self.extract_state(query),
570
+ "location": self.extract_locations(query), # Now returns list or None
571
+ "vehicle_type": self.extract_vehicle_types(query), # Now returns list or None
572
  "date": self.extract_date(query),
573
  "date_range": self.extract_date_range(query),
574
  "day": self.extract_day(query),
 
596
  def build_sql(self, filters, intents):
597
  """
598
  Build production-grade SQL from filters and intents.
599
+ Handles complex aggregations, date ranges, time ranges, multiple vehicles, and multiple locations.
600
  """
601
 
602
  # =========================================================
 
643
  """)
644
 
645
  # =========================================================
646
+ # BUILD WHERE CLAUSE FROM FILTERS (HANDLE MULTIPLE VALUES)
647
  # =========================================================
648
 
649
  where_conditions = []
 
656
  if filters["state"]:
657
  where_conditions.append(f"state = '{filters['state']}'")
658
 
659
+ # Location filter - HANDLE MULTIPLE LOCATIONS
660
  if filters["location"]:
661
+ if isinstance(filters["location"], list):
662
+ # Multiple locations with OR logic
663
+ location_conditions = [
664
+ f"LOWER(location) LIKE '%{loc.lower()}%'"
665
+ for loc in filters["location"]
666
+ ]
667
+ where_conditions.append(f"({' OR '.join(location_conditions)})")
668
+ else:
669
+ # Single location (legacy)
670
+ where_conditions.append(f"LOWER(location) LIKE '%{filters['location'].lower()}%'")
671
 
672
+ # Vehicle type filter - HANDLE MULTIPLE VEHICLE TYPES
673
  if filters["vehicle_type"]:
674
+ if isinstance(filters["vehicle_type"], list):
675
+ # Multiple vehicle types with OR logic
676
+ vehicle_conditions = [
677
+ f"LOWER(vehicle_type) LIKE '%{vtype.lower()}%'"
678
+ for vtype in filters["vehicle_type"]
679
+ ]
680
+ where_conditions.append(f"({' OR '.join(vehicle_conditions)})")
681
+ else:
682
+ # Single vehicle type (legacy)
683
+ where_conditions.append(f"LOWER(vehicle_type) LIKE '%{filters['vehicle_type'].lower()}%'")
684
 
685
  # Date range filter
686
  if filters["date_range"]:
 
817
  print(f" Extracted Filters:")
818
  print(f" - Plate: {filters['plate']}")
819
  print(f" - State: {filters['state']}")
820
+
821
+ # Handle multiple locations
822
+ if filters['location']:
823
+ if isinstance(filters['location'], list):
824
+ print(f" - Locations: {', '.join(filters['location'])}")
825
+ else:
826
+ print(f" - Location: {filters['location']}")
827
+ else:
828
+ print(f" - Location: None")
829
+
830
+ # Handle multiple vehicle types
831
+ if filters['vehicle_type']:
832
+ if isinstance(filters['vehicle_type'], list):
833
+ print(f" - Vehicle Types: {', '.join(filters['vehicle_type'])}")
834
+ else:
835
+ print(f" - Vehicle Type: {filters['vehicle_type']}")
836
+ else:
837
+ print(f" - Vehicle Type: None")
838
+
839
  print(f" - Date: {filters['date']}")
840
  print(f" - Date Range: {filters['date_range']}")
841
  print(f" - Day: {filters['day']}")