반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

JAVA - Find duplicate letters in a String

2022. 7. 26. 05:19 | Posted by 솔웅


반응형

문장 안에 중복 되는 글자 찾는 코딩이다.

소스 코드는 아래와 같다.

public class DuplicateCharacters {  
     public static void main(String[] args) {  
        String string1 = "Find duplicated letters in a String.";  
        int count;  
          
        //Converts given string into character array  
        char string[] = string1.toCharArray();  
          
        System.out.println("Duplicate characters in a given string: ");  
        //Counts each character present in the string  
        for(int i = 0; i <string.length; i++) {  
            count = 1;  
            for(int j = i+1; j <string.length; j++) {  
                if(string[i] == string[j] && string[i] != ' ') {  
                    count++;  
                    //Set string[j] to 0 to avoid printing visited character  
                    string[j] = '0';  
                }  
            }  
            //A character is considered as duplicate if count is greater than 1  
            if(count > 1 && string[i] != '0')  
                System.out.println(string[i]);  
        }  
    }  
}  

하나 하나 분석해 보겠다.

public class DuplicateCharacters {   // 클래스 이름은 DuplicateCharacters 이다.
     public static void main(String[] args) {  // 실행 하기 위해 main 메소드를 사용한다.
        String string1 = "Find duplicated letters in a String.";  // 이게 입력 되는 스트링이다.
        int count;  // 중복 되는 글자가 몇개나 있는지 담을 인티저 변수이다.
          
        //Converts given string into character array  
        char string[] = string1.toCharArray();  // 스트링을 어레이로 변환한다.
          
        System.out.println("Duplicate characters in a given string: ");  // 입력 값을 프린트 한다.
        //Counts each character present in the string  
        for(int i = 0; i <string.length; i++) {  // 배열내 값의 갯수 즉 스페이스를 포함한 글자의 갯수 만큼 for 문을 돌린다.
            count = 1;  // count를 1로 선언한다.
            for(int j = i+1; j <string.length; j++) {  // 배열 내 값의 갯수 보다 한개 적은 숫자 만큼 for 문을 돌린다. 이중 루프이다.
                if(string[i] == string[j] && string[i] != ' ') {  // 만약에 스페이스가 아닌데 같은 글자가 있으면 이 if 문을 실행한다.
                    count++;  // 같은 글자가 있다면 count를 하나 증가 시킨다.
                    //Set string[j] to 0 to avoid printing visited character  
                    string[j] = '0';  // 해당 되는 같은 글자는 0으로 바꾼다.
                }  
            }  
            //A character is considered as duplicate if count is greater than 1  
            if(count > 1 && string[i] != '0')  // count가 1보다 크고 값이 0이 아니면 if문을 출력한다.
                System.out.println(string[i]);  // 해당 값을 출력한다.
        }  
    }  
}  

 

코드를 조금 바꿔 봤다.

출력 값을 리스트에 담아서 이 리스트를 출력 하도록 수정했다.

 

소스 코드는 아래와 같다.

수정한 부분만 주석을 달았다.

 

import java.util.List; // List 를 사용하기 위해 import 함
import java.util.ArrayList; // ArrayList를 사용하기 위해 import 함


public class DuplicateCharacters {  
     public static void main(String[] args) {  
        String string1 = "Find duplicated letters in a String.";  
        int count;  
          
        //Converts given string into character array  
        char string[] = string1.toCharArray();  
        List<Character> answer = new ArrayList(); // 출력 될 값을 담을 ArrayList를 선언 함. 이름은 answer
          
        System.out.println("Duplicate characters in a given string: ");
        System.out.println();
        //Counts each character present in the string  
        for(int i = 0; i <string.length; i++) {  
            count = 1;  
            for(int j = i+1; j <string.length; j++) {  
                if(string[i] == string[j] && string[i] != ' ') {  
                    count++;  
                    //Set string[j] to 0 to avoid printing visited character  
                    string[j] = '0';  
                }  
            }  
            //A character is considered as duplicate if count is greater than 1  
            if(count > 1 && string[i] != '0')  {
                answer.add(string[i]); // 출력 값을 answer 에 담는다. 
            }
        }  
        
        answer.forEach(System.out::println); // forEach를 사용해 ArrayList answer의 값을 출력한다.

    }  
}  

반응형

JAVA - String Revers Sample

2022. 7. 25. 23:17 | Posted by 솔웅


반응형


요즘 Leetcode에서 문자열 reverse 하는 문제를 풀어 봤는데 주말에 한번 더 연습을 해 보았다.
내가 만든 코드와 결과는 아래와 같다.





코드를 잘 살펴 보자.

class reverse { // 클래스 이름은 reverse이다.
    public static void main(String[] args) { // 실행을 하기 위해 main() 메소드를 사용했다.
        String a = "Hello World"; // 테스트 할 스트링이다.
        char[] b = a.toCharArray(); // 리버스 하기 위해 스트링을 char[] 데이터 타입으로 변환했다.
        
        int left = 0; // left는 0으로 선언
        int right = b.length-1; // right 는 b[] 의 맨 마지막 자리로 선언
        
        while(left < right) { // left가 right보다 작을 때 까지만 while문을 실행한다.
            char temp = b[left]; // 임시로 입력값의 맨 앞자리 값을 temp에 담는다.
            b[left++] = b[right]; // b의 left 자리에 right 자리값을 넣는다.
            b[right--] = temp; // 임시로 저장했던 left 자리의 값을 right에 넣는다.
            // left++ 와 right—의 의미는 이전 글 참조
        }
        
        for(int i = 0; i < b.length; i++){ // for문을 돌려 b[]의 값들을 출력한다.
            System.out.println(b[i]);
        }
        
    }
}

반응형

Leetcode 541. Reverse String 2 - Easy

2022. 7. 22. 08:26 | Posted by 솔웅


반응형


이번 문제는 문제 자체를 이해하기가 좀 어렵다.
문제를 잘 해석해 보면…
우선 Example 1 의 인풋과 아웃풋을 보자

abcdefg 와 2가 인풋이다.
여기서 요구하는 사항은 두번째 글자를 첫번째 글짜와 Reverse 하라는 거다.
그래서 아웃풋을 보면 abcd가 bacd 가 되었다.
그 다음 요구는 각각의 2k 마다 이 일을 하라는 거다.
여기서 인풋 k = 2이기 때문에 2 * 2 = 4 마다 이 일을 하라는 거다.

즉 abcd는 일을 다 끝냈고 그 다음에 남은건 efg 이니까 여기서 두번째 글자와 첫번째 글자의 위치를 바꾸라는 거다.


아래 스크립트로 원하는 답을 얻을 수 있다.

class Solution {
    public String reverseStr(String s, int k) {
        char[] a = s.toCharArray();
        for (int start = 0; start < a.length; start += 2 * k) {
            int i = start, j = Math.min(start + k - 1, a.length - 1);
            while (i < j) {
                char tmp = a[i];
                a[i++] = a[j];
                a[j--] = tmp;
            }
        }
        return new String(a);
    }
}

한줄 한줄 분석해 보자.

class Solution { ==> 클래스 이름은 Solution이다.
    public String reverseStr(String s, int k) {
==> 메소드 이름은 reverseStr이고 입력값으로 스트링 s , int k 를 받는다. return 값은 String이다.
        char[] a = s.toCharArray();
==> 입력받은 스트링을 toCharArray()를 이용해서 배열로 만들고 변수 a 에 할당한다.
        for (int start = 0; start < a.length; start += 2 * k) {
==> 루프를 만들어 start가 배열 아이템 갯수 보다 작을 때 까지 돌린다. start는 초기 값이 0이고 루프 한번 돌면 start 값은 start + 2 * k 만큼 증가한다.
            int i = start, j = Math.min(start + k - 1, a.length - 1);
==> inti 에는 start 값이 대입된다. Int j 에는 start + k 값과 a.length - 1 값 중 작은 값이 대입 된다.
==>
            while (i < j) {
==> i 가 j 보다 작을 때 까지만 while문을 돌린다.  즉 2*k 블럭이 더이상 없으면 while문은 종료한다.
                char tmp = a[i];
==> tmp에 배열의 첫번째 값을 대입한다.
                a[i++] = a[j];
==> a 의 첫번째 값에 j의 값을 대입한다. 그리고 i 에 1을 더해 준다.
                a[j--] = tmp;
==> 배열 맨 마지막에 첫번째 값을 대입한다.
            }
        }
==> 이건 for 와 while이 있는 이중 루프이다.
==> start와 i와 j가 어떻게 증가하는지 이해 해야 한다.
==> start는 k 가 2인경우 0, 4, 8, 12 이렇게 증가한다. k가 3인 경우는 0, 6, 12, 18 이렇게 증가한다.
==> i 는 매번 루프를 돌 때무다 start가 대입 되고 while을 돌 때는 1씩 증가한다.
==> j 는 start + k 와 a.length -1 중 작은 값이 대입 되고 while을 돌 때 마다 1씩 증가한다.
        return new String(a);
==> 배열 a를 스트링 형으로 바꿔서 return 한다.
    }
}




입력값을 위와 같이 바꿔 봤다.

결과 값을 보면 세번째 값인 c가 맨 앞에 왔다. 그 다음 블럭은 ghijkl 이다.
두번째 블럭 세번째 값인 i 가 맨 앞으로 왔다.
세번째 블럭은 mn 이다.
여기서 두번째 값인 n이 앞으로 왔다.
이건 요구 조건 충족이 되는건가?
하여간 Expected 와 결과값이 같으니까 통과 하긴 했는데…
왜 두번째 값인 n이 앞으로 왔는지 모르겠다. 입력값은 3이니까 세번째 값이 와야 되는데… 그런데 세번째 값이 없으니…. 맨 마지막 값이 앞으로 와 버렸다.
로직상 이렇게 작동하는건 맞긴 한데…
이게 요구 조건에 충족되는 건지는 모르겠다.

파이썬 코드는 아래와 같다.

class Solution(object):
    def reverseStr(self, s, k):
        a = list(s)
        for i in xrange(0, len(a), 2*k):
            a[i:i+k] = reversed(a[i:i+k])
        return "".join(a)





반응형

Leetcode 344 Reverse String - Easy

2022. 7. 22. 07:22 | Posted by 솔웅


반응형


오늘의 Leetcode 연습문제는 Reverse String 이다.
아래 input과 output이 있다.

 

코드는 아래와 같다.

 

class Solution {

    public void reverseString(char[] s) {

        int left = 0;

        int right = s.length - 1;

        while (left < right) {

            char temp = s[left];

            s[left++] = s[right];

            s[right--] = temp;

        }

    }

}

 

한 줄 한 줄 살펴 보겠다.

 

class Solution {

==> 클래스 이름은 Solution있다.

    public void reverseString(char[] s) {

==> 메소드 이름은 reverseString 이고 input 은 char[] s 이며 return 값은 없다. 그냥 입력 String 을 reverse 하는 기능만 있다. 

        int left = 0; ==> left 라는 int 변수에 0을 대입한다.

        int right = s.length - 1; ==> right 라는 int 변수에 입력 값 s의 맨 마지막 글자가 대입된다.

        while (left < right) { ==> left 가 right 보다 작을 경우에만 실행하고 그렇지 않으면 while 문을 종료한다.

            char temp = s[left]; ==> temp 라는 캐릭터에 s[left] 값을 대입한다.

            s[left++] = s[right];

==> 이 부분을 잘 이해 해야 한다.

==> left++ 는 left = left+1과 같다. 첫번째는 left 가0이다. 그리고 left + 1을 하면 left 가 1이 된다.

==> 실행 순서는 s[0] = s[right]을 먼저 실행 한 다음에 left = left+1이 된다.

==> 그렇게 되면 s[0]에 s의 맨 마지막 값이 대입되고 난 후 left는 1이 된다.

==> 다음 while 문 돌 때는 left가 0 이 아니라 1 이 된다.

            s[right--] = temp;

==> 이것도 마찬가지 right-- 는 right = right -1 과 같다.

==> 위에서 설명한 대로 s[right] = temp 가 먼저 실행 된다. 즉 s[right]에 s 의 맨 처음 값이 대입 된다.

        }

    }

}

 

While 문을 처음 되면은 s 의 처음 값이 맨 마지막으로 가고 맨 마지막 값이 맨 처음으로 오게 된다.

두번째 while 문에서는 s의 두번 째 값이 끝에서 두번째로 가고 끝에서 두번째 값이 두번째로 오게 된다.

이 while 문은 배열의 중간까지 오게 되면 left가 right보다 작지 않으므로 멈춘다.

 

그러므로 배열의 중간을 기준으로 reverse 되게 된다.

 

즉 해당 requirement를 충족하게 된다.

 

파이썬에서는 아주 간단하게 해결 할 수 있다.

class Solution:
    def reverseString(self, s):
        s.reverse()

 

Recursive 기능을 구현한 Helper를 사용해서 아래와 같이 코딩을 할 수 있다.

 

class Solution {
  public void helper(char[] s, int left, int right) {
    if (left >= right) return;
    char tmp = s[left];
    s[left++] = s[right];
    s[right--] = tmp;
    helper(s, left, right);
  }

  public void reverseString(char[] s) {
    helper(s, 0, s.length - 1);
  }
}

 

helper 메소드 내에서 자신을 호출함으로서 루프 효과를 내고 있다.

String을 reverse 하는 로직을 똑 같다.

 

파시썬으로는 아래와 같이 할 수 있다.

class Solution:
    def reverseString(self, s):
        def helper(left, right):
            if left < right:
                s[left], s[right] = s[right], s[left]
                helper(left + 1, right - 1)

        helper(0, len(s) - 1)

 

 

 

 

 

 

반응형

Leetcode 118. Pascal's Triangle (Easy)

2022. 7. 20. 09:43 | Posted by 솔웅


반응형


오늘의 Leetcode 문제는 118번 Pascal’s Triangle 이다.
난이도는 Easy. 전혀 Easy 하지 않은 것 같은데.
아래가 풀어야 할 문제이다.


우선 패턴을 분석하면 첫줄은 숫자가 한개이고 그 값은 1이다.
두번째 줄은 숫자가 두개이고 그 값은 1과 1이다.
세번째 줄은 숫자가 세개이고 그 값은 1, 2,1 이다. 여기서 2는 윗줄의 1과 1을 더한 것이다.
네번째 줄은 숫자가 네개이고 그 값은 1, 3, 3, 1 이다.
첫번째 3은 윗줄의 첫번째 값 1과 두번째 값 2을 더한 값이다.
두번째 3은 윗줄의 두번째 값 2와 세번째 값 1을 더한 값이다.
다섯번째 줄은 숫자가 다섯개이고 그 값은 1, 4, 6, 4, 1 이다.
첫번째 4는 윗줄의 첫번쨰 숫자 1과 두번째 숫자 3을 더한 값이다.
그 다음 6은 윗줄의 두번째 숫자 3과 세번째 숫자 3을 더한 값이다.
그 다음 4는 윗줄의 세번째 숫자 3과 네번째 숫자 1을 더한 값이다.

이제 패턴을 알 것 같다.
1. 줄이 늘어날 수록 숫자의 갯수는 1씩 증가한다. 첫번째 줄은 한개의 숫자가 있고 그 값은 1이다.
2. 맨 처음 값과 맨 마지막 값은 각각 1이다.
3. 나머지 값들은 윗줄의 (col -1) + 윗줄의 col 값이다. 여기서 col은 해당 숫자가 그 줄의 몇번째에 있는지를 나타내는 것이다.

그럼 각 줄의 맨 처음과 맨 마지막을 제외한 나머지의 값을 구하는 공식은 다음과 같다.

triangle[row][col] = triangle[row-1][col-1] + triangle[row-1][col]

이제 패턴과 그 패턴에 따른 해당 값을 구하는 공식을 완성했으니 코딩을 시작할 차례다.

Example 1의 Output을 보면 그 값은 이중배열이다.

아래와 같이 코딩을 하면 요구 조건을 충족할 수 있다.

class Solution {
   public List<List<Integer>> generate(int numRows) {
       List<List<Integer>> triangle = new ArrayList<>();
       triangle.add(new ArrayList<>());
       triangle.get(0).add(1);
       for(int rowNum =1; rowNum  < numRows; rowNum++) {
           List<Integer> currRow = new ArrayList<>();
           List<Integer> prevRow = triangle.get(rowNum -1);
           currRow.add(1);
           for (int col = 1; col < prevRow.size(); col++) {
               currRow.add(prevRow.get(col-1) + prevRow.get(col));
           }
           currRow.add(1);
           triangle.add(currRow);
       }
       return triangle;
   }
}

한줄 한줄 분석해 보자.

class Solution { ==> 클래스 이름은 Solution이다.
   public List<List<Integer>> generate(int numRows) { ==> 메소드 이름은 generate이다.
==> input 값으로 numRows를 받는다.
==> return 값은 List<List<Integer>>이다.  배열 갯수를 마음대로 변경할 수 있도록 하기 위해서 List를 사용한다.
==> Expected Output 값이 이중 배열이기 때문에 List 안에 또 List를 넣었다. 배열내 값의 데이터 타입은 Integer이다.

       List<List<Integer>> triangle = new ArrayList<>();
==> 결과 값을 저장할 변수를 만든다. 이 변수 이름은 triangle이다.
==> List를 사용하기 위해서는 ArrayList, LinkedList, Vector, Stack 클래스들로 인스턴스 화 할 수 있다.
==> 이 중에서 ArrayList를 생성해서 인스턴스 화 시켰다.

       triangle.add(new ArrayList<>());
==> 이중 배열이기 때문에 triangle안에 ArrayList를 add 했다.

       triangle.get(0).add(1);
==> triangle의 첫 줄에는 값 1을 hardcoding으로 집어 넣었다.

       for(int rowNum =1; rowNum  < numRows; rowNum++) {
==> 결과 값을 만들기 위해 for문을 만들었다. 이 메소드의 input 값인 numRows가 결과 값의 줄의 갯수를 의미하는 것이니, 그 갯수만큼 줄을 만들기 위해서 numRows만큼만 for 문을 돌린다.

           List<Integer> currRow = new ArrayList<>();
==> 현재의 줄을 ArrayList로 인스턴스 화 한다. 이름은 currRow

           List<Integer> prevRow = triangle.get(rowNum -1);
==> 계산을 해서 현재 Row의 각 컬럼의 값을 얻기 위해서 이전 줄의 값을 가져와야 한다. prevRow에 이전 줄의 값들을 저장한다.

           currRow.add(1);
==> 현재 줄의 첫번째 값은 항상 1이다. 이 값을 하드코딩으로 집어 넣었다.

           for (int col = 1; col < prevRow.size(); col++) {
==> 현재 줄의 첫번째 값과 마지막 값을 제외한 중간에 위치한 컬럼의 값을 구하기 위해 for문을 만들었다.
==> 이전 줄의 size보다 한개 작은 숫자 만큼 돌린다.
               currRow.add(prevRow.get(col-1) + prevRow.get(col));
==> 이 코드가 핵심이다.
==> 현재 줄의 현재 컬럼에 이전줄의 현재 컬럼 -1 의 값 + 이전줄의 현재 컬럼 값을 넣어 준다.
           }
           currRow.add(1);
==> 현재 줄의 맨 마지막에는 값 1을 하드코딩으로 넣어 준다.

           triangle.add(currRow);
==> triangle에 현재 줄의 값을 add 한다.
       }

       return triangle;
==> 이중 for 문에서 구한 결과 값을 리턴한다.
   }
}

이렇게 하면 원하는 결과를 얻을 수 있다.
참고로 첫번째 for문은 결과 값이 몇줄이 될 것인지 정하는 것이고,
두번째 for 문은 각 줄에 값들을 구하기 위해 만들어진 것이다.

약간 다른 방법으로는 아래와 같이 할 수도 있다.

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> triangle = new ArrayList<List<Integer>>();

        // Base case; first row is always [1].
        triangle.add(new ArrayList<>());
        triangle.get(0).add(1);

        for (int rowNum = 1; rowNum < numRows; rowNum++) {
            List<Integer> row = new ArrayList<>();
            List<Integer> prevRow = triangle.get(rowNum-1);

            // The first row element is always 1.
            row.add(1);
==> 여기 까지는 위와 같다.

            // Each triangle element (other than the first and last of each row)
            // is equal to the sum of the elements above-and-to-the-left and
            // above-and-to-the-right.
            for (int j = 1; j < rowNum; j++) {
==> 위 코드와 다른 것인 이 한 줄이다.
==> 두번째 for문을 rowNum보다 작을 때까지 돌린다.
==> rowNum은 두번째 줄이 1이다. (위 에서 첫번쨰 줄에는 1을 하드 코딩했고 첫번째 for문에서 그 다음 줄을 rowNum = 1 을 했기 때문이다.)
==> 그리고 두번째 for 문에서는 이 rowNum보다 작을 때까지 for문을 돌린다.
==> 그러니까 실질 적으로는 두번째 에서는 줄 -2 만큼 돌리게 된다.
==> 즉 4번째 줄은 4-2 이니까 두개의 값만 구하게 된다.
==> 요구 조건을 충족 시키는 방법이다. 위 코드와는 조금 다르지만 결과 값은 똑 같다.
                row.add(prevRow.get(j-1) + prevRow.get(j));
            }

            // The last row element is always 1.
            row.add(1);
            triangle.add(row);
        }
        return triangle;
    }
}

Python 3의 코딩은 아래와 같다.

class Solution:
    def generate(self, num_rows: int) -> List[List[int]]:
        triangle = []

        for row_num in range(num_rows):
            # The first and last row elements are always 1.
            row = [None for _ in range(row_num + 1)]
            row[0], row[-1] = 1, 1

            # Each triangle element is equal to the sum of the elements
            # above-and-to-the-left and above-and-to-the-right.
            for j in range(1, len(row) - 1):
                row[j] = triangle[row_num - 1][j - 1] + triangle[row_num - 1][j]

            triangle.append(row)

        return triangle




반응형

자바스크립트 기초 메모

2016. 3. 24. 23:55 | Posted by 솔웅


반응형

* 자바스크립트 기초

- 브라우저가 페이지의 HTML 텍스트를 받아서 DOM 스트럭처에 parse 한다. 그 후 해당 페이지의 content들에 대한 각 층들을 구분해서 표시할 준비를 한다. 그리고 표시하기 전에 style을 적용한 후 표시한다.
- HTML은 정적인 부분을 정의하기에 아주 좋은 언어이다. 하지만 웹의 다이나믹한 부분을 구현하는데는 한계가 있다.
- 자바스크립트는 이 다이나믹한 뷰를 제공하는 클라이언트 side의 가장 강력한 스크립트 언어이다.
- 자바스크립트는 ajax, user experience, responsive web design 의 가장 중요한 부분을 구현한다.
- DOM 은 jQuery 같은 라이브러리를 사용해서 클라이언트 side의 스크립팅을 간편하게 만들어 준다. 하지만 separation of concerns 를 처리하는 문제를 해결하지는 못한다.
- 이의 해결을 위한 다른 라이브러리나 프레임워크들이 있다.

* 자바스크립트의 목적
- 자바스크립트는 객체지향언어이다. 자바스크립트로 객체를 정의하고 메소드와 프로퍼티들을 할당한다.
- 자바스크립트 객체는 배열이나 hash와 관련돼 있다. (key value pairs)
: obj[key]=value 나 obj.name = value 형식으로 키를 할당한다.
: delete obj.name 으로 delete한다.
: string key의 iteration order는 definition order 이고 numeric key는 변경할 수 있다.
- 함수(프로퍼티)는 obj.method() 형식으로 호출된다. 또한 this. 로 참조되고 프로퍼티들은 어느때나 assign되거나 remove 될 수 있다.
- 함수는 new Func(params) 로 constructor에서 실행될 때 새로운 객체를 생성할 수 있다. 이때 대개 이러한 함수의 이름은 대문자를 사용한다.

* 객체 생성하기
- obj = new Object(); , obj = {};

dot notation을 사용하는 경우
var employee ={}; - 값이 정해지지 않은 상태
employee.Id = 12345;
employee.Name = "Tom" - Key : Name, Value : Tom
employee.Name
delete employee.Name
employee => Object {Id:12345}

[]를 사용하는 경우
var employee = {};
employee["Id"] = 12345;
employee["Name"] = "Tom"
employee => Object{Id:12345, Name:"Tom"}
delete employee["Name"]
employee => Object{Id:12345}

* 객체에 존재하지 않는 프로퍼티 참조하기
- 객체에 해당 프로퍼티가 존재하지 않으면 undefined 가 return 된다.
- in operator를 사용해서 키의 존재를 확인할 수 있다.
var employee = {}
employee.Id
employee.Id === undefined
"Id" in employee => false
employee.Id = 12345
"Id" in employee => true



* 객체 키에 대해 iterationg 하기
- for.. in을 사용해서 iterate 할 수 있다.
var employee = {}
employee.Id = 12345
employee.Name = "Tom"
for(key in employee) {console.log("Key : " + key + "Value : " + employee[key]}
==> Key : Id Value : 12345
==> Key : Name Value : Tom

* Object reference
- 객체에 할당된 변수는 그것을 reference 한다. 즉 포인터와 같은 역할을 하는데 실제 data를 가리키는 것이다.
- 이 reference 변수를 사용해서 객체의 property를 바꿀 수 있다.
- 변수는 실제로는 reference 이다. 함수의 객체에 전달할 때의 값이 아니다.
var employee = {};
employee["Id"] = 12345;
var obj = employee; - 같은 객체에 대한 새로운 point
obj.Id = 54321
employee.Id ==> 54321

* this 키워드
-객체에서 함수가 호출될 때 this 가 이 객체의 reference가 된다.

var foo = {
    name : "Guest",
    setName : function(){
        this.name = prompt('Enter your name'); - this 는 foo 객체의 reference 가 된다.
    },
    getName : function(){
        console.log("Your name is : " + this.name);
    }
};

foo.getName(); ==> Your name is : Guest
foo.setName() - 프롬프트가 나타나면 이름을 입력한다.
foo.getName(); ==> Your name is : '프롬프트에 입력한 이름'

* 생성자 함수
- obj = {...}를 사용해 객체를 생성할 수 있다.
- 다른 방법으로는 constructor를 사용할 수 있다. 이 때 Constructor는 Pascal case 여야 한다.
- 초기에 빈 객체인데 여기에 초기화 할 때 어떤 프로퍼티를 할당해야 하는 경우 사용한다. result 는 returned 이다.

function Calculator(firstVar, secondVar) {
    this.firstVar = firstVar;
    this.secondVar = secondVar;
    this.sum = function(){
        return this.firstVar + this.secondVar;
    }
}

new Calculator(5,5);
var calcObj1 = new Calculator(5,5);
var calcObj2 = new Calculator(15,15);

calcObj1.sum(); ==> 10
calcObj2.sum(); ==> 30

* 프로토타입 상속
- 자바스크립트에서 상속은 프로퍼티 베이스이다. 다른 클래스에서 클래스를 상속하는 대신 다른 객체에서 객체를 상속한다.
- 다른 객체에서 객체를 상속하기 위해서는 아래와 같이 한다.
childObject._proto_=baseObject
: 이 syntax는 크롬과 파이어폭스에서 사용된다. 다른 브라우저에서는 내부적으로 이 프로퍼티가 존재한다. (hidden)
childObject = Object.create(baseObject)
ConstructorFunction.prototype = baseObject
: 이 syntax는 최근의 모든 브라우저에서 동일하게 작동한다.

1. _proto_ 사용해서 프로토타입 상속하기
var foo {
    fooVar : "Foo Variable",
    fooMethod : function(){
        console.log(this.fooVar);
    }
}

var bar = {
    barVar : "Bar Variable"
}

bar._proto_ = foo // bar 객체가 foo로부터 상속했다.
==> Object{fooVar: "Foo Variable", fooMethod:function}
bar
==> Object{barVar: "Bar Variable", fooVar: "Foo Variable", fooMethod:function}



2. Object.create() 사용해서 프로토타입 상속하기

var foo {
    fooVar : "Foo Variable",
    fooMethod : function(){
        console.log(this.fooVar);
    }
}

var bar = Object.create(foo) // bar 객체가 foo로부터 상속했다.

bar
==> Object{fooVar: "Foo Variable", fooMethod:function}

bar.barVar - "Bar Variable"
==> Object{barVar: "Bar Variable", fooVar: "Foo Variable", fooMethod:function}

3. prototype 사용해서 프로토타입 상속하기
function Employee(){
    this.Id = 0;
    this.Name = "";
}

function Manager(){}
Manager.prototype = new Employee(); // Manager가 Employee 객체를 상속한다.
==> Employee {Id: 0, Name: ""}
var ani1 = new Manager();
ani1
==>Manager {Id: 0, Name: ""} // new Manager로 생성되는 모든 객체는 ID와 Name을 갖게 된다.
ani1.Id = 1111;
ani1.Name = "Tom"
ani1
==> Manager {Id: 1111, Name: "Tom"}

반응형

'etc. > Responsive Web' 카테고리의 다른 글

반응형 웹 Media Query 를 위한 CSS 탬플릿 자료  (0) 2012.07.29


반응형

Getting Started with Apache Cassandra and Java (Part II)

  By Rebecca Mills, Junior Apache Cassandra Evangelist (@RebccaMills)



Requirements

이 강좌를 따라하시려면 Cassandra instance를 이미 가동한 상태여야 합니다. (작은 클러스터면 더 좋습니다.) Datastax Java Driver 도 인스톨 하세요. (refer to Part 1), 그리고 다음 10분 강좌를 마치시면 좋습니다.
http://www.PlanetCassandra.org/try-cassandra


Try it out

이 예제는 간단한 console application을 만드는 겁니다. Part 1 에서 이미 기본적인것은 다 완료 됐습니다. 이 글에서는 connection policies, prepared statements 그리고 query builder 등에 대해서 알아보겠습니다. 텍스트 에디터를 열어서 sinble main method 가 있는 이름이 GettingStartedTwo 인 클래스를 생성하세요.


public class GettingStartedTwo {

public static void main(String[] args) {

        Cluster cluster;
        Session session;
        ResultSet results;
        Row rows;

이제 cluster에 접속하고 session instance를 생성하겠습니다.

// Connect to the cluster and keyspace "demo"
Cluster cluster = Cluster.builder()
                  .addContactPoint("localhost")
                  .build();
Session session = cluster.connect("demo");


여기서 single instance 대신에 cluster 를 실행하겠습니다. 실패할 경우를 대비해서 적당한 장소에 safeguard를 넣겠습니다. 이 작업은 RetryPolicy 를 사용해서 할 겁니다. retry policy 는 시간이 초과되거나 node 접속이 가능하지 않을 때 반응할 수 있는 디폴트 behavior 가 가능하도록 합니다. DefaultRetryPolicy 를 사용하면 디폴트를 사용합니다.

    * read 시 여러번 반복했지만 데이터를 받지 못할 때

    * write 시 정해진 시간안에 batch statements에 의해 사용되는 log 를 writing 하는게 완료되지 않을 때

cluster = Cluster
    .builder()
    .addContactPoint("192.168.0.30")
    .withRetryPolicy(DefaultRetryPolicy.INSTANCE)
    .build();
session = cluster.connect("demo");

load balancing policy는 어떤 node 가 해당 쿼리에 대해 반응하고 작동할 것인가를 결정해 줍니다. client 는 어떤 node 에서도 read 와 write를 할 수 있습니다. 이것이 어떤 경우에는 효율적이지 않을 수 있습니다. 만약에 어떤 node 가 다른 node에서 가능한 read나 write를 하라는 요청을 받았다면 그 노드는 해당 요청에 대해 처리될 수 있도록 추가 작업이 필요합니다. 이런 경우를 다루기 위해 load balancing을 사용할 수 있습니다. TokenAwarePolicy 는 해당 요청이 primary key에 의해 지정된 data에 대해 해당 node로 가던가 아니면 요청을 적당한 노드로 복제하던가 하는 작업을 하게 될 겁니다. TokenAwarePolicy 를 사용할 때 DCAwareRoundRobinPolicy 를 사용하고 있는데요. 이는 그 요청이 local datacenter에서 수행될 수 있도록 관리하게 됩니다. 지금 현재 우리는 하나의 local cluster 만 가지고 있지만 그 다음 단계인 multi-datacenter 로 확장하게 될 경우 이러한 기능을 사용하는 것은 아주 바람직한 방법이라고 할 수 있습니다.

cluster = Cluster
        .builder()
        .addContactPoint("192.168.0.30")
        .withRetryPolicy(DefaultRetryPolicy.INSTANCE)
        .withLoadBalancingPolicy(
                         new TokenAwarePolicy(new DCAwareRoundRobinPolicy()))
        .build();
session = cluster.connect("demo");




이제 demo 키스페이스에 연결 됐습니다. users 테이블에 user를 입력해 봅시다. Part 1 에서 이미 다뤘던 내용입니다. 여기서는 Part 1과는 약간 다른 방법을 사용할 건데요. prepared statement를 사용할 겁니다. 이는 데이터베이스에 데이터를 넣고 뺄 때 좀 더 scure 하고 더 좋은 performance를 보여줄겁니다. Prepared statement는 cluster에 의해 한번만 parse 되어야 합니다. 그리고 나서 쿼리가 변수에 대입되고 그 변수를 이용해서 해당 쿼리 (statement)를 실행해서 cluster로부터 데이터를 읽고 쓰게 됩니다.


// Insert one record into the users table
        PreparedStatement statement = session.prepare(

        "INSERT INTO users" + "(lastname, age, city, email, firstname)"
                + "VALUES (?,?,?,?,?);");

        BoundStatement boundStatement = new BoundStatement(statement);

        session.execute(boundStatement.bind("Jones", 35, "Austin",
                "bob@example.com", "Bob"));


Java driver를 사용하면 쉽게 해당 user를 다시 받을 수 있습니다. Part 1에서는 CQL의 string representation을 사용했었습니다. 여기서는 같은 것을 Query Builder로 할 겁니다. 이 방법은 잠재적인 CQL injection attack에 대해 좀 더 secure 한 방법으로 그 위험성을 없앨수 있는 방법입니다.


// Use select to get the user we just entered
        Statement select = QueryBuilder.select().all().from("demo", "users")
                .where(eq("lastname", "Jones"));
        results = session.execute(select);
        for (Row row : results) {
            System.out.format("%s %d \n", row.getString("firstname"),
                    row.getInt("age"));
        }

이제 Bob의 생일이니까 나이를 한살 더 올리는 update를 실행하겠습니다.

// Update the same user with a new age
        Statement update = QueryBuilder.update("demo", "users")
                .with(QueryBuilder.set("age", 36))
                .where((QueryBuilder.eq("lastname", "Jones")));
                        session.execute(update);
// Select and show the change
        select = QueryBuilder.select().all().from("demo", "users")
                .where(eq("lastname", "Jones"));
        results = session.execute(select);
        for (Row row : results) {
            System.out.format("%s %d \n", row.getString("firstname"),
                    row.getInt("age"));


                    이제 테이블에서 Bob을 delete 합시다. 그리고 나서 users 테이블에 있는 남아있는 데이터를 모두 print 하겠습니다. Bob의 정보는 delete 됐으니까 더이상 볼 수 없을 겁니다. (이외에 다른 정보가 이전에 입력돼 있다면 그 정보는 보여질 겁니다.)

// Delete the user from the users table
           Statement delete = QueryBuilder.delete().from("users")
                .where(QueryBuilder.eq("lastname", "Jones"));
        results = session.execute(delete);
        // Show that the user is gone
           select = QueryBuilder.select().all().from("demo", "users");
        results = session.execute(select);
        for (Row row : results) {
            System.out.format("%s %d %s %s %s\n", row.getString("lastname"),
                    row.getInt("age"), row.getString("city"),
                    row.getString("email"), row.getString("firstname"));
        }

작업이 끝나면 반드시 connection을 끊어 주세요.

// Clean up the connection by closing it
cluster.close();
    }
}




CQL은 SQL 과 아주 유사합니다. 대부분의 SQL syntax가 CQL에서도 사용됩니다. 만역 관계형 데이터베이스에 대한 사전 지식이 있다면 데이터에 대한 쿼리를 이해하시기 쉬울 겁니다.

이제 Cassandra cluster에 연결하고 데이터베이스에 대해 쿼리를 실행시키면서 관리하는 방법을 배웠습니다. 이 글을 통해 Java Driver를 사용해서 Cassandra를 사용하는 것이 얼마나 쉬운일인지 이해하셨으면 합니다. 전체 console application 에 대한 샘플 코드는 이곳에서 받으실 수 있습니다.

반응형


반응형


Getting Started with Apache Cassandra and Java (Part I)

By Rebecca Mills, Apache Cassandra Junior Evangelist (@rebccamills)




이 강좌를 따라하시려면 Cassandra instance를 이미 가동한 상태여야 합니다. 그리고 다음 10분 강좌를 마치시면 좋습니다.
http://www.PlanetCassandra.org/try-cassandra

Part 1을 이미 보셨다면 그 다음인 Part 2를 보세요. Getting Started with Apache Cassandra and Java Part II.

그리고 사용할 keyspace와 schema 를 만들어 두시고 아래 내용을 실습할 때 사용하세요.


 
Setup



우리는 이 내용을 배우기 위해 간단한 console 어플리케이션을 만들 겁니다. 텍스트 에디터를 여시고 클래스 이름이 GettingStarted 라는 single main method를 가지는 자바파일을 생성하세요.

public class GettingStarted {

        public static void main(String[] args) {

그리고 다운로드 페이지에서 driver jar 파일을 다운로드 받습니다. 다운 받으시려면 여기를 누르세요. 다운 받은 후엔 이것을 여러분의 작업 directory에 expand 하세요. 이렇게 하면 자바파일을 컴파일 할 때 classpath 에 이것을 포하게 됩니다.

For example:
javac -classpath cassandra-java-driver-2.0.2/cassandra-driver-core-2.0.2.jar:. GettingStarted.java

  이 파일을 run 할 때에는 아래와 같이 하게 됩니다.
 
java -classpath cassandra-java-driver-2.0.2/*:cassandra-java-driver-2.0.2/lib/*:. GettingStarted



Try it Out

코딩은 main method에 할 겁니다. 우선 reference들을 hold 할 cluster 와 session instance 필드들을 생성해야 합니다. session은 cluster 에 대한 connection을 관리할 겁니다.

Cluster cluster;
Session session;

instance 에 연결하는 것은 Cluster.builder 메소드를 사용합니다. 여기에 contact point를 넣고 cluster instance를 build 할 겁니다. 해당 cluster로부터 session을 받은 다음 demo 라는 keyspace에 연결합니다.

// Connect to the cluster and keyspace "demo"
cluster = Cluster.builder().addContactPoint("127.0.0.1").build();
session = cluster.connect("demo");

이제 demo 라는 키스페이스에 연결 됐습니다. 이제 user를 users라는 테이블에 insert 해 보겠습니다.

// Insert one record into the users table
session.execute("INSERT INTO users (lastname, age, city, email, firstname) VALUES ('Jones', 35, 'Austin', 'bob@example.com', 'Bob')");

자바 드라이버를 사용해서 쉽게 그 user 정보를 다시 받아올 수 있습니다.

// Use select to get the user we just entered
ResultSet results = session.execute("SELECT * FROM users WHERE lastname='Jones'");
for (Row row : results) {
System.out.format("%s %d\n", row.getString("firstname"), row.getInt("age"));
}


Bob의 생일이니까 이제 Bob의 나이를 update 해 보겠습니다.

// Update the same user with a new age
session.execute("update users set age = 36 where lastname = 'Jones'");
// Select and show the change
results = session.execute("select * from users where lastname='Jones'");
for (Row row : results) {
System.out.format("%s %d\n", row.getString("firstname"), row.getInt("age"));

}

이제 테이블에서 Bob을 한번 지워 보겠습니다. 그리고 나서 모든 row를 print 합니다. delete 된 이후에는 Bob에 대한 정보를 볼 수 없게 될 겁니다. (이전에 정보가 입력돼 있다면 그 정보는 보실 수 있을 겁니다.)

// Delete the user from the users table
session.execute("DELETE FROM users WHERE lastname = 'Jones'");
// Show that the user is gone
results = session.execute("SELECT * FROM users");
for (Row row : results) {
System.out.format("%s %d %s %s %s\n", row.getString("lastname"), row.getInt("age"),  row.getString("city"), row.getString("email"), row.getString("firstname"));
}

모든 작업을 마치면 connection을 반드시 닫아 주세요.

// Clean up the connection by closing it
cluster.close();
    }
}




CQL은 SQL 과 아주 유사합니다. 대부분의 SQL syntax가 CQL에서도 사용됩니다. 만역 관계형 데이터베이스에 대한 사전 지식이 있다면 데이터에 대한 쿼리를 이해하시기 쉬울 겁니다.

이제 Cassandra cluster에 연결하고 데이터베이스에 대해 쿼리를 실행시키면서 관리하는 방법을 배웠습니다. 이 글을 통해 Java Driver를 사용해서 Cassandra를 사용하는 것이 얼마나 쉬운일인지 이해하셨으면 합니다. 전체 console application 에 대한 샘플 코드는 이곳에서 받으실 수 있습니다.

반응형


반응형

Composite Partition Key



composite partition key를 가지고 있는 테이블에 대해 카산드라는 partition key로 여러개의 컬럼을 사용한다. 이 컬럼들은 retrieval (검색)을 용이하게 하기 위해 partition 안에 logical set들을 구성한다. simple partition key와는 다르게 composite partition key는 어디에 데이터가 자리잡을지를 정하기 위해 두개 이상의 컬럼들을 사용한다. Composite partition key들은 single partition에 들어가기엔 데이터가 너무 클 경우 사용된다. partition key에 대해 한개 이상의 컬럼을 사용하는 것은 데이터를 chunks 나 bucket들로 파편화하게 된다. 이 데이터는 group 화 되어 있지만 더 작은 chunk들로 구성되게 된다. 이 방법은 Cassandra cluster 가 hotspotting 이거나 하나의 node에 집중적이고 반복적으로 데이터를 write 하는 경우에 유용하게 사용할 수 있다. 왜냐하면 이 경우 하나의 partition에 아주 집중적으로 writing이 이뤄질 것이기 때문이다. Cassandra는 종종 time series data를 사용하는 경우가 있고 hotspotting 이 문제화 될 수 있다. 입력되는 데이터를 bucket들에 파편화 할 때 년:월:일:시 이 4개의 컬럼을 기반으로 나뉘어지게 되는데 이럼으로서 partition에 hotspot을 줄일 수 있게 됩니다.

데이터는 partition key를 사용해서 검색 (retrieve) 하게 됩니다. 테이블에서 데이터를 검색하기 위해서는 모든 컬럼들의 값들이 partition key안에 정의 되어 있어야 합니다. (만약 secondary index 가 사용되지 않을 경우). 아래 race_year, race_name 테이블은 composition partition key로 된 primary key 안에 있습니다. 데이터를 검색하기 위해서는 두 파라미터 모두가 identify 되어야 합니다.




Composit partition key를 가지고 있는 테이블을 생성하려면 아래와 같이 하면 된다.

    CREATE TABLE 구문의 마지막에 쉼표를 넣고 PRIMARY KEY 라는 키워드를 입력한다. 그리고 그 다음에 partition key 의 컬럼 이름을 넣는다. Composite partition key인 컬럼 이름은 double parentheses(이중 괄호) 안에 넣습니다.

카산드라는 데이터의 전체 row를 partition key 에 따라서 node 에 저장합니다. 만약 하나의 partition에 너무 많은 데이터가 있다면 그리고 그 데이터들을 여러개의 노드들에 분산시키기를 원한다면 composite partition key를 사용합니다.



Using a composite partition key

쿼리를 실행시킬 때 결과를 정렬 시켜야 되는 컬럼이 있다면 이 컬럼을 만들 때 primary key 안에 이 composite partition key를 사용하세요. 아래 예제는  rank_by_year_and_name 이라는 테이블을 만듭니다. 여기에는 레이스에서 이긴 사이클 선수의 순위와 이름이 저장될 겁니다. 이 테이블은 primary key의 composition partition key 로 정의된 컬럼으로 race_year 와 race_name을 사용하고 있습니다. 쿼리는 year와 race name 값을 사용해서 레이스에서 우승한 사이클 선수의 순위를 알아내는데 사용됩니다.

composite partition key 테이블은 아래와 같이 두가지 방법으로 생성할 수 있습니다.



Procedure

    * cycling 키스페이스에 rank_by_year_and_name 이라는 테이블을 생성합니다. composite partition key 로 race_year 와 race_name을 사용합니다. 이 테이블에는 primary key로 사용하는 컬럼인 rank 가 있습니다. 테이블을 만들기 전에, USE 구문을 사용해서 해당 키스페이스로 들어가세요. 이 예제에는 맨 마지막에 primary key 를 정의 합니다. PRIMARY KEY 에서 이중괄호를 사용한 것을 기억하시기 바랍니다.

    cqlsh> USE cycling;
    CREATE TABLE rank_by_year_and_name (
    race_year int,
    race_name text,
    cyclist_name text,
    rank int,
    PRIMARY KEY ((race_year, race_name), rank)
    );




  

USE 구문을 생성해서 해당 키스페이스 안에 들어가는것 대신에 CREATE TABLE 구문에서 키스페이스 이름을 사용함으로서 해당 키스페이스에 테이블을 만들 수 있습니다.

    cqlsh> CREATE TABLE cycling.rank_by_year_and_name (
    race_year int,
    race_name text,
    cyclist_name text,
    rank int,
    PRIMARY KEY ((race_year, race_name), rank)
    );



Compound Primary Key



compound primary key를 가지고 있는 테이블을 만들려면 simple 이나 composite partition key를 사용합니다. 거기에 추가로 clustering 컬럼들을 정의하게 됩니다. Clustering 은 clustering 컬럼들을 정의한 것에 근거해 각 partition 안에 데이터들을 정렬하는 storage engine process 입니다. 대개 컬럼들은 알파벳 오름차순으로 정렬 됩니다. 일반적으로 데이터들의 different grouping은 simplistic choice 보다 더 좋은 read 와 write 결과를 가져오게 됩니다.

데이터는 Cassandra cluster 전체에 걸쳐 distributed 된다는 것을 기억해 두세요. 만약 적은 데이터를 얻기 위해 전체 partition을 read 해야만 한다면 어플리케이션은 large partition으로부터 데이터를 검색해야 하는 부담을 갖게 될 것입니다. physical node 에서 partition key 에 대한 row들이 clustering column들에 근거해 정렬돼 저장됐다면 row들을 검색하는게 아주 효율적일 겁니다. clustering column을 사용하는 테이블내에서 데이터를 그룹핑하는 것은 관계형 데이터베이스의 JOIN 과 비슷하면서 오직 한개의 테이블만 사용하게 됨으로서 훨씬 좋은 performance를 보여줍니다. 이 테이블은 partition key로서 category를 사용하고 clustering column으로서 points를 사용합니다. 각각의 category에 대해 point들은 내림차순으로 정렬된다는 것을 기억해 두세요.



partition으로부터 데이터를 검색하는 것은 clustering column들과 함께 다용도로 사용됩니다. 위 예제를 예로 들면 one-day-races 에 대해 200보다 큰 point 값들을 검색할수 있습니다.

compound primary key를 가지고 있는 테이블을 생성하려면 아래와 같이 합니다.

    * CREATE TABLE 구문의 마지막 컬럼 다음에 PRIMARY KEY 키워드를 넣고 그 키의 column 이름을 넣습니다. 그 컬럼 이름은 괄호로 감싸게 됩니다.



Using a compound primary key

정렬된 결과를 받는 쿼리를 사용할 컬럼을 생성할 때 compound primary key를 사용합니다. 만약 pro cycling 예제가 관계형 데이터베이스용으로 디자인 된다면 races 테이블에 대해 cyclists 테이블에 foreign key를 만들에 서로 관계를 맺게 할 겁니다. 카산드라에서는 이 데이터를 denormalize 하게 됩니다. 왜냐하면 distributed system 에서는 JOIN 은 그렇게 좋은 퍼포먼스를 보여주지 못하기 때문입니다. 이후에 카산드라에서 퍼포먼스를 향상시키기 위해 사용하는 다른 schema를 보게 될 겁니다. Collections와 indexes 는 두개의 데이터 모델링 방법입니다. 아래 예제는 각 race category 에 대해 사이클 선수의 성과 ID, points 가 저장되는 cyclist_category 테이블을 생성합니다. 이 테이블은 partition key로 category를 사용하고 single clustering column으로는 points를 사용합니다. 이 테이블은 points로 정렬된 결과를 검색할 수 있습니다. 검색 내용은 하나의 카테고리 안에 사이클 선수와 그들의 points 리스트가 될 겁니다.

compound primary key 테이블은 아래와 같이 두가지 방법으로 생성할 수 있습니다.



Procedure

    * Compound primary key 를 가지고 있는 table을 만들려면 두개 이상의 primary key를 사용합니다. 이 예제에서는 points에 대해 내림차순으로 보기 위해 WITH CLUSTERING ORDER BY라는 추가적인 절을 사용하고 있습니다. 오름차순이 저장하기에는 좀 더 효율적입니다. 하지만 storage engine의 특성상 내림차순이 더 빠릅니다.

    cqlsh> USE cycling;
    CREATE TABLE cyclist_category (
    category text,
    points int,
    id UUID,
    lastname text,    
    PRIMARY KEY (category, points)
    ) WITH CLUSTERING ORDER BY (points DESC);





    USE 구문을 사용해서 해당 키스페이스 안에 들어가지 않고 테이블을 만들려면 keyspace 이름을 아래와 같이 명시해 주면 됩니다.

    cqlsh> CREATE TABLE cycling.cyclist_category (
    category text,
    points int,
    id UUID,
    lastname text,    
    PRIMARY KEY (category, points)
    ) WITH CLUSTERING ORDER BY (points DESC);

    Note: category와 points의 조합은 cyclist_category 테이블내에서 row를 구별해 주는 unique 한 구분 방법입니다. 같은 category에 대해 한개 이상의 row들이 존재할 수 있는데 이 경우 row들은 다른 points 값을 가지고 있는 경우 일 겁니다. 위 예제를 다시 보세요. 이 데이터를 저장하기 위한 최적화 된 데이터 모델링 인가요? 어떤 condition들이 에러를 유발할 수 있을 까요?



Creating a counter table



Counter는 여러 데이터 모델들에 유용합니다. 예를 들어, 어느 회사에서 회사 홈페이지의 웹페이지 뷰 숫자를 하는 경우 혹은 Scorekeeping 어플리케이션은 온라인으로 현재 게임하는 유저의 숫자를 저장하거나 게임에 접속한 유저의 숫자를 저장하기 위해 counter를 사용할 수 있을 겁니다. 아래 테이블은 id를 primary key로 하고 counter 테이블의 popularity필드에 thumbs up/down 에 근거한 cyclist의 popularity 를 관리하는데 counter 가 사용됨을 보여 줍니다.



distributed database의 Tracking count를 살펴보는 건 흥미로운 일입니다. 카산드라에서는 counter value는 Memtable 안에 위치할 것입니다. 그리고 특정 시기에 한개 이상의 SSTable에 commit 하게 됩니다. 노드들 사이에서 replication (응답) 하는 것은 특정 상황에서 consistency 문제를 야기시킬 수 있습니다. 카산드라의 counter는 counter의 기능을 개선하기 위해 Cassandra 2.1 에서 redesign 됐습니다.다. 그 내용을 자세히 보려면 "What’s New in Cassandra 2.1: Better Implementation of Counters" 를 보세요.

counter는 증가되는 숫자를 저장하기 위한 특별한 column 입니다. counter는 integer 값을 가지면 오름차순/내림차순으로 사용할 수 있습니다. counter는 다른 컬럼들에서 서로 다르게 구현될 수 있기 때문에 counter 컬럼들은 dedicated table에서만 정의 될 수 있습니다.

카산드라 2.1 이상에서는 cassandra.yaml 파일에 여러 counter-related setting을 configure 할 수 있습니다.


Note : 카산드라 2.0.x 에서 사용한 replicate_on_write 테이블 프로퍼티는 카산드라 2.1 에서는 사용하지 않습니다.

counter 컬럼은 index 되거나 delete 될 수 없습니다. counter 컬럼에 데이터를 로드하기 위해서는 혹은 counter의 값을 증가/감소 시키기 위해서는 UPDATE command를 사용합니다. Cassandra는 counter 컬럼을 업데이트 할 때 USING TIMESTAMP 나 USING TTL 은 reject 합니다.

counter 컬럼을 가지고 있는 테이블을 생성하기 위해서는 이렇게 하면 됩니다.

    * counter 와 non-counter 컬럼들을 정의하기 위한 CREATE TABLE을 사용하는데 PRIMARY KEY 정의 하는 일 부분으로 non-counter column들을 사용합니다.




Using a counter

counter 컬럼에 데이터를 로드하기 위해서 혹은 counter 값을 증가/감소시키기 위해서는 UPDATE 명령어를 사용합니다. Cassandra는 counter 컬럼을 업데이트 할 때 USING TIMESTAMP 나 USING TTL 은 reject 합니다.



Procedure

    * counter column을 갖는 테이블 생성.

    cqlsh> USE cycling;
    CREATE TABLE popular_count (
      id UUID PRIMARY KEY,
      popularity counter
      );





    * counter column에 데이터를 로딩하는 방법은 다른 테이블과 좀 다릅니다. 해당 데이터는 insert 되는 것이 아니라 update 된다.

    UPDATE cycling.popular_count
     SET popularity = popularity + 1
     WHERE id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47;

    * counter value를 잘 살펴보세요. popularity 가 1의 값을 가지고 있는 부분을 잘 보세요.


    SELECT * FROM cycling.popular_count;





    * 추가적인 증가/감소는 counter column의 value를 바꿀 겁니다.




Create table with COMPACT STORAGE

테이블을 생성하면서 compound primary key를 할 때 저장된 모든 데이터에 대해 그것과 함께 column 이름이 저장될 필요가 있습니다. 디스크에 하나의 컬럼으로서 각 컬럼에 대응하는 저장된 각각의 non-primary key 컬럼 대신에 디스크에 전체 row를 한개의 컬럼에 저장합니다. 디스크 공간을 절약해야 할 경우 WITH COMPACT STORAGE를 사용합니다. 그러면 legacy (Thrift) storage engine format 으로 데이터를 저장합니다.

CREATE TABLE sblocks (
  block_id uuid,
  subblock_id uuid,
  data blob,
  PRIMARY KEY (block_id, subblock_id)
)
WITH COMPACT STORAGE;





Compact storage 를 사용하면 compount primary key가 아닌 한개 이상의 컬럼을 정의해야 되는것을 막아 줍니다. compact 되지 않은 primary key를 사용하는 compact table은 primary key 가 아닌 여러개의 컬럼들을 가질 수 있습니다.

compound primary key를 사용하는 compact table은 적어도 한개 이상의 clustering 컬럼을 정의 해야만 합니다. compact table이 생성되고 나면 컬럼들은 추가되거나 제거될 수 없습니다. WITH COMPACT STORAGE 를 사용하지 않으면 CQL은 non-compact storage로 테이블을 생성합니다.

Collection과 static 컬럼들은 COMPACT STORAGE tables 에 사용될 수 없습니다.



Table schema collision fix

Dynamic schema 생성이나 update는 에러를 유발하는 schema collision 을 발생 시킬 수 있습니다.

Procedure

    1. schema match를 위해 모든 노드에서 전체적으로 재 시작을 합니다. 모든 노드가 있는 클러스터에서 nodetool을 실행합니다. 그리고 한개의 schema 버전이 있는지 확인합니다.

    2.각 노드에서 - data directory 를 체크 한다. - 질문에 해당하는 테이블에 대한 두개의 디렉토리를 찾는다. 만약 한개의 디렉토리만 있다면 다음 노드로 이동한다. 만약에 그 노드에 두개 이상의 디렉토리가 있다면 오래된 테이블 디렉토리는 업데이트 이전것이고 새것은 update 이후를 위한 것이다. 다음으로 간다.

    $ cqlsh -e "SELECT * FROM system.schema_column_families" |grep <tablename>

    3. 어떤 cf_id (column family ID) 가 system.schema_columnfamilies 의 최신 테이블 ID 인지 찾는다. 이 column family ID는 결과의 5번째 컬럼이다.
   
    Move the data from the older table to the newer table's directory and remove the old directory. Repeat this step as necessary.
    4. 이전 테이블의 데이터를 최근 테이블의 디렉토리로 옮기고 이전 디렉토리는 지운다. 이 작업을 다른 노드들에서도 계속 반복한다.

    5. nodetool refresh 를 실행한다.

반응형


반응형

Creating a table


CQL 에서는 SQL 과 마찬가지로 컬럼의 row들을 가지고 있는 테이블들에 데이터가 저장된다.

Note: 카산드라 내부에서 구현되는 row와 column (행과 열) 들은 SQL 과 똑같지 않다. 좀 더 자세한 내용은 A thrift to CQL3 upgrade guide or CQL3 for Cassandra experts 를 보세요.

테이블은 runtime 시 업데이트나 쿼리를 block 하지 않으면서 create, drop, alter 될 수 있다. 테이블을 생성할 때 primary key와 컬럼들을 테이블 property들과 함께 정의한다. 테이블 프로퍼티를 configure 하기 위해서는 optional로 WITH clause와 keyword argument들을 사용한다. "caching, compaction, and other operations" 테이블 프로퍼티들은 테이블 세팅처럼 테이블별로 특정된다.


Create schema using cqlsh

cqlsh 를 사용해서 테이블 스키마를 생성한다. Dynamic 하게 schema를 생성 하는것은 지원되지 않는다. 만약 여러 client들이 동시에 테이블을 generate 하기 위한 시도가 있을 경우 collision 이 생길 수도 있다. 만약 이렇게 collision이 일어날 경우 해결하는 방법은 schema collision fix instructions를 보면 나와있다.

Primary Key

primary key는 data storagy은 순서와 위치를 지정한다. 이 primary key는 테이블이 생성될 때 정의되고 alter 되지 않는다. 만약 primary key 의 변경이 필요하면 새로운 table schema를 생성하고 그 새로운 테이블에 데이터를 입력해야 한다. 카산드라는 어떤 node가 특정 테이블 row를 hold 할 것인가를 식별하는 partition row store, partition key, primary key 의 component 이다. 테이블 생성 이후에 그 테이블을 alter 하는 자세한 방법은 ALTER TABLE 을 보라.

프라이머리 키는 반드시 partition key를 가져야 한다. Composite partition key는 data set을 split 할 수 있다. 그래서 관련된 데이터가 각각의 partition들에 분산되서 저장된다. Compound primary key는 partition에 data를 order 한 clustering column을 포함한다.

테이블의 primary key를 정의하는 것은 카산드라에서는 아주 중요한 부분이다. 어떤 컬럼이 primary key 가 될 것인지 정의하기 이전에 데이터가 테이블에 어떻게 insert되고 retrive 될 것인지를 충분히 고려한 후 모델링을 해야 한다. 클러스터의 노드들 사이에서의 partition들의 size, partition안의 데이터의 순서, partition들의 distribution 이런 것들은 테이블에 대한 베스트 primary key를 결정하기 위해 고려되어야 할 사항들이다.


Table characteristics


테이블 이름으로는 알파벳 문자로 이뤄진 string과 밑줄 (맨처음은 안 됨)을 사용할 수 있다. 테이블 이름을 정할 때

    다른 keyspace에 테이블을 생성할 경우.  keyspace 이름과 table 이름을 구분하기 위해 . (점)을 사용한다. 이렇게 하면 다른 키스페이스에 있더라도 원하는 키스페이스에 해당 테이블을 생성할 수 있다.
    현재 위치한 키스페이스를 사용할 경우. 테이블 이름만 명시하면 된다.

Column characteristics

컬럼은 CQL table의 중요한 component 이다. 테이블 스키마를 디자인하는데 유연성을 주기위해 여러개의 컬럼 타입들이 존재한다. 테이블 내의 각각의 컬럼은 테이블을 생성할 때 데이터 타입을 할당하게 된다. 컬럼 타입들은 collection-type 컬럼을 제외하고는 괄호를 사용해서 표시하고 컬럼이름과 type 의 쌍은 쉼표로 구분된다. 다음 예제는 UUID, text 그리고 timestamp 등 세개의 데이터 타입을 사용한다.

CREATE TABLE cycling.cyclist_alt_stats ( id UUID PRIMARY KEY, lastname text, birthday timestamp, nationality text, weight text, height text );





Collection column 타입은 map, set, list를 지원한다. 이 컬렉션 컬럼은 collection type을 사용해서 정의되고 그 다음에 꺽쇠 괄호 안에 int나 text 등의 다른 타입이 온다. 다음 예제는 각 collection type을 지정한다. 하지만 실제 쿼리를 위해 디자인 하는 것은 아니다.

CREATE TABLE cycling.whimsey ( id UUID PRIMARY KEY, lastname text, cyclist_teams set<text>, events list<text>, teams map<int,text> );





현재로서는 Collection type은 nest 될 수 없다. (collection 안의 또 다른 collection은 불가능하다.) Collection은 frozen data type을 포함할 수 있습니다. 그 사용예와 사용법을 보시려면  Collection type 을 보세요.
컬럼 타입인 tuple 데이터 타입은 입력된 positional field의 고정된 length 값을 가진다. tuple을 사용저 정의 타입의 대안으로 사용한다. tuple은 많은 field (32768)들을 가질 수 있다. 대개 tuple은 적은 수의 field로 생성한다. tuple은 대개 2~5 개 필드들에 사용된다. 테이블에 tuple을 생성하려면 꺽쇠 괄호를 사용한다. 그리고 tuple component type을 정의하기 위해 쉼표로 구분한다. Tuple은 nest 될 수 있다. (Tuple 안에 또 다른 Tuple을 만들 수 있다.) 다음 예제는 text 필드로 구성되고 또 그 안에 두개의 float 필드들이 있는 tuple 타입을 만드는 것을 보여 준다.

CREATE TABLE cycling.route (race_id int, race_name text, point_id int, lat_long tuple<text, tuple<float,float>>, PRIMARY KEY (race_id, point_id));





Note : 카산드라 2.1.0 에서 2.1.2 까지는 tuple을 위해 frozen을 사용해야 한다. 2.1.3 이상의 버전에서는 이 keyword가 꼭 필요하지는 않다.

frozen <tuple <int, tuple<text, double>>>

좀 더 자세한 정보는 "Tuple type" 을 보세요.

사용자 정의 타입 (UDTs)은 여러 필드들의 데이터 타입이 CREATE TYPE 을 사용해 생성할 때 컬럼 타입으로 사용될 수 있다. UDT는 여러 테이블 정의에서 사용되어질 컬럼 타입일 경우 사용된다. 사용자 정의 컬럼 타입은 frozen 키워드를 사용한다. frozen 은 여러 컴포넌트들을 하나의 value로 serializes 한다. Non-frozen type은 개별적인 필드에 대해 update할 수 있도록 한다. 카산드라는 frozen type의 value를 blob로 처리한다. keyspace의 scope : 키스페이스 이름 다음에 점을 찍고 타입의 이름을 사용한다. 이렇게 하면 (점을 사용하면) 카산드라는 해당 키스페이스의 타입에 접근하면서도 현재의 키스페이스를 바꾸지는 않는다. 다른 방법으로 키스페이스를 특정 하지 않으면 카산드라는 현재의 키스페이스 내에서 작업할 것이다. 자세한 사항은 "Using a user-defined type" 를 보라.

counter는 점차 증가하는 숫자를 저장하는데 사용되는 특별한 컬럼이다. countercounter data type 컬럼을 포함하고 있는 dedicated table에만 사용될 수 있다.



Using the keyspace qualifier

가끔 키스페이스를 선택하기 위해 USE 구문을 사용하는 것이 불편할 때가 있다. Connection pooling 은 여러개의 키스페이스를 관리해야 한다. 여러 키스페이스를 tracking 하는 것을 간편화하기 위해 USE 구문 대신에 keyspace  qualifier 를 사용한다. 이 statement들에 keyspace qualifier를 사용해서 키스페이스를 특정할 수 있다.

    ALTER TABLE
    CREATE TABLE
    DELETE
    INSERT
    SELECT
    TRUNCATE
    UPDATE

Procedure


사용할 키스페이스에 있지 않으면서 그 키스페이스 내의 테이블을 특정하려면 그 키스페이스 이름을 적고 그 다음에 점을 찍고 그리고 테이블 이름을 명시한다.
예를 들어 cycling.race_winners 이렇게 하면 cycling 키스페이스 안의 race_winners 테이블을 명시하는 것이다.

cqlsh> INSERT INTO cycling.race_winners ( race_name, race_position, cyclist_name ) VALUES (
  'National Championships South Africa WJ-ITT (CN)',
  1,
  {firstname:'Frances',lastname:'DU TOUT'}
);


Simple Primary Key

간단한 프라이머리 키를 가지고 있는 테이블에 대해 카산드라는 partition key로서 한개의 column name을 사용한다. 이 primary key는 이 경우 오직 한개의 partition key만을 갖게 되는 것이다. 만약 많은 양의 데이터가 사용되고 -프라이머리 키가 정의된 컬럼에 대해 적은 value만이 insert 되는 경우- 그 partition은 더 커지게 된다. Large partition은 read request에 대해 반응 속도가 느리거나 노드의 할당된 디스크공간에 대해 너무 크게 증가할 것이다. 다른 한편으로는 간단한 프라이머리 키에 저장된 데이터는 insert 와 retrieve시 속도가 빠를 것이다. 만약 해당 컬럼에 대해 많은 value들이 여러 노드에 걸쳐 partition들이 distribute 될 경우 더 효과적일 것이다.

가끔 카산드라를 사용할 때 부딪히는 첫번째 어려움에는 simple primary key를 가지고 있는 테이블과 관련된 일도 포함된다. 오직 해당 primary key만이 테이블에서 데이터를 retrieve 할 때 명시되어야 한다는 것을 명심하라. (secondary index가 사용되지 않을 경우.) 많은 production table들이 simple primary key 같은 unique identifier 들을 사용한다. 이런 identifier를 살펴보고 테이블의 나머지 데이터를 retrive 하라. 만약 어플리케이션이 simple lookup table이 필요하면 simple primary key를 사용하라. 그 테이블은 primary key로 id를 사용할 것이다.



To create a table having a single primary key, two methods can be used:
single primary key를 가지고 있는 테이블을 생성할 때 두가지 방법을 사용할 수 있다.

   
    CREATE TABLE definition 안의 컬럼 이름 다음에 PRIMARY KEY 라는 키워드를 넣는다.
    CREAET TABLE definition 안의 마지막 컬럼 정의 이후에 PRIMARY KEY 라는 키워드를 넣는다. 그 다음에 그 키의 컬럼 이름을 명시하면 된다. 그 컬럼 이름은 괄호 안에 넣는다.



Using a simple primary key


정렬된 결과를 return 하도록 하는 쿼리에 사용될 컬럼을 생성하기 위해 simple primary key를 사용한다. 이 예제는 ID number 가 저장된 cyclist_name 테이블을 생성한다. 그리고 컬럼들에는 사이클 선수의 이름과 성이 들어간다. 이 테이블은 primary key로 UUID 를 사용한다. 이 테이블은 해당 ID number에 해당하는 사이클 선수의 이름을 찾을 때 사용될 수 있다.


simple primary key 테이블 생성 방법은 다음과 같이 3가지가 있다.

Procedure

    cycling 키스페이스안에 cyclist_name이라는 테이블을 생성한다. primary key로 ID를 사용한다. 이 테이블을 생성하기 이전에 USE 구문으로 해당 키스페이스로 들어간다.

    cqlsh> USE cycling;
    CREATE TABLE cyclist_name ( id UUID PRIMARY KEY, lastname text, firstname text );





    테이블 definition 끝에 primary key 를 지정함으로서 똑같은 결과를 얻을 수 있다.

    cqlsh> USE cycling;
    CREATE TABLE cyclist_name ( id UUID, lastname text, firstname text, PRIMARY KEY (id) );

    USE 구문을 사용하지 않고 키스페이스 이름을 명시함으로서 해당 키스페이스에 테이블을 사용할 수 있다.

    cqlsh> CREATE TABLE cycling.cyclist_name ( id UUID, lastname text, firstname text, PRIMARY KEY (id) );

    Cassandra 2.2 이상 버전에서는 date 나 time data type을 사용할 수 있다.

    CREATE TABLE cycling.birthdays (id UUID PRIMARY KEY, bday date, btime time);



제가 설치한 카산드라에서 이 구문을 실행했더니 위와 같은 에러가 나오네요.


그래서 카산드라 버전을 확인해 봤습니다.


select peer, release_version from system.peers;
select release_version from system.local;




역시나 제가 설치한 카산드라 버전은 2.1.11.969 이네요.


여기까지 테이블 생성 관련해서 Simple Primary Key 까지 공부했습니다.


다음은 테이블 생성이 나머지 부분을 공부해 보겠습니다.

반응형
이전 1 2 3 4 5 6 7 ··· 10 다음