2016년 12월 20일 화요일

Document CRUD - Read

Tags


All

  • collection.all()
all은 cursor를 반환한다. cursor는 toArray를 통해 모든 리스트를 볼 수 있고, next, hasNext등으로 하나씩 결과를 볼 수 있다.
arangosh> db.five.save({ name : "one" });
{ 
  "_id" : "five/103", 
  "_key" : "103", 
  "_rev" : "_UOaqyO----" 
}
arangosh> db.five.save({ name : "two" });
{ 
  "_id" : "five/107", 
  "_key" : "107", 
  "_rev" : "_UOaqyOG---" 
}
arangosh> db.five.save({ name : "three" });
{ 
  "_id" : "five/110", 
  "_key" : "110", 
  "_rev" : "_UOaqyOK---" 
}
arangosh> db.five.save({ name : "four" });
{ 
  "_id" : "five/113", 
  "_key" : "113", 
  "_rev" : "_UOaqyOK--_" 
}
arangosh> db.five.save({ name : "five" });
{ 
  "_id" : "five/116", 
  "_key" : "116", 
  "_rev" : "_UOaqyOK--A" 
}
arangosh> db.five.all().toArray();
[ 
  { 
    "_key" : "107", 
    "_id" : "five/107", 
    "_rev" : "_UOaqyOG---", 
    "name" : "two" 
  }, 
  { 
    "_key" : "116", 
    "_id" : "five/116", 
    "_rev" : "_UOaqyOK--A", 
    "name" : "five" 
  }, 
  { 
    "_key" : "113", 
    "_id" : "five/113", 
    "_rev" : "_UOaqyOK--_", 
    "name" : "four" 
  }, 
  { 
    "_key" : "103", 
    "_id" : "five/103", 
    "_rev" : "_UOaqyO----", 
    "name" : "one" 
  }, 
  { 
    "_key" : "110", 
    "_id" : "five/110", 
    "_rev" : "_UOaqyOK---", 
    "name" : "three" 
  } 
]

결과의 개수를 제한하기 위해 limit과 skip을 활용한다.
arangosh> db.five.save({ name : "one" });
{ 
  "_id" : "five/123", 
  "_key" : "123", 
  "_rev" : "_UOaqyPW---" 
}
arangosh> db.five.save({ name : "two" });
{ 
  "_id" : "five/127", 
  "_key" : "127", 
  "_rev" : "_UOaqyPa---" 
}
arangosh> db.five.save({ name : "three" });
{ 
  "_id" : "five/130", 
  "_key" : "130", 
  "_rev" : "_UOaqyPa--_" 
}
arangosh> db.five.save({ name : "four" });
{ 
  "_id" : "five/133", 
  "_key" : "133", 
  "_rev" : "_UOaqyPa--A" 
}
arangosh> db.five.save({ name : "five" });
{ 
  "_id" : "five/136", 
  "_key" : "136", 
  "_rev" : "_UOaqyPa--B" 
}
arangosh> db.five.all().limit(2).toArray();
[ 
  { 
    "_key" : "136", 
    "_id" : "five/136", 
    "_rev" : "_UOaqyPa--B", 
    "name" : "five" 
  }, 
  { 
    "_key" : "127", 
    "_id" : "five/127", 
    "_rev" : "_UOaqyPa---", 
    "name" : "two" 
  } 
]

Query by Example

  • collection.byExample(example)
  • collection.byExample(path, value, ...)
JSON 값으로 조회하는 기능이다.
arangosh> db.users.save({ name: "Gerhard" });
{ 
  "_id" : "users/143", 
  "_key" : "143", 
  "_rev" : "_UOaqyQC---" 
}
arangosh> db.users.save({ name: "Helmut" });
{ 
  "_id" : "users/147", 
  "_key" : "147", 
  "_rev" : "_UOaqyQG---" 
}
arangosh> db.users.save({ name: "Angela" });
{ 
  "_id" : "users/150", 
  "_key" : "150", 
  "_rev" : "_UOaqyQG--_" 
}
arangosh> db.users.all().toArray();
[ 
  { 
    "_key" : "147", 
    "_id" : "users/147", 
    "_rev" : "_UOaqyQG---", 
    "name" : "Helmut" 
  }, 
  { 
    "_key" : "143", 
    "_id" : "users/143", 
    "_rev" : "_UOaqyQC---", 
    "name" : "Gerhard" 
  }, 
  { 
    "_key" : "150", 
    "_id" : "users/150", 
    "_rev" : "_UOaqyQG--_", 
    "name" : "Angela" 
  } 
]
arangosh> db.users.byExample({ "_id" : "users/20" }).toArray();
[ ]
arangosh> db.users.byExample({ "name" : "Gerhard" }).toArray();
[ 
  { 
    "_key" : "143", 
    "_id" : "users/143", 
    "_rev" : "_UOaqyQC---", 
    "name" : "Gerhard" 
  } 
]
arangosh> db.users.byExample({ "name" : "Helmut", "_id" : "users/15" }).toArray();
[ ]
아래와 같이 cursor를 담아두고 하나씩 print할 수도 있다.
arangosh> var a = db.users.byExample( {"name" : "Angela" } );
arangosh> while (a.hasNext()) print(a.next());
{ 
  "_key" : "170", 
  "_id" : "users/170", 
  "_rev" : "_UOaqyQ6--_", 
  "name" : "Angela" 
}
byExample을 사용한 검색은 equals 매칭이다.
예를 들어 아래와 같은 document로 byExample 검색을 한다면

{ "a" : { "c" : 1 } }
{ "a" : { "c" : 1 }, "b" : 1 } //이것은 find 될 것이다.
{ "a" : { "c" : 1, "b" : 1 } } //이것은 해당되지 않는다.
그런데 field에 .(dot)는 path로 해석되는데 아래와 같은 example로 찾게 된다면 a attribute의 c attribute 값이 1인 것을 찾는다.
{ "a.c" : 1 }
위 두 경우 모두 찾을 수 있다.
{ "a" : { "c" : 1 }, "b" : 1 } //found

{ "a" : { "c" : 1, "b" : 1 } } //found

 array를 찾고자 한다면 array가 통째로 일치해야 한다.

두 번째 signiture로 할 경우 예제는 아래와 같다. path에서 .(dot)를 path로 인식한다.

path가 없으면 전체 리스트의 cursor객체를 리턴한다.


아래는 일치하는 하나만 리턴한다. MongoDB에서 findOne과 유사하다.
  • collection.firstExample(path, value1, ...)
일치하는 것 중 첫 번째만 return한다.
일치하는게 없다면 null이 리턴된다.
순서를 지정하지 않을 경우 어떤 것이 첫 번째가 되는지는 알 수 없다.

Any

collection에서 아무 document나 리턴한다. collection이 비어있다면 null을 return한다. 신박한 기능이다.
  • collection.any()
any의 결과는 매번 호출할 때마다 다르다.

Count

collection에서 document의 수를 리턴한다.
  • collection.count();

Exist

document가 존재하는지 여부를 리턴한다.
  • collection.exists(object)
  • collection.exist(id or key)
  • collection.exist(array)
주어진 object의 _id나 _key로 존재하는지 판단한다. 둘 다 주어지면 _id로 먼저 판단한다. _id가 주어질 경우 _id의 collection과 평가하려는 collection이 일치하지 않을 경우 에러가 발생한다. _rev가 주어진 경우 _id나 _key로 검사하고 검색된 값이 주어진 _rev와 다르면 에러를 던진다. 만약 document가 검색되었을 경우 해당 document의 _id, _key, _rev가 리턴되며, 찾지 못할 경우 false가 리턴된다. 여러 개를 한꺼번에 평가할 경우 array로 전달한다. 만약 적절한 object 형태가 아니거나, id or key의 형식이 아니거나(빈 문자열 같은), 찾으려는 값이 속한 collection과 쿼리가 이루어지는 collection이 다르면 에러가 발생한다. array로 검색할 경우, 에러가 발생하면 즉시 멈추고 에러를 반환한다.

Lookup by keys

  • collection.documents(keys)
key 배열을 넘겨서 document를 찾아 리턴한다. document가 없는 key는 무시된다. 에러도 throw되지 않는다.

Lookup by key

_id, 혹은 _key로 찾을 때 사용한다. 둘 다 주어질 경우 _id로 먼저 찾는다. 주어진 _id의 collection명과 찾으려는 collection이 일치하지 않을 경우 에러가 발생한다.
  • collection.document(obj)
  • collection.document(key or _id)
  • collection.document(array)  //up to 3.x
_rev를 줄 수도 있는데 만약 _id or _key와 _rev을 주었는데 find 된 document(_id or key로 검색된)가 _rev가 다른 경우 error가 발생함. arangod server에서 실행되는 환경이라면(ex. Foxx app) 성능상의 이유로 immutable document가 반환되며 이 값의 속성들을 바꿀 수 없다. 바꾸고 싶다면 javascript object로 copy 후 사용해야 한다.

_id로 검색하는 예제
arangosh> db.example.document("example/2873916");
{ 
  "_key" : "2873916", 
  "_id" : "example/2873916", 
  "_rev" : "_UPtzFti---" 
}
 _key로 검색하는 예제
arangosh> db.example.document("2873916");
{ 
  "_key" : "2873916", 
  "_id" : "example/2873916", 
  "_rev" : "_UPtzFrO---" 
}
object로 검색하는 예제
arangosh> db.example.document({_id: "example/2873916"});
{ 
  "_key" : "2873916", 
  "_id" : "example/2873916", 
  "_rev" : "_UPtzFsK---" 
}

array로 검색하는 예제
arangosh> db.example.document(["2873916","2873917"]);
[ 
  { 
    "_key" : "2873916", 
    "_id" : "example/2873916", 
    "_rev" : "_UPtzFtC---" 
  }, 
  { 
    "_key" : "2873917", 
    "_id" : "example/2873917", 
    "_rev" : "_UPtzFtC--_" 
  } 
]
못 찾을 경우 에러
arangosh> db.example.document("example/4472917");
[ArangoError 1202: document not found]
잘못된 형식일 경우 에러
arangosh> db.example.document("");
[ArangoError 1205: illegal document handle]



Production에서는 절대 쓰지말아야 할 메소드

toArray

  • collection.toArray()
collection전체를 document array로 바꾼다. 모두 RAM에 올라가기 때문에 document 수에 따라 resource를 많이 잡아먹을 수도 있다.

@Deprecated method이지만 3.x까진 살아 있는 메소드

Range
Closed Range

Document CRUD - Create

Tags

Document CRUD

Insert

  • collection.insert(data, option)
data는 object여야 하고, _id, _rev값은 무시되고 ArangoDB가 자동으로 생성된 값을 넣는다. _key값이 주어지지 않으면 자동 생성한다. 만약 _key값이 주어지면 collection내에서 유일한 값이어야 한다.
insert후 _id, _key, _rev값이 리턴된다.
arangosh> db.example.insert({ Hello : "World" });
{ 
  "_id" : "example/14800", 
  "_key" : "14800", 
  "_rev" : "_UOasXsi---" 
}
arangosh> db.example.insert({ Hello : "World" }, {waitForSync: true});
{ 
  "_id" : "example/14804", 
  "_key" : "14804", 
  "_rev" : "_UOasXsi--_" 
}

  존재하는 _key인 336846을 직접 지정해서 insert에 실패했다.
 _id를 직접 지정해도 무시된다

 없는 key를 지정하면 해당 key로 insert에 성공한다

option은 다음 항목이 있다.
  • waitForSync(boolean)
  • silent(boolean, >= 3.0) : true일 경우 insert 후 아무것도 리턴하지 않는다.
  • returnNew(boolean, >=3.0) : true일 경우 return되는 값의 new 필드에 생성된 값을 넣고 이 document를 반환한다.
 silent = true일 경우 _key, _id, _rev값이 반환되지 않는다.
 returnNew를 true로 할 경우, new 필드에 insert된 document를 포함하여 리턴한다.

참고) ArangoDB 2.2이후부터 insert는 save의 alias이다.

ArangoDB3.0부터는 data에 array가 허용된다.
  • collection.insert(array, options)
arangosh> db.example.insert([{ Hello : "World" }, {Hello: "there"}])
[ 
  { 
    "_id" : "example/14781", 
    "_key" : "14781", 
    "_rev" : "_UOasXra---" 
  }, 
  { 
    "_id" : "example/14785", 
    "_key" : "14785", 
    "_rev" : "_UOasXra--_" 
  } 
]
이 경우 모든 document array가 insert되고, 그에 따른 모든 결과가 반환된다. 만약 에러가 발생하면 에러가 발생한 object는 error object가 반환된다.

 3개의 object를 insert하는데 두 번째 것이 이미 존재하는 _key여서 해당 object만 error를 리턴한다.

Document

Tags

Document

document는 ArangoDB에서 JSON object이다. 예시 형태는 아래와 같다.
{
  "_id" : "myusers/3456789",
  "_key" : "3456789",
  "_rev" : "14253647",
  "firstName" : "John",
  "lastName" : "Doe",
  "address" : {
    "street" : "Road To Nowhere 1",
    "city" : "Gotham"
  },
  "hobbies" : [
    {name: "swimming", howFavorite: 10},
    {name: "biking", howFavorite: 6},
    {name: "programming", howFavorite: 4}
  ]
}
document는 기본적으로 3개의 값을 갖는다.
  • _id : document의 고유 id로 collection이름과 _key값의 조합값이다.
  • _key : primary key. 이 값은 document 생성시 사용자가 지정할 수 있다. ArangoDB의 primary index에 의해 자동으로 index된다. 이 값은, Collection내에서 유일한 값이어야 한다. database나 전체에서 유일한 값은 아니다.(no guarantee)
  • _rev : revision. ArangoDB에 의해 관리된다.
위에서 _id, _key는 불변값으로 생성 이후로 변할 수 없다.

Document Revision


ArangoDB는 MVCC(Multiple Version Concurrency Control)를 지원한다. 하나의 document는 여러 revision을 가질 수 있다. revision은 숫자가 포함된 문자열 값이며, 한 document에 대해 고유한 값이다. ArangoDB 3.1이상에서는 _rev 값은 timestamp이다. 이 timestamp는 ArangoDB서버의 local시간이며 millisec 단위이다. 정확히는 "Hybrid Logical Clock"이 사용된다. 만약 동시에 write된다하더라도, 하나의 shard 내에서 한 document의 서로 다른 revision은 서로 다른 _rev값을 갖는 것이 보장된다. 그러나 cluster내 서버 간의 시간 차가 있을 수 있고, 서로 다른 shard나 collection간에 timestamp에 대한 고유값 보장이 불가능하다.

Hybrid Logical Clock에서는 이러한 특징에 대해 다음과 같이 언급했다. :

cluster 내 서버 A -> 서버 B로 메세지가 전송될 때, message 출발과 도착에 약간의 시간이 소요되기때문에 보낸 시간보다 받은시간이 timestamp값이 크다. 해결책 중 하나는 실제 시간보다 미래의 시간의 timestamp 값을 취하는 것인데 시간차가 크지 않다면 상대적으로 정확하지만 그래도 시간차는 생길 수 있다.

ArangoDB는 Revision 값을 위해 내부적으로 64bit unsigned integer를 사용한다. client로 document revision이 반환될 때, big integer를 지원하지 않는 client에 의해 값이 잘리지 않게 하기 위해 revision값을 string으로 넣는다. client는 revision을 다루거나 저장할 때 ArangoDB에 의해 문자열로 return 된 값으로 다루어야 한다.

Multiple Documents in a single Command

ArangoDB 3.0부터 document API는 single document뿐만 아니라 multiple document를 한 command로 다룰 수 있게 되었다. 이는 성능면에서 매우 중요하다. 특히 cluster에서는 하나의 request가 여러 network를 거칠 수 있기 때문에 더욱 그렇다. 또다른 이점은 client와 server의 network 의 request가 줄어들 수 있다는 것이다. single command로 여러 document에 작용하기 위한 일반적인 방법은 JSON Array로 command를 보내는 것이다.

2016년 12월 18일 일요일

Collection

Tags

Collection

Collection은 Document의 집합으로, RDB의 Table과 유사한 개념이다.

Collection Method

생성

  • db._create("collection name", properties, type)
test database에서 test_collection이라는 이름의 collection을 생성했다.
id가 11983인 collection이 생성되었다.
이제 test_collection에 아래와 같이 접근할 수 있다.
  • db.test_collection.{some method}
properties는 아래 Collection 속성을 참고하도록 한다.
type은 document 이거나 edge 둘 중 하나이다.
type을 주지 않으면 default로 document이다. 아래와 같이 type이 필요없는 전용 method도 있다.
  • db._createDocumentCollection(collection-name, properties)
  • db._createEdgeCollection(collection-name, properties)

Document Collection 속성

collection에는 여러 옵션이 있는데 아래와 같다.
  • waitForSync : true일 경우, data가 disk에 쓰여진 후 return 된다.
  • journalSize : byte 단위.
  • isVolatile : memory에만 쓰고 persistence로 유지하지 않는다.
  • keyOptions : key 생성을 위한 추가 옵션. 아래 속성을 포함하는 JSON Array여야 한다.
    • type
    • allowUserKeys
    • increment
    • offset
  • indexBuckets
  • numberOfShards
  • shardKeys
위 속성 중, type, isVolatile, keyOptions는 생성된 후 바뀔 수 없다.
현재 속성은 아래와 같이 확인할 수 있으며

아래와 같이 수정할 수 있다.

Edge Collection Properties

  • waitForSync : default false
  • journalSize : default configuration value


Collection 상태 리포트

ArangoDB는 아래와 같은 명령을 통해 collection의 상태에 대한 리포트를 제공한다.
  • collection.figures()
arangosh> db.demo.figures()
{ 
  "indexes" : { 
    "count" : 1, 
    "size" : 32128 
  }, 
  "lastTick" : 97, 
  "uncollectedLogfileEntries" : 0, 
  "compactionStatus" : { 
    "message" : "skipped compaction because collection has no datafiles", 
    "time" : "2016-12-16T10:29:55Z" 
  }, 
  "readCache" : { 
    "count" : 1, 
    "size" : 48192 
  }, 
  "documentReferences" : 0, 
  "waitingFor" : "-", 
  "alive" : { 
    "count" : 1, 
    "size" : 184 
  }, 
  "dead" : { 
    "count" : 0, 
    "size" : 0, 
    "deletion" : 0 
  }, 
  "datafiles" : { 
    "count" : 0, 
    "fileSize" : 0 
  }, 
  "journals" : { 
    "count" : 1, 
    "fileSize" : 33554432 
  }, 
  "compactors" : { 
    "count" : 0, 
    "fileSize" : 0 
  }, 
  "revisions" : { 
    "count" : 1, 
    "size" : 48192 
  } 
}
주요 속성은 아래와 같다.
  • alive.count : 모든 data 파일과 collection의 journal에서 active인 document의 수
  • alive.size : active된 document의 size
  • dead.count : 삭제되거나 새로운 document로 대체된 document의 수
  • dead.size : dead document의 총 size
  • dead.deletion : 삭제 표시된 문서의 수
  • datafiles.count : data 파일 수
  • datafiles.fileSize : 총 data 파일 용량
  • journals.count
  • journals.fileSize
  • compactors.count
  • compactors.fileSize
  • shapefiles.count
  • shapefiles.fileSize
  • shapes.count
  • shapes.size
  • attributes.count
  • attributes.size
  • indexes.count
  • indexes.size
  • maxTick
  • uncollectedLogfileEntries
  • documentReferences
  • waitingFor
  • compactionStatus.time:
  • compactionStatus.message
위에서 size에 해당하는 속성들은 실제 파일시스템 상의 size와 약간의 오차가 있을 수 있다. figure에서 표시된 용량보다 실제 용량이 일반적으로 더 크다.


arangosh> db.demo.figures()
{ 
  "indexes" : { 
    "count" : 1, 
    "size" : 32128 
  }, 
  "lastTick" : 97, 
  "uncollectedLogfileEntries" : 0, 
  "compactionStatus" : { 
    "message" : "skipped compaction because collection has no datafiles", 
    "time" : "2016-12-16T10:29:55Z" 
  }, 
  "readCache" : { 
    "count" : 1, 
    "size" : 48192 
  }, 
  "documentReferences" : 0, 
  "waitingFor" : "-", 
  "alive" : { 
    "count" : 1, 
    "size" : 184 
  }, 
  "dead" : { 
    "count" : 0, 
    "size" : 0, 
    "deletion" : 0 
  }, 
  "datafiles" : { 
    "count" : 0, 
    "fileSize" : 0 
  }, 
  "journals" : { 
    "count" : 1, 
    "fileSize" : 33554432 
  }, 
  "compactors" : { 
    "count" : 0, 
    "fileSize" : 0 
  }, 
  "revisions" : { 
    "count" : 1, 
    "size" : 48192 
  } 
}

Load

Collection을 메모리로 올린다.
arangosh> col = db.example;
[ArangoCollection 14508, "example" (type document, status loaded)]
arangosh> col.load();
arangosh> col;
[ArangoCollection 14508, "example" (type document, status loaded)]

Unload

Collection을 메모리에서 내린다. 그러나 query가 모두 끝날 때까지 유예되고, 이후 실행된다.
arangosh> col = db.example;
[ArangoCollection 7351, "example" (type document, status loaded)]
arangosh> col.unload();
arangosh> col;
[ArangoCollection 7351, "example" (type document, status unloaded)]

Rename

  • collection.rename(new-name)

삭제

  • collection.drop(options)
collection의 data와 index 모두 제거한다. system collection을 제거하기 위해서는 option에서 isSystem을 true 값을 주어야 한다.

 

collection의 모든 데이터는 삭제하되, index는 남기는 것은 아래와 같이 truncate를 이용한다.
  • collection.truncate()

 

Revision

Collection은 document의 data가 변경될때 revision id가 바뀐다. revision을 통해 collection의 data가 바뀌었는지 안바뀌었는지 check할 수 있다. revision은 string값을 return한다.

Reserve

  • collection.reserve(number)
collection에 index들의 resize시 미리 잡을 용량을 지정한다. 이는, collection에 bulk insert가 되기 전 reserve를 통해 memory re-allocation과 re-location을 방지할 수 있다.

Checksum

  • collection.checksum(withRevisions, withData)
withRevision은 true일 경우 document의 revision id가 hash 계산에 포함된다.
withData는 true일 경우 모든 document의 속성값이 checksum 대상에 포함된다. 이 경우 느리지만 정확한 checksum이 될 수 있다.

Rotate(TODO)

  • collection.rotate()
현재 collection의 journal이 read-only data파일로 되며 GC 대상이 된다. 만약 현재 collection의 journal이 없다면 에러가 발생한다.


Database Method

단일 Collection 가져오기

  • db._collection(collection identifier like id or name)

만약 없는 collection을 찾고자 한다면 null을 반환한다.

모든 Collection 가져오기

  • db._collections()

Collection 삭제

  • db._drop(collection-name, options)
 Collectio drop이 아닌 truncate는 아래와 같다
  • db._truncate(collection-name)

Collection type

collection.type()
collection의 타입을 리턴하는데, 가능한 타입은 아래 두 개 뿐이다.
  • 2: document collection
  • 3: edge collection

Collection Name Convention

  • 영어 대소문자
  • 숫자
  • underscore(_), dash(-)
  • system collection만 underscore로 시작할 수 있으며 사용자 정의 collection은 항상 영어 대소문자로 시작해야 함
  • 대소문자 구분
  • 64바이트 이하 길이

ArangoDB 3.x부터 제공되는 Collection의 Replication 동기화 관련

TODO