PostGIS va vektorlar
Qo‘lda yozilgan xaritada chiziladigan va tahrirlanadigan sakkiz xil geometriya turi, metr bilan gradusni farqlaydigan fazoviy filtrlar va pgvector orqali o‘xshashlik bo‘yicha qidiruv.
Mavjud v0.2.0
FastFort PostGIS va pgvector ustunlarini spec qatlamida GeoAlchemy2 yoki pgvector’ni import qilmasdan aniqlaydi. Fazoviy ma’lumoti bo‘lmagan loyiha fazoviy qo‘llab-quvvatlashning mavjudligi uchun hech narsa to‘lamaydi; fazoviy ma’lumoti bor loyihaga esa bazaning kengaytmasidan boshqa hech qanday tizim paketi kerak emas.
Geometriya ustunlari
from geoalchemy2 import Geography, Geometry, WKBElement
class Hub(Base):
location: Mapped[WKBElement | None] = mapped_column(
Geography(geometry_type="POINT", srid=4326), default=None
)
catchment: Mapped[WKBElement | None] = mapped_column(
Geometry(geometry_type="POLYGON", srid=4326), default=None
)
Barcha sakkiz tur chiziladi va tahrirlanadi: POINT, LINESTRING, POLYGON
(teshiklari bilan birga), MULTIPOINT, MULTILINESTRING, MULTIPOLYGON,
GEOMETRYCOLLECTION va turi ko‘rsatilmagan GEOMETRY.
Har bir maydon ostidagi format bo‘yicha maslahat ustunning o‘z kind’idan
tanlanadi — «kenglik va uzunlik» POINT ustida aynan to‘g‘ri, POLYGON
ustida esa chalg‘ituvchi bo‘ladi.
Xaritani yoqish
Standart holatda o‘chirilgan, va bu e’tibordan chetda qolgan narsa emas, balki qaror: uni yoqish admin boshqa birovning serveridan rasm yuklab olishi degani. Bu server qaysi qatorlar ko‘rilayotgani va ular taxminan qayerda ekanini biladi, aksariyat tayl xizmatlarida esa bu haqda shartlar bor. URL ko‘rsatish — loyihangizning ularni o‘qib chiqqani haqidagi bayonoti.
FastFortSettings(
ui={
"map_tile_url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
"map_attribution": "© OpenStreetMap contributors",
"map_center": "41.3111, 69.2797",
},
)
Bu server — adminning CSP’dagi img-src kengaytiriladigan yagona manba,
shu sababli bu sozlama, shablon qo‘sha oladigan narsa emas.
map_tile_url bo‘lmasa, maydon doim bo‘lib kelgan oddiy matn kiritish
maydonicha qoladi. Hech narsa buzilmaydi — orqasida shunchaki rasm yo‘q.
Bu qanchalik chuqurlashadi
ui={"map_max_zoom": 19} # the default
So‘raladigan eng chuqur daraja — bu vidjetning emas, tayl manbaining xususiyati. Serverda mavjud bo‘lgandan chuqurroq so‘rov 404 qaytaradi, muvaffaqiyatsiz tayl esa bo‘sh kvadrat bo‘ladi.
Ko‘rinish yana ikki daraja chuqurroq davom etadi va qo‘lidagi so‘nggi taylni masshtablaydi — har qanday xarita ilovasi o‘z tasvirlaridan tashqarida qanday ishlasa, xuddi shunday: javob berishni to‘xtatgan «+» tugmasi vidjet buzilganday tuyuladi, xaritaning tafsilotlari tugaganday emas.
Xarita nima ekani
Leaflet emas, qo‘lda yozilgan Web Mercator. 256 pikselli taylar transform
orqali joylashtiriladi, har bir masshtab darajasi uchun bitta qatlam bo‘lib,
shu tufayli masshtablash bir zumda sodir bo‘ladi va yangi taylar
yuklanmaguncha oldingi daraja pastda turaveradi.
U yonidagi matn maydonini almashtirmaydi, balki unga yozadi. JavaScript o‘chirilganda ham maydon avvalgidek WKT, EWKT yoki GeoJSON joylash mumkin bo‘lgan qutichaligicha qoladi, forma esa baribir yuboriladi — bu boshqa har qanday boshqaruv elementi amal qiladigan qoidaning aynan o‘zi.
Faqat noto‘g‘ri ketgandan keyingina ravshan bo‘ladigan ikkita tafsilot:
- Poligonning yopuvchi uchi birinchi uchiga bog‘langan. Birini surib, ikkinchisini surmasangiz, saqlashda halqa yorilib ochiladi.
- Xarita — uslub jadvalida mantiqiy emas, jismoniy yo‘nalishlarda yozilgan roppa-rosa ikkita joydan biri, chunki bu skript tomonidan jismoniy piksellarda joylashtiriladigan koordinatalar maydoni. (Ikkinchisi — checkbox belgisi: belgi qaysi tilda bo‘lmasin, belgiligicha qoladi.)
Geography — bu geometry emas
Ikkalasi ham bir xil maydon turiga kiradi va bir xil boshqaruv elementini
chizadi. Masofa geography uchun metr, geometry uchun esa SRID
birliklari — 4326 uchun gradus — degani.
/admin/logistics.vehicle/?last_seen__dwithin=41.3111,69.2797,5000
5000 metr, chunki Vehicle.last_seen — Geography. Xuddi shu filtr
Geometry ustunida 5000 ni gradus sifatida o‘qigan bo‘lardi, bu esa
sayyoraning yarmiga yaqin radius degani.
GeometrySpec.geography aynan shu ikkisi aralashib ketmasligi uchun mavjud,
so‘rov esa kerak bo‘lgan joyda turni o‘zgartiradi.
Fazoviy filtrlar
?catchment__within=POLYGON((69.2 41.3, 69.3 41.3, 69.3 41.2, 69.2 41.3))
?catchment__intersects=POINT(69.28 41.31)
?catchment__contains=POINT(69.28 41.31)
?catchment__bbox=69.1,41.2,69.4,41.4
?last_seen__dwithin=41.3111,69.2797,5000
Shuningdek overlaps, touches va crosses.
Qiymat forma foydalanadigan xuddi shu parser bilan tahlil qilinadi, shu sababli maydon qabul qiladigan har narsani filtr ham qabul qiladi.
Ro‘yxat katagida
Geometriya qisqacha ko‘rinishda chiziladi — Polygon · 14 points, Point · 41.31, 69.28 — sahifani ochgan odamga buzilgan ma’lumotdek ko‘rinadigan WKB
o‘n oltilik blob emas.
Eksport atayin boshqacha. Ro‘yxat katagi qisqacha ko‘rsatadi; faylni odam
kabi kamida shunchalik dastur ham o‘qiydi, import bergan va’da esa — bu
tizim yozgan faylni o‘zi qaytarib o‘qiy olishi. Shu sababli geometriya
o‘zining forma boshqaruv elementi ko‘rsatadigan xuddi shu matn sifatida
eksport qilinadi: nuqta — lat, lng sifatida, qolgan hammasi — EWKT
sifatida.
Vektorli qidiruv
from pgvector.sqlalchemy import Vector
class Product(Base):
embedding: Mapped[object | None] = mapped_column(Vector(3), default=None)
Adminda faqat o‘qish uchun. Embeddingni hech kim qo‘lda kiritmaydi; ustunni foydali qiladigan narsa — so‘rovning o‘zi:
?embedding__near=[0.4,0.1,0.9]&embedding__k=5
Besh eng yaqini, saralangan holda. Bu tartib sahifadan so‘ralgan boshqa har qanday narsadan ustun turadi — alifbo bo‘yicha emas, «eng yaqini, tenglik bo‘lsa nom bo‘yicha» tartibida, chunki natijalari keyin alifbo bo‘yicha qayta saralanadigan o‘xshashlik bo‘yicha qidiruv — bu endi o‘xshashlik bo‘yicha qidiruv emas.
?embedding__near=[...]&embedding__metric=cosine # default
?embedding__near=[...]&embedding__metric=l2
?embedding__near=[...]&embedding__metric=l1
?embedding__near=[...]&embedding__metric=inner
?embedding__near=[...]&embedding__within=0.35 # a distance bound
VectorSpec o‘lchamlar sonini va turini (vector, halfvec, sparsevec,
bit) o‘zida saqlaydi, shu sababli noto‘g‘ri uzunlikdagi qiymat baza xatosi
emas, validatsiya xatosi bo‘ladi.
Kengaytmalarni olish
FastFort hech qachon DDL ishga tushirmaydi. Kengaytmalarni yoqish loyihangizning vazifasi, va bu ataylab shunday: baza kengaytmalarini sezdirmay o‘rnatadigan admin freymvorkini hech kim tekshira olmaydi.
EXTENSIONS = ("postgis", "hstore", "citext", "vector", "pg_trgm", "btree_gist")
async def create_schema() -> None:
async with engine.begin() as connection:
if connection.dialect.name == "postgresql":
for extension in EXTENSIONS:
await connection.execute(
sa.text(f"CREATE EXTENSION IF NOT EXISTS {extension}")
)
await connection.run_sync(Base.metadata.create_all)
postgis, hstore, citext va vector — har biri o‘z turini qo‘shadi,
shu sababli ularning birini ishlatgan CREATE TABLE birinchi yozuvda emas,
DDL bosqichida muvaffaqiyatsiz tugaydi.