es에 데이터도 저장하고~ 간단한 검색도 하고~

spring에서 사용해야 하는데...

어찌 좀 편하게 방법이 없을까... 고민하던중...

 

spring.io 에 보면 어지간한 저장소와 연결할 수 있는 spring-data 프로젝트가 있더군요!

 

음...그래서 이것저것 막 삽질해보는데...뭐가 버전이 안맞고 안되고..어휴 ㅠㅠ

maven에서 jar 참조하는 버전을 변경하고 해보는데도..안되고... 어휴 ㅠㅠ

 

es에 비해 버전업이 너무 느려 ㅠㅠ

es 버전을 7.1.1을 사용하는데...

 

결국 대강 JPA 흉내라도 내보자는 심산으로...만들어 봐야겠다~~

 

결심

 

일단 PO인 도메인(이하 Docu)부터 생성...


@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@lombok.ToString
@Document(indexName="zipcode")
public class DocuZipcode implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -3670872846932659171L;

	@Id
	public String buildingControlNumber;
	
	public String lawTownCode;
	
	public String city;
	
	public String ward;
	
	public String town;
	
	public String lee;
	
	public String isMountaion;
	
	public String jibun;
	
	public String jibunSub;
	
	public String roadCode;
	
	public String road;
	
	public String isUnderground;
	
	public String buildingNumber;
	
	public String subBuildingNumber;
	
	public String buildingName;
	
	public String buildingNameDetail;
	
	public String dongNumber;
	
	public String dongCode;
	
	public String dongName;
	
	public String zipcode;
	
	public String zipcodeNumber;
	
	public String massDlvr;
	
	public String moveReasonCode;
	
	public String stdnDate;
	
	public String preRoadName;
	
	public String cityBuildingName;
	
	public String isPublic;
	
	public String newZipCode;
	
	public String idDetailAddr;
	
	public String etc1;
	
	public String etc2;
	
	public String message;
	
	public String path;
	
	public String host;
	
}

 

public으로 만든 이유는 나중에 접근하기 위해서...

private으로 해도 접근 하는 방식이 있는 걸로 아는데...찾아보기도 귀찬고..ㅠ

 

@Document 라는 어노테이션은

대강 만들었어요!

클래스 이름을 인덱스 이름으로 사용 할 수는 없기에...

 

내용을 보자면..

@Target(TYPE)
@Retention(RUNTIME)
public @interface Document {
	String indexName() default "";
	String typeName() default "_doc";
}

 

ES 6부터는 어차피 인덱스 하나당 타입이 하나고, 해당 타입명은 기본으로 _doc을 사용하기에...

 

 

그리고 입력 삭제 검색 등 기본적인 기능을 다 때려박은 클래스 하나 생성..

public abstract class AbstractRepo <PK extends Serializable, T>{
	
	@Autowired
	private Client client;
	
	private final Class<T> persistentClass;
	
	protected Client getClient() {
		return client;
	}
	
	public AbstractRepo(){
		this.persistentClass =(Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
	}
	
	public UpdateByQueryRequestBuilder updateByQuery() {
		String index = getIndexName(persistentClass);
		UpdateByQueryRequestBuilder updateByQuery =
				  new UpdateByQueryRequestBuilder(client, UpdateByQueryAction.INSTANCE);
		updateByQuery.source(index);
				
		return updateByQuery;
	}
	
	public SearchRequestBuilder searchRequestBuilder() {
		String index = getIndexName(persistentClass);
		SearchRequestBuilder reqBuilder = client.prepareSearch(index)
		.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
		
		return reqBuilder;
	}
	
	public List<T> fullTextSearch(String text) {
		
		List<T> ts = new ArrayList<>();
		
		String index = getIndexName(persistentClass);
		SearchResponse response = client.prepareSearch(index)
		.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
		.setQuery(QueryBuilders.matchQuery("fullTextSearch", text)).get();
		
		SearchHit[] hits = response.getHits().getHits();
		for(SearchHit hit : hits) {
			String jsonString = hit.getSourceAsString();
			ObjectMapper mapper = new ObjectMapper();
			try {
				T t = mapper.readValue(jsonString, persistentClass);
				ts.add(t);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return ts;
	}
	
	public IndexRequestBuilder builder(T t, String routing) {
		String index = getIndexName(persistentClass);
		String type = getTypeName(persistentClass);
		String id = getId(t);
		
		if(id == null) {
			throw new ServiceException("E", "id is null");
		}
		
		IndexRequestBuilder indexRequestBuilder = null;
		
		try {
			ObjectMapper mapper = new ObjectMapper();
			String jsonString;
			jsonString = mapper.writeValueAsString(t);
			
			indexRequestBuilder = client.prepareIndex(index, type, id)
					.setSource(jsonString, XContentType.JSON);
			if(!StringUtil.isBlank(routing)) {
				indexRequestBuilder.setRouting(routing);
			}
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		
		return indexRequestBuilder;
	}
	
	public IndexResponse index(T t, String routing) {
		
		IndexResponse response = null;
		
		try {
			IndexRequestBuilder indexRequestBuilder = this.builder(t, routing);
			response = indexRequestBuilder.get();
		} catch (Exception e) {
			throw new ServiceException("E", "인덱스 생성에 실패하였습니다");
		}
		
			
		return response;
	}
	
	public BulkRequestBuilder bulkRequest() {
		BulkRequestBuilder bulkRequest = client.prepareBulk();
		return bulkRequest;
	}
	
	public BulkResponse bulk(List<T> ts) {
		
		BulkRequestBuilder bulkRequest = client.prepareBulk();

		for(T t : ts) {
			bulkRequest.add(this.builder(t, null));
		}
		
		BulkResponse bulkResponse = bulkRequest.get();
		
		if (bulkResponse.hasFailures()) {
			throw new ServiceException("E", "bulk failure");
		}
		
		return bulkResponse;
	}
	
	public void update(T t) {
		
		String index = getIndexName(persistentClass);
		String type = getTypeName(persistentClass);
		String id = getId(t);
		
		if(id == null) {
			throw new ServiceException("E", "id is null");
		}
		
		try {
			ObjectMapper mapper = new ObjectMapper();
			String jsonString;
			jsonString = mapper.writeValueAsString(t);
		
			UpdateResponse updateResponse = client.prepareUpdate(index, type, id)
					.setDoc(jsonString, XContentType.JSON).get();
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		
	}
	
	public T get(PK id, String routing) {

		String index = getIndexName(persistentClass);
		String type = getTypeName(persistentClass);
		
		GetRequestBuilder request = client.prepareGet(index, type, id.toString());
		
		if(!StringUtil.isBlank(routing)) {
			request.setRouting(routing);
		}
		
		GetResponse response = request.get();
		
		String jsonString = response.getSourceAsString();
		
		if(jsonString == null) {
			return (T)null;
		}
		
		ObjectMapper mapper = new ObjectMapper();
		try {
			T t = mapper.readValue(jsonString, persistentClass);
			return t;
		} catch (IOException e) {
			return (T)null;
		}
		
	}
	
	protected String getId(T t) {
		
		Field[] fields = t.getClass().getDeclaredFields();
		for(Field f : fields) {
			if(f.isAnnotationPresent(Id.class)) {
				
				Object obj;
				try {
					obj = f.get(t);
					return obj==null?null:obj.toString();
				} catch (IllegalArgumentException | IllegalAccessException e) {
					// field를 접근성을 위해 public으로 선언 했으며, 없는게 나올수가 없으므로 위 exception은 발생이 불가능 한거 아닌가... 
					e.printStackTrace();
				}
			}
		}
		
		return null;
	}
	
	protected String getIndexName(Class<T> clazz) {
		
		Document document = clazz.getAnnotation(Document.class);
		return document.indexName();
	}
	
	protected String getTypeName(Class<T> clazz) {
		
		Document document = clazz.getAnnotation(Document.class);
		return document.typeName();
	}
}

 

각각의 Exception처리는 뭐 편하신대로 하시면 더 좋을 거 같아요. 너무 대충해서..

 

참고로 @Id라는 어노테이션은 javax.persistence.Id 입니다.

해당 어노테이션이 있는 필드의 값을 도큐먼트의 _id로 사용하고자 이렇게 하였어요!

 

물론 그걸 알기 위해 위에 클래스에서 reflect.Field를 통한 접근도 하고있죠.

이래서 Docu에서 필드의 접근자를 public을 사용했고요.

 

그럼 이제 각 Docu별로 ES에 접근하기 위한 레파지토리를 만들어 보도록 할게요!

 

@Repository("docuZipcodeRepository")
public class DocuZipcodeRepository extends AbstractRepo<String, DocuZipcode>{
	
	
}

 

내용은~ 

별게 없어요.

이제 이 안의 내용은 사용에 따라 채우시면 될거 같아요.

this.searchRequestBuilder(); 를 사용해서 검색빌더를 생성하고 검색을 하고..

Hits 결과를 문자열로 받아서 오브젝트 매퍼를 이용하여 원하는 클래스로 변환하고...

 

여기서 더 공과 시간을 들이면 편해질지 모르겠으나, 일단 이정도에서 만족하며...

저는 그럼 일을 하도록 하겠습니다~ ㅋㅋㅋ

 

일을 위한 일을 하느라 시간을 쓸데 없이 많이 쓴듯...

+ Recent posts