Merhaba arkadaşlar,
Elasticsearch kullanan bir adres arama sistemi üzerinde çalışıyorum ve scoring/boost konusunda yardıma ihtiyacım var.
Mevcut Yapı
- İller, ilçeler ve mahalleler indexlenmiş durumda
- İstanbul, Ankara ve İzmir'deki sonuçlar için özel ağırlıklar tanımlanmış
- full_text alanında "[MAHALLE ADI] MAH, [İLÇE] / [İL]" formatında veriler mevcut
Sorun
Şu anda iki tür arama senaryosu için farklı davranışlar istiyorum:
Spesifik İl-İlçe Araması (örn: "Konya Meram")
- Öncelikle tam eşleşen sonuçlar gelmeli (Konya'daki Meram ilçesi ve mahalleleri)
- Sonra diğer benzer sonuçlar gelmeli
- Bu durumda İstanbul/Ankara/İzmir önceliği düşük olmalı
Tek Kelime Araması (örn: sadece "Meram")
- İstanbul'daki eşleşmeler
- Ankara'daki eşleşmeler
- İzmir'deki eşleşmeler
- Son olarak diğer illerdeki eşleşmeler
Denediğim Çözüm
'functions' => [
[
'filter' => [
'term' => [
'city_name.raw' => 'ISTANBUL'
]
],
'weight' => 1000000
],
// ... Ankara ve İzmir için benzer yapılar
]
Ancak bu durumda "Konya Meram" gibi spesifik aramalar yapıldığında bile İstanbul/Ankara/İzmir sonuçları öne çıkabiliyor.
Beklenen Davranış
İl-ilçe belirtildiğinde o ile özel sonuçlar öncelikli olmalı
Sadece ilçe/mahalle ismi yazıldığında büyükşehirler öncelikli olmalı
Tam eşleşmeler her zaman üst sıralarda yer almalı
Scoring ve boost değerlerini nasıl ayarlamalıyım? Ya da daha iyi bir yaklaşım mı önerirsiniz?
Yardımlarınız için şimdiden teşekkürler.
Teknoloji Stack:
Laravel 10
Elasticsearch 8
elasticsearch/elasticsearch
public function search(?string $query = null): array
{
if (empty($query)) {
$params = [
'index' => $this->indexName,
'body' => [
'size' => 15,
'query' => [
'function_score' => [
'query' => [
'bool' => [
'should' => [
[
'match' => [
'city_name' => [
'query' => 'ISTANBUL',
'boost' => 3
]
]
],
[
'match' => [
'city_name' => [
'query' => 'ANKARA',
'boost' => 2
]
]
]
]
]
],
'functions' => [
[
'filter' => [
'terms' => [
'district_name.raw' => [
'KADIKOY', 'BESIKTAS', 'SISLI', 'USKUDAR',
'BEYOGLU', 'MALTEPE', 'SARIYER', 'FATIH',
'BAKIRKOY', 'ATASEHIR'
]
]
],
'weight' => 2
]
],
'boost_mode' => 'multiply'
]
]
]
];
} else {
$searchTerm = $this->convertToSearchableText($query);
// İl/şehir araması için parametreler
$cityParams = [
'index' => $this->indexName,
'body' => [
'size' => 3,
'query' => [
'bool' => [
'must' => [
[
'match' => [
'city_name' => [
'query' => $searchTerm,
'boost' => 4
]
]
]
]
]
],
'collapse' => [
'field' => 'city_name.raw'
]
]
];
// İlçe araması için parametreler
$districtParams = [
'index' => $this->indexName,
'body' => [
'size' => 7,
'query' => [
'bool' => [
'should' => [
[
'match_phrase_prefix' => [
'district_name' => [
'query' => $searchTerm,
'boost' => 3
]
]
],
[
'match' => [
'district_name' => [
'query' => $searchTerm,
'fuzziness' => 'AUTO',
'boost' => 2
]
]
]
],
'minimum_should_match' => 1
]
],
'collapse' => [
'field' => 'district_name.raw'
]
]
];
// Mahalle araması için parametreler
$neighborhoodParams = [
'index' => $this->indexName,
'body' => [
'size' => 40,
'query' => [
'function_score' => [
'query' => [
'bool' => [
'should' => [
[
'multi_match' => [
'query' => $searchTerm,
'fields' => [
'neighborhood_name^3',
'district_name^2',
'city_name^4',
'full_text'
],
'type' => 'best_fields',
'fuzziness' => 'AUTO',
'prefix_length' => 2
]
],
[
'match_phrase_prefix' => [
'full_text' => [
'query' => $searchTerm,
'boost' => 5
]
]
]
],
'minimum_should_match' => 1
]
],
'functions' => [
[
'filter' => ['term' => ['city_name.raw' => 'ISTANBUL']],
'weight' => 1000
],
[
'filter' => ['term' => ['city_name.raw' => 'ANKARA']],
'weight' => 800
],
[
'filter' => ['term' => ['city_name.raw' => 'IZMIR']],
'weight' => 600
],
[
'filter' => [
'terms' => ['district_name.raw' => [
'KADIKOY', 'BESIKTAS', 'SISLI', 'USKUDAR', 'BEYOGLU',
'ÇANKAYA', 'KEÇIÖREN', 'MAMAK', 'YENIMAHALLE'
]]
],
'weight' => 2
]
],
'score_mode' => 'sum',
'boost_mode' => 'multiply'
]
],
'sort' => [
'_score' => ['order' => 'desc']
]
]
];
$results = [];
// İl araması sonuçları
$cityResponse = $this->client->search($cityParams);
foreach ($cityResponse['hits']['hits'] as $hit) {
$source = $hit['_source'];
$results[] = [
'id' => "city:{$source['city_id']}",
'text' => "{$source['city_name']}",
'data' => [
'province_id' => $source['city_id'],
'province_name' => $source['city_name']
]
];
}
// İlçe araması sonuçları
$districtResponse = $this->client->search($districtParams);
foreach ($districtResponse['hits']['hits'] as $hit) {
$source = $hit['_source'];
$results[] = [
'id' => "district:{$source['district_id']}",
'text' => "{$source['district_name']} / {$source['city_name']}",
'data' => [
'province_id' => $source['city_id'],
'province_name' => $source['city_name'],
'district_id' => $source['district_id'],
'district_name' => $source['district_name']
]
];
}
// Mahalle araması sonuçları
$neighborhoodResponse = $this->client->search($neighborhoodParams);
foreach ($neighborhoodResponse['hits']['hits'] as $hit) {
$source = $hit['_source'];
$results[] = [
'id' => "neighborhood:{$source['id']}",
'text' => mb_strtoupper($source['full_text']),
'data' => [
'province_id' => $source['city_id'],
'province_name' => $source['city_name'],
'district_id' => $source['district_id'],
'district_name' => $source['district_name'],
'neighborhood_id' => $source['id'],
'neighborhood_name' => $source['neighborhood_name'],
'zip_code' => $source['zip_code']
]
];
}
return $results;
}
}