Redis

[ Redis ] Radius/Polygon Geospatial queries With FT.SEARCH

dev-byul 2024. 4. 18. 08:51

사전 주의사항

FT.SEARCH 를 사용하기 위해서는 RedisSearch Module이 사전에 설치되어있어야 가능합니다.

Redis에서는 RedisStack을 이용하는 방법을 추천하고 있습니다.

별도 설치 혹은 Redis Cloud를 사용하는 방법이 있습니다.

별도 설치 중 Docker를 이용하여 설치하는 방법은 제가 작성한 블로그를 참고 하셔도 좋습니다.

Data Insert

Index설정과 데이터 조회를 하기 전 지리정보 데이터를 갖진 데이터를 작성합니다.

위도 값은 20으로 고정한채 경도값을 약 1km 너비로 하여 데이터를 생성 했습니다.

HSET geo_table:1 geo "126.0,20" geo_shape "POINT(126.0 20)"
HSET geo_table:2 geo "126.0113,20" geo_shape "POINT(126.0113 20)"
HSET geo_table:3 geo "126.0225,20" geo_shape "POINT(126.0225 20)"
HSET geo_table:4 geo "126.0338,20" geo_shape "POINT(126.0338 20)"
HSET geo_table:5 geo "126.0451,20" geo_shape "POINT(126.0451 20)"
HSET geo_table:6 geo "126.0563,20" geo_shape "POINT(126.0563 20)"
HSET geo_table:7 geo "126.0676,20" geo_shape "POINT(126.0676 20)"
HSET geo_table:8 geo "126.0789,20" geo_shape "POINT(126.0789 20)"
HSET geo_table:9 geo "126.0902,20" geo_shape "POINT(126.0902 20)"
HSET geo_table:10 geo "126.1014,20" geo_shape "POINT(126.1014 20)"
HSET geo_table:11 geo "126.1127,20" geo_shape "POINT(126.1127 20)"
HSET geo_table:12 geo "126.1239,20" geo_shape "POINT(126.1239 20)"
HSET geo_table:13 geo "126.1352,20" geo_shape "POINT(126.1352 20)"
HSET geo_table:14 geo "126.1465,20" geo_shape "POINT(126.1465 20)"
HSET geo_table:15 geo "126.1578,20" geo_shape "POINT(126.1578 20)"
HSET geo_table:16 geo "126.1690,20" geo_shape "POINT(126.1690 20)"
HSET geo_table:17 geo "126.1803,20" geo_shape "POINT(126.1803 20)"
HSET geo_table:18 geo "126.1916,20" geo_shape "POINT(126.1916 20)"
HSET geo_table:19 geo "126.2028,20" geo_shape "POINT(126.2028 20)"
HSET geo_table:20 geo "126.2141,20" geo_shape "POINT(126.2141 20)"

 

더보기

해당 위도 경도 값을 openlayer를 통해 표시 할 경우 다음과 같이 표시 됩니다.

const coordinates = [[126.0, 20],[126.0113, 20],[126.0225, 20],[126.0338, 20],[126.0451, 20],[126.0563, 20],[126.0676, 20],[126.0789, 20],[126.0902, 20],[126.1014, 20],[126.1127, 20],[126.1239, 20],[126.1352, 20],[126.1465, 20],[126.1578, 20],[126.169, 20],[126.1803, 20],[126.1916, 20],[126.2028, 20],[126.2141, 20]];

const features: Feature<Point>[] = [];
coordinates.forEach((cod) => {
	const coordinate = fromLonLat([cod[0], cod[1]]);
	const point = new Point(coordinate);
	const feature = new Feature(point);

	features.push(feature);
});

const vectorSource = new VectorSource({
	features: features,
});

const pointsLayer = new VectorLayer({
	source: vectorSource,
});

baseMap.addLayer(pointsLayer);

Indexing

인덱스는 FT.CREATE 를 통해 생성이 가능합니다. Redis 에서는 다음과 같이 Syntax를 정의 했습니다.

해당 글에서는 ON HASH 형태의 데이터를 인덱싱 할 예정입니다. 이에 JSON 형태의 인덱싱 기법은 링크(JSON Indexing)에 가서 보다 자세히 확인해주시길 바랍니다.

FT.CREATE index [ON HASH | JSON] [PREFIX count prefix [prefix ...]]  [FILTER {filter}] [LANGUAGE default_lang] [LANGUAGE_FIELD lang_attribute]  [SCORE default_score] [SCORE_FIELD score_attribute] [PAYLOAD_FIELD payload_attribute] [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS]  [NOHL] [NOFIELDS] [NOFREQS] [STOPWORDS count [stopword ...]]   [SKIPINITIALSCAN] SCHEMA field_name [AS alias] TEXT | TAG | NUMERIC | GEO | VECTOR | GEOSHAPE [ SORTABLE [UNF]] [NOINDEX] [ field_name [AS alias] TEXT | TAG | NUMERIC | GEO | VECTOR | GEOSHAPE [ SORTABLE [UNF]] [NOINDEX] ...]

지리 정보 데이터를 조회 하기 위해서는 다음과 같은 필드 타입으로 작성해야 합니다.

  • GEO
    • Radius 검색 기능을 지원합니다.
    • 속성 값은 쉼표로 구분된 경도, 위도를 포함하는 문자열이어야 합니다.
FT.CREATE idx:geo ON HASH PREFIX 1 "geo_table:" SCHEMA geo GEO
  • GEOSHAPE
    • Radius와 다각형(Polygon) 검색 기능을 지원합니다.
    • 속성 값은 쉼표로 구분된 다각형 가장자리를 나타내는 2D 점의 WTK 표기법 목록을 따라야 합니다.
      • WKT 표기법 : POLYGON((x1 y1, x2 y2, x3 y3 ... ))
    • 좌표계는 SPHERICAL, FLAT 가 존재합니다.
      • SPHERICAL : 지리적 경도 및 위도의 좌표계
      • FLAT : 데카르트 XY 좌표계
FT.CREATE idx:geoshape ON HASH PREFIX 1 "geo_table:" SCHEMA geo_shape GEOSHAPE SPHERICAL

 

작성한 인덱스를 삭제하는 방법은 다음과 같다.

FT.DROPINDEX index [DD]

Radius Search

좌표(경도, 위도)를 중심으로 원형 형태로 데이터를 검색할 수 있습니다.

FT.SEARCH index "@geo_field:[lon lat radius unit]"
  • lon : 경도
  • lat : 위도
  • radius : 반지름
  • unit : 반지름의 단위 [ m, km, mi, ft ]

현재 작성된 데이터와 인덱스 기반으로 검색 쿼리를 다음과 같이 작성 할 수 있습니다.

FT.SEARCH idx:geo "@geo:[126 20 10 km]"

Shape Search

Shape Search Syntax는 다음과 같습니다.

FT.SEARCH index "@geo_shape_field:[{WITHIN|CONTAINS} $shape] PARAMS 2 shape "shape_as_wkt" DIALECT 3
  • @geo_shape_field : 인덱싱으로 선택된 필드 중 질의할 필드명
  • WITHIN | CONTAINS
    • WITHIN : 지정된 형태 내부에 있는 데이터를 질의
    • CONTAINS : 지정된 형태가 포함하는 데이터를 질의
  • PARAMS : 쿼리에 사용될 매개 변수의 수와 값을 정의
  • DIALECT : 사용할 쿼리 언어의 버전을 지정 DIALECT 3 은 RedisSearch의 쿼리 언어 버전 3을 사용함을 나타냅니다.

 

검색 쿼리를 다음과 같이 질의를 했습니다.

FT.SEARCH idx:geoshape "@geo_shape:[within $poly]" PARAMS 2 poly "POLYGON((126.1 19.99, 126.1 20.01, 126.2 20.01, 126.2 19.99, 126.1 19.99))" DIALECT 3

 

총 9개의 데이터가 출력이 되었으며, 10 ~ 18번까지의 데이터가 출력 됨을 확인 할 수 있습니다.

더보기

질의한 범위는 openlayer를 통해 표시 할 경우 다음과 같이 표시 됩니다.

const polygonCoordinates = [[126.1, 19.99],[126.1, 20.01],[126.2, 20.01],[126.2, 19.99],[126.1, 19.99]];

const polygon = new Polygon([
	polygonCoordinates.map((coord) => fromLonLat(coord)),
]);

const polygonFeature = new Feature(polygon);

const polygonVecotrSource = new VectorSource({
	features: [polygonFeature],
});

const polygonLayer = new VectorLayer({ source: polygonVecotrSource });

baseMap.addLayer(polygonLayer);

'Redis' 카테고리의 다른 글

[ Redis ] RedisStack With.Docker  (0) 2024.04.16