Skip to content

Commit

Permalink
Merge pull request #7 from Nexters/feature/spot_search
Browse files Browse the repository at this point in the history
Feature/spot search
  • Loading branch information
minkj1992 authored Jan 22, 2021
2 parents 67c61cd + a2835c9 commit 0284999
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 33 deletions.
5 changes: 4 additions & 1 deletion src/place/kakaoMapSearch/search.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ registerEnumType(SortType, {
});

// https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword
@InputType()
@InputType({
description:
"SortType은 정확도(accuracy)가 기본이며, 거리순(distance) 정렬을 원할 경우 x,y는 필수 입니다.",
})
export class KeywordSearchDto {
@Field(() => String)
query: string;
Expand Down
4 changes: 3 additions & 1 deletion src/place/place.entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Field, ObjectType, Float } from "@nestjs/graphql";

// https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-keyword
@ObjectType()
@ObjectType({
description: "카카오 지도 api로 부터 받은 위치 정보로 (TTL 300 cache)",
})
export class Place {
@Field(() => String, { description: "kakao place id" })
id: string;
Expand Down
7 changes: 5 additions & 2 deletions src/place/place.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import { KeywordSearchDto } from "./kakaoMapSearch/search.dto";
export class PlaceResolver {
constructor(private readonly searchService: SearchService) {}

@Query(() => [Place])
async placesByKeyword(
@Query(() => [Place], {
description:
"키워드로 위치 정보를 확인합니다. \n내부적으로 카카오 API를 요청합니다.",
})
async getPlacesByKeyword(
@Args("filters") filters: KeywordSearchDto
): Promise<object> {
const places: Place[] = await this.searchService.searchByKeyword(filters);
Expand Down
26 changes: 23 additions & 3 deletions src/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------

"""Emoji를 포함한 유저데이터를 포함하여, mongodb에 저장시킬 장소 데이터"""
type Spot {
"""kakao place id"""
id: String!
Expand All @@ -17,11 +18,11 @@ type Spot {
road_address_name: String
place_url: String
distance: String
location: String!
x: Float
y: Float
}

"""카카오 지도 api로 부터 받은 위치 정보로 (TTL 300 cache)"""
type Place {
"""kakao place id"""
id: String!
Expand All @@ -45,11 +46,21 @@ type DeleteSpotDto {
}

type Query {
findSpots: [Spot!]!
placesByKeyword(filters: KeywordSearchDto!): [Place!]!
"""(For Debugging) mongoDB에 저장된 모든 스팟 반환"""
getAllSpots: [Spot!]!

"""검색 키워드와 매칭되는 스팟들을 반환"""
getSpotsByKeyword(keyword: String!): [Spot!]!

"""
키워드로 위치 정보를 확인합니다.
내부적으로 카카오 API를 요청합니다.
"""
getPlacesByKeyword(filters: KeywordSearchDto!): [Place!]!
getPlaceFromCache(placeId: String!): Place!
}

"""SortType은 정확도(accuracy)가 기본이며, 거리순(distance) 정렬을 원할 경우 x,y는 필수 입니다."""
input KeywordSearchDto {
query: String!
category_group_code: String
Expand All @@ -68,8 +79,17 @@ enum SortType {
}

type Mutation {
"""
스팟을 생성/업데이트합니다.
기존에 누군가에 의해서 만들어졌다면 update됩니다.
"""
createSpot(createSpotInput: CreateSpotInput!): Spot!

"""(For Debugging) 스팟 하나 삭제"""
removeSpot(id: String!): DeleteSpotDto!

"""(For Debugging) 스팟 모두 삭제"""
removeAllSpots: DeleteSpotDto!
}

input CreateSpotInput {
Expand Down
12 changes: 7 additions & 5 deletions src/spot/entities/spot.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@ import { ObjectType, Field, ID, Int, Float } from "@nestjs/graphql";
import * as mongoose from "mongoose";
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";

@ObjectType()
@ObjectType({
description:
"Emoji를 포함한 유저데이터를 포함하여, mongodb에 저장시킬 장소 데이터",
})
@Schema({ timestamps: true }) // graphql 은 timestamp 삽입 어떻게 할까?
export class Spot {
@Prop({ required: true, unique: true })
@Field(() => String, { description: "kakao place id" })
@Prop({ required: true, unique: true })
id: string;

@Prop({ required: true })
@Field((type) => [String], { description: "list of emoji ids" })
@Prop({ required: true })
emojis: string[];

@Prop({ required: true })
@Field(() => String)
@Prop({ required: true, text: true }) // {text : true} for indexing
place_name: string;

@Field(() => String, { nullable: true })
Expand Down Expand Up @@ -49,7 +52,6 @@ export class Spot {
@Prop()
distance?: string;

@Field(() => String)
@Prop({
type: {
type: String,
Expand Down
45 changes: 28 additions & 17 deletions src/spot/spot.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { DeleteSpotDto } from "src/spot/dto/delete.spot.dto";
export class SpotResolver {
constructor(private readonly spotService: SpotService) {}

@Mutation(() => Spot)
@Mutation(() => Spot, {
description:
"스팟을 생성/업데이트합니다.\n기존에 누군가에 의해서 만들어졌다면 update됩니다.",
})
async createSpot(
@Args("createSpotInput") createSpotInput: CreateSpotInput
): Promise<Spot> {
Expand All @@ -22,31 +25,39 @@ export class SpotResolver {
}
}

@Query(() => [Spot])
async findSpots() {
@Query(() => [Spot], {
description: "(For Debugging) mongoDB에 저장된 모든 스팟 반환",
})
async getAllSpots(): Promise<Spot[]> {
return await this.spotService.findAll();
}

// @Query(() => [Spot])
// async getSpots(
// @Args("x", { type: () => Float }) x: number,
// @Args("y", { type: () => Float }) y: number
// ) {
// return await this.spotService.getSpot(x, y);
// }

// @Query(() => Spot, { name: "spot" })
// async findOne(@Args("id", { type: () => Int }) id: number) {
// return this.spotService.findOne(id);
// }

// @Mutation(() => Spot)
// async updateSpot(@Args("updateSpotInput") updateSpotInput: UpdateSpotInput) {
// return this.spotService.update(updateSpotInput.id, updateSpotInput);
// }

@Mutation(() => DeleteSpotDto)
@Mutation(() => DeleteSpotDto, {
description: "(For Debugging) 스팟 하나 삭제",
})
async removeSpot(@Args("id", { type: () => String }) id: string) {
return await this.spotService.remove(id);
}

@Mutation(() => DeleteSpotDto, {
description: "(For Debugging) 스팟 모두 삭제",
})
async removeAllSpots(): Promise<void> {
await this.spotService.removeAll();
}

@Query(() => [Spot], { description: "검색 키워드와 매칭되는 스팟들을 반환" })
async getSpotsByKeyword(@Args("keyword") keyword: string): Promise<Spot[]> {
return await this.spotService.getByKeyword(keyword);
}

// @Query(() => Spot, { name: "spot" })
// async findOne(@Args("id", { type: () => Int }) id: number) {
// return this.spotService.findOne(id);
// }
}
32 changes: 28 additions & 4 deletions src/spot/spot.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable } from "@nestjs/common";
import { Injectable, HttpException, HttpStatus } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import { Model, Types } from "mongoose";
import { SearchService } from "src/place/kakaoMapSearch/search.service";
Expand Down Expand Up @@ -57,7 +57,13 @@ export class SpotService {
}

async findAll(): Promise<Spot[]> {
return this.spotModel.find().exec();
return this.spotModel
.find()
.exec()
.catch((err) => {
console.error(err);
throw new HttpException("bad request", HttpStatus.BAD_REQUEST);
});
}

async findOne(id: string) {
Expand All @@ -72,7 +78,25 @@ export class SpotService {
return this.spotModel.remove({ id: id }).exec();
}

async getSpot(cx: number, cy: number) {
return this.spotModel;
async getByKeyword(keyword: string): Promise<Spot[]> {
/*
mongodb 한국어 쿼리 참고자료
- https://ip99202.github.io/posts/nodejs,-mongodb-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EA%B2%80%EC%83%89-%EA%B8%B0%EB%8A%A5/
- https://github.com/Tekiter/EZSET/blob/master/backend/src/api/v1/simple.route.js
*/
return this.spotModel
.find({ place_name: new RegExp(keyword) })
.exec()
.catch((err) => {
console.error(err);
throw new HttpException(
"There are no spots that matched with keyword.",
HttpStatus.BAD_REQUEST
);
});
}

async removeAll() {
// remove all spots for cleanup
}
}

0 comments on commit 0284999

Please sign in to comment.