सर्च सर्विस आर्किटेक्चर: वाल्की के साथ स्टैंडअलोन फ्लास्क एप्लिकेशन

यह लेख बताता है कि हमारी सर्च सर्विस एक अलग सर्वर पर एक स्टैंडअलोन फ्लास्क एप्लिकेशन के रूप में कैसे चलती है, जो उच्च-प्रदर्शन वेक्टर खोज, कैशिंग और ऑटोकम्प्लीट के लिए वाल्की (रेडिस फोर्क) का उपयोग करती है।

समस्या: खोज प्रदर्शन और स्केलेबिलिटी

खोज ऑपरेशन कम्प्यूटेशनल रूप से महंगे हैं:

  • फ़िल्टर निष्कर्षण: प्रत्येक क्वेरी के खिलाफ 2,500+ वाक्यांशों का मिलान करना

  • संबंधित खोजें: 65K क्वेरीज़ में समानता की गणना करना

  • ऑटोकम्प्लीट: 65K क्वेरीज़ पर प्रीफ़िक्स मिलान

  • उत्पाद फ़िल्टरिंग: कई मानदंडों द्वारा 5K+ उत्पादों को फ़िल्टर करना

मुख्य वेब सर्वर पर इन ऑपरेशनों को चलाने से होता है:

  • धीमा पेज लोड: खोज अन्य अनुरोधों को ब्लॉक करती है

  • मेमोरी दबाव: एम्बेडिंग्स एक विशाल मेमोरी फुटप्रिंट के लिए जिम्मेदार हैं

  • सीपीयू प्रतिस्पर्धा: समानता गणना सीपीयू-गहन है

  • स्केलिंग कठिनाई: खोज को स्वतंत्र रूप से स्केल नहीं कर सकते

हमें एक समर्पित खोज सेवा की आवश्यकता है जो स्वतंत्र रूप से स्केल कर सके।

समाधान: स्टैंडअलोन खोज सेवा

हम एक समर्पित सर्वर पर एक अलग फ्लास्क एप्लिकेशन चलाते हैं:

मुख्य वेब सर्वर
    ↓ HTTP API कॉल
खोज सेवा
    ↓ वाल्की क्वेरीज़
वाल्की सर्वर

यह आर्किटेक्चर प्रदान करता है:

  • स्वतंत्र स्केलिंग: मुख्य वेब सर्वर को प्रभावित किए बिना खोज सेवा को स्केल करें

  • संसाधन अलगाव: खोज ऑपरेशन मुख्य वेब सर्वर को प्रभावित नहीं करते

  • कैशिंग: वाल्की दोहराई गई क्वेरीज़ के लिए परिणाम कैश करती है

  • उच्च उपलब्धता: खोज सेवा मुख्य वेब सर्वर को प्रभावित किए बिना रीस्टार्ट कर सकती है

खोज सेवा घटक

1. फ़िल्टर निष्कर्षण API

  • एंडपॉइंट: /api/extract_filters

  • उद्देश्य: प्राकृतिक भाषा क्वेरीज़ से संरचित फ़िल्टर निकालना

  • उदाहरण:

GET /api/extract_filters?q=mini+pc+16gb+ram

प्रतिक्रिया:
{
  "Form Factor": "Mini PC",
  "Main Memory": "16"
}

कार्यान्वयन:

कैशिंग: वाक्यांश मैपिंग वाल्की में कैश की गई (30-दिन TTL)

2. संबंधित खोजें API

  • एंडपॉइंट: /api/related

  • उद्देश्य: वेक्टर खोज का उपयोग करके अर्थपूर्ण रूप से समान क्वेरीज़ ढूंढना

  • उदाहरण:

POST /api/related
{
  "query": "mini pc",
  "limit": 10
}

प्रतिक्रिया:
{
  "related": [
    {"query": "small computer", "similarity": 0.92},
    {"query": "compact desktop", "similarity": 0.89},
    {"query": "mini pc 8gb", "similarity": 0.87}
  ]
}

कार्यान्वयन:

  • all-mpnet-base-v2 का उपयोग करके क्वेरी को एम्बेड करें

  • निकटतम पड़ोसियों के लिए वाल्की रेडिसर्च क्वेरी करें

  • समानता के अनुसार क्रमबद्ध शीर्ष N परिणाम लौटाएं

कैशिंग: परिणाम वाल्की में कैश किए गए (7-दिन TTL)

3. ऑटोकम्प्लीट API

  • एंडपॉइंट: /api/autocomplete

  • उद्देश्य: उपयोगकर्ता के टाइप करते ही क्वेरीज़ सुझाना

  • उदाहरण:

GET /api/autocomplete?q=mini+p&limit=5

प्रतिक्रिया:
{
  "suggestions": [
    "mini pc",
    "mini pc 16gb",
    "mini pc 8gb ram",
    "mini pc fanless",
    "mini pc windows 11"
  ]
}

कार्यान्वयन:

  • प्रीफ़िक्स मिलान के साथ वाल्की रेडिसर्च क्वेरी करें

  • लोकप्रियता (इम्प्रेशन + क्लिक स्कोर) के अनुसार रैंक करें

  • शीर्ष N सुझाव लौटाएं

कैशिंग: ऑटोकम्प्लीट इंडेक्स वाल्की में (दैनिक अपडेटेड)

4. लोकप्रिय क्वेरीज़ API

  • एंडपॉइंट: /api/popular

  • उद्देश्य: सबसे लोकप्रिय क्वेरीज़ प्राप्त करना

  • उदाहरण:

GET /api/popular?limit=10

प्रतिक्रिया:
{
  "queries": [
    "mini pc",
    "thin client",
    "industrial pc",
    "all in one pc"
  ]
}

कार्यान्वयन:

  • वाल्की या JSON से क्वेरीज़ लोड करें

  • ट्रैफ़िक स्कोर (इम्प्रेशन + क्लिक) के अनुसार क्रमबद्ध करें

  • शीर्ष N क्वेरीज़ लौटाएं

कैशिंग: लोकप्रिय क्वेरीज़ वाल्की में कैश की गईं (30-दिन TTL)

वाल्की एकीकरण

वाल्की एक रेडिस फोर्क है जो प्रदान करती है:

  • वेक्टर खोज: समानता खोज के लिए रेडिसर्च मॉड्यूल

  • कैशिंग: तेज़ इन-मेमोरी की-वैल्यू स्टोर

  • ऑटोकम्प्लीट: सॉर्टेड सेट्स के साथ प्रीफ़िक्स मिलान

  • स्थायित्व: स्थायित्व के लिए AOF (Append-Only File)

रेडिसर्च के साथ वेक्टर खोज

हम वेक्टर समानता खोज के लिए वाल्की के रेडिसर्च मॉड्यूल का उपयोग करते हैं:

इंडेक्स निर्माण:

client.ft("queries_idx").create_index([
    VectorField("embedding", "FLAT", {
        "TYPE": "FLOAT32",
        "DIM": 768,
        "DISTANCE_METRIC": "COSINE"
    }),
    TextField("query"),
    NumericField("score")
])

वेक्टर खोज:

query_embedding = model.encode(query)
results = client.ft("queries_idx").search(
    Query("*=>[KNN 10 @embedding $vec AS score]")
    .sort_by("score")
    .return_fields("query", "score")
    .dialect(2),
    query_params={"vec": query_embedding.tobytes()}
)

यह कोसाइन समानता द्वारा 10 निकटतम पड़ोसियों को लौटाता है।

कैशिंग रणनीति

हम वाल्की में कई डेटा प्रकार कैश करते हैं:

वाक्यांश मैपिंग (30-दिन TTL):

client.setex(
    "seo:phrase_mappings",
    30 * 24 * 3600,
    json.dumps(phrase_mappings)
)

संबंधित खोजें (7-दिन TTL):

cache_key = f"related:{query_hash}"
client.setex(cache_key, 7 * 24 * 3600, json.dumps(results))

लोकप्रिय क्वेरीज़ (30-दिन TTL):

client.setex(
    "seo:popular_queries",
    30 * 24 * 3600,
    json.dumps(popular_queries)
)

ऑटोकम्प्लीट इंडेक्स (दैनिक अपडेटेड):

for query, score in queries:
    client.zadd("autocomplete:mini", {query: score})

सॉर्टेड सेट्स के साथ ऑटोकम्प्लीट

हम ऑटोकम्प्लीट के लिए वाल्की सॉर्टेड सेट्स का उपयोग करते हैं:

इंडेक्स संरचना:

autocomplete:m     → ["mini pc": 5000, "mini computer": 3000]
autocomplete:mi    → ["mini pc": 5000, "mini computer": 3000]
autocomplete:min   → ["mini pc": 5000, "mini computer": 3000]
autocomplete:mini  → ["mini pc": 5000, "mini computer": 3000]

प्रीफ़िक्स लुकअप:

prefix = "mini"
results = client.zrevrange(f"autocomplete:{prefix}", 0, 9, withscores=True)

यह "mini" से शुरू होने वाली शीर्ष 10 क्वेरीज़, स्कोर के अनुसार क्रमबद्ध, लौटाता है।

API संचार

मुख्य वेब सर्वर HTTP के माध्यम से खोज सेवा को कॉल करता है:

फ़िल्टर निष्कर्षण

from app.shared.filter_service import extract_filters_from_query

filters = extract_filters_from_query("mini pc 16gb ram")
# आंतरिक रूप से कॉल करता है: GET SEARCH_SERVICE_URL/api/extract_filters?q=...

संबंधित खोजें

import requests

response = requests.post(
    "SEARCH_SERVICE_URL/api/related",
    json={"query": "mini pc", "limit": 10},
    timeout=2
)
related = response.json()["related"]

ऑटोकम्प्लीट

response = requests.get(
    "SEARCH_SERVICE_URL/api/autocomplete",
    params={"q": "mini p", "limit": 5},
    timeout=1
)
suggestions = response.json()["suggestions"]

त्रुटि हैंडलिंग और फॉलबैक

मुख्य वेब सर्वर खोज सेवा विफलताओं को सहजता से संभालता है:

try:
    filters = extract_filters_from_query(query)
except Exception as e:
    logger.error(f"Search service failed: {e}")
    filters = {}  # फॉलबैक खाली फ़िल्टर पर

यह सुनिश्चित करता है कि खोज सेवा डाउन होने पर भी मुख्य वेब सर्वर कार्य करता रहे।


### नेटवर्क कॉन्फ़िगरेशन

सभी सर्वर एक निजी नेटवर्क पर हैं:

- **मुख्य वेब सर्वर**: खोज सेवा और वाल्की तक पहुंच सकता है

- **खोज सेवा**: वाल्की तक पहुंच सकती है

- **वाल्की**: केवल मुख्य वेब सर्वर और खोज सेवा से पहुंच योग्य

खोज सेवा या वाल्की तक कोई बाहरी पहुंच नहीं।

## SEO पाइपलाइन के साथ एकीकरण

खोज सेवा SEO पाइपलाइन के साथ एकीकृत होती है:

### चरण 11: वाल्की में माइग्रेट करें

SEO पाइपलाइन डेटा को वाल्की में लोड करती है:

```python
# क्वेरी एम्बेडिंग्स लोड करें
for query, embedding in zip(queries, embeddings):
    client.hset(f"query:{query_hash}", mapping={
        "query": query,
        "embedding": embedding.tobytes(),
        "score": score
    })

# रेडिसर्च इंडेक्स बनाएं
client.ft("queries_idx").create_index([...])

विवरण के लिए वाल्की माइग्रेशन देखें।

क्वेरी लॉगिंग

खोज सेवा SEO पाइपलाइन के लिए क्वेरीज़ लॉग करती है:

log_entry = {
    "timestamp": datetime.now(timezone.utc).isoformat(),
    "query": query,
    "filters_extracted": filters,
    "results_count": len(results)
}
with open(SEO_LIVE_QUERIES_LOG, "a") as f:
    f.write(json.dumps(log_entry) + "\n")

ये लॉग चरण 1d: लाइव क्वेरीज़ प्राप्त करें में वापस फीड होते हैं।

संदर्भ

तकनीकी अवधारणाएं