feat: update specialty URL generation logic and improve indexing strategy for specialties
This commit is contained in:
+15
-29
@@ -158,35 +158,21 @@ async function getClinicUrls(baseUrl, scope) {
|
|||||||
|
|
||||||
// صفحات فرود تخصص per-domain — روی هر دامنه «تخصص + آن شهر» است، پس همهجا self.
|
// صفحات فرود تخصص per-domain — روی هر دامنه «تخصص + آن شهر» است، پس همهجا self.
|
||||||
//
|
//
|
||||||
// فقط تخصصهایی که در همین دامنه واقعاً پزشک دارند اعلام میشوند؛ تولید بیقید هر ۹۳
|
// هر تخصص ایندکسپذیر روی هر دامنه اعلام میشود، مستقل از اینکه امروز در آن شهر پزشک
|
||||||
// تخصص برای هر ۳۵ دامنه هزاران صفحهٔ خالی به گوگل معرفی میکرد (thin content انبوه).
|
// دارد یا نه — همان سیاستی که صفحه اعمال میکند. اگر sitemap به تعداد پزشکان گره
|
||||||
|
// میخورد، صفحهٔ ایندکسپذیرِ اعلامنشده میساختیم و با هر افت و خیز داده، URLها از
|
||||||
|
// sitemap در میآمدند و برمیگشتند.
|
||||||
//
|
//
|
||||||
// منبع، همان پزشکانی است که این sitemap منتشرشان میکند — نه endpoint شمارش. آن
|
// دستههای سطحبالا (lib/specialtyIndexing) noindex هستند؛ اعلامشان سیگنال متناقض
|
||||||
// endpoint با فیلتر لیست پزشکان اختلاف دارد (مثلاً پزشکِ یافتشده از آدرس کلینیک را
|
// به گوگل میدهد، پس منتشر نمیشوند.
|
||||||
// نمیشمارد)، و اگر sitemap از یک منبع و خودِ صفحه از منبع دیگر تصمیم بگیرد، صفحهٔ
|
function getSpecialtyUrls(baseUrl) {
|
||||||
// ایندکسپذیرِ اعلامنشده یا برعکس میسازیم. اینطور هیچ فراخوانی اضافهای هم ندارد.
|
return specialtiesData
|
||||||
function getSpecialtyUrls(baseUrl, doctors) {
|
.filter((s) => s?.slug && s.status === 1 && isIndexableSpecialty(s))
|
||||||
// والدها noindex هستند (lib/specialtyIndexing)؛ اعلامشان در sitemap سیگنال
|
.map((s) => ({
|
||||||
// متناقض به گوگل میدهد، پس اصلاً منتشر نمیشوند.
|
url: `${baseUrl}/specialties/${s.slug}`,
|
||||||
const slugById = new Map(
|
changeFrequency: 'weekly',
|
||||||
specialtiesData
|
priority: 0.8,
|
||||||
.filter((s) => s?.slug && s.status === 1 && isIndexableSpecialty(s))
|
}));
|
||||||
.map((s) => [String(s.id), s.slug])
|
|
||||||
);
|
|
||||||
|
|
||||||
const slugs = new Set();
|
|
||||||
for (const doctor of doctors) {
|
|
||||||
for (const specialty of doctor?.specialties ?? []) {
|
|
||||||
const slug = slugById.get(String(specialty?.id));
|
|
||||||
if (slug) slugs.add(slug);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [...slugs].map((slug) => ({
|
|
||||||
url: `${baseUrl}/specialties/${slug}`,
|
|
||||||
changeFrequency: 'weekly',
|
|
||||||
priority: 0.8,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// هر پست فقط در sitemap دامنهٔ canonical خودش: پست شهریافته → دامنهٔ آن شهر،
|
// هر پست فقط در sitemap دامنهٔ canonical خودش: پست شهریافته → دامنهٔ آن شهر،
|
||||||
@@ -231,7 +217,7 @@ export default async function sitemap() {
|
|||||||
|
|
||||||
const allUrls = [
|
const allUrls = [
|
||||||
...getStaticPages(baseUrl),
|
...getStaticPages(baseUrl),
|
||||||
...getSpecialtyUrls(baseUrl, doctors),
|
...getSpecialtyUrls(baseUrl),
|
||||||
...doctorUrls,
|
...doctorUrls,
|
||||||
...clinicUrls,
|
...clinicUrls,
|
||||||
...blogUrls,
|
...blogUrls,
|
||||||
|
|||||||
@@ -54,14 +54,13 @@ export async function generateMetadata({ params }) {
|
|||||||
const description = `لیست بهترین پزشکان ${specialty.name} در ${cityName} همراه با نشانی مطب، ساعات کاری و امتیاز بیماران. رزرو نوبت آنلاین متخصص ${specialty.name}.`;
|
const description = `لیست بهترین پزشکان ${specialty.name} در ${cityName} همراه با نشانی مطب، ساعات کاری و امتیاز بیماران. رزرو نوبت آنلاین متخصص ${specialty.name}.`;
|
||||||
const image = "/assets/images/og-image.png";
|
const image = "/assets/images/og-image.png";
|
||||||
|
|
||||||
// تخصصی که در این شهر پزشکی ندارد فقط یک عنوان و متن قالبی است — thin content.
|
// ایندکس به تعداد پزشکان شهر گره نمیخورد: صفحه روی هر دامنه متن مقدمه و FAQ یکتای
|
||||||
// ایندکس نمیشود (و در sitemap هم نمیآید)، ولی follow میماند تا لینکهای
|
// خودش را دارد (buildSpecialtyIntro با هشِ تخصص+شهر واگرا میشود) و شهری که امروز
|
||||||
// داخلیاش دنبال شوند. بهمحض افزودهشدن اولین پزشک، خودکار ایندکسپذیر میشود.
|
// پزشک ندارد فردا دارد — نوسان ایندکس بدتر از صفحهٔ کمپزشک است.
|
||||||
const { total } = await getDoctors(specialty.id, isRoot ? null : matchedCity?.id);
|
//
|
||||||
|
// تنها استثنا دستهٔ سطحبالاست (دارای زیرشاخهٔ فعال): محتوای مستقلی ندارد و با
|
||||||
// دستهٔ سطحبالا (دارای زیرشاخهٔ فعال) محتوای مستقلی ندارد و با فرزندانش روی همان
|
// فرزندانش روی همان کوئری رقابت میکند؛ صفحهٔ قابلایندکس، همان زیرشاخه است.
|
||||||
// کوئری رقابت میکند؛ صفحهٔ قابلایندکس، همان زیرشاخه است.
|
const noindex = isCategorySpecialty(specialty);
|
||||||
const noindex = isCategorySpecialty(specialty) || total === 0;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title,
|
title,
|
||||||
|
|||||||
Reference in New Issue
Block a user