그래서 최근에 N1QL 엔진 인스턴스를 실행하는 경우 클러스터에 대해 N1QL 쿼리를 실행할 수 있도록 Node.js 클라이언트에 지원을 추가했습니다(이 지원이 포함된 업데이트된 버전의 Node.js 클라이언트를 받으려면 npm에서 github 마스터 브랜치(https://github.com/couchbase/couchnode)를 가리키세요). 구현할 당시에는 테스트할 대상이 많지 않았기 때문에 뷰를 사용하지 않고 Node.js의 맥주 샘플 예제가 얼마나 멋진지 보는 것도 흥미로운 시도라고 생각했습니다.

먼저 샘플 데이터에서 단순히 모든 맥주 또는 양조장을 선택하는 기본 쿼리를 변환하는 것으로 시작한 다음, 라이브 검색 쿼리도 N1QL을 사용하도록 변환하는 작업으로 넘어갔습니다. 변환 과정에 대한 블로그 게시물을 작성하고 그 과정에서 발견한 몇 가지 사항에 대해 언급하기로 했습니다.

첫 번째 쿼리는 다음과 같습니다:

var q = {
  limit : 엔트리_당_페이지,
  stale : false
};
db.보기( "맥주", "by_name", q).쿼리(함수(err,) {
  var= _.pluck(, 'id');
  db.getMulti(, null, 함수(err, 결과) {
    var 맥주 = _.지도(결과, 함수(v, k) {
      v..id = k;
      반환 v.;
    });
    res.렌더링('맥주/인덱스', {'맥주':맥주});
  })
});

및 변환된 버전입니다:

db.쿼리(
    "SELECT META().id AS id, * FROM beer-sample WHERE type='beer' LIMIT " + 엔트리_당_페이지,
    함수(err, 맥주) {
  res.렌더링('맥주/인덱스', {'맥주':맥주});
});

보시다시피, 더 이상 목록을 검색하기 위해 두 가지 작업을 따로 수행할 필요가 없습니다. 필요한 모든 정보를 반환하고 적절한 형식을 지정하는 N1QL 쿼리를 실행하면 데이터를 다시 포맷하고 ID 값을 추가할 필요 없이 결과 집합의 일부로 간단히 선택할 수 있습니다. N1QL 버전이 훨씬 더 간결하고 쿼리 구성이 간단하다는 점이 마음에 듭니다.

그런 다음 비슷한 경로를 따라 양조장 목록 기능을 변환했는데, 보시다시피 비슷하게 아름답고 간결한 결과물을 얻을 수 있습니다:

db.쿼리(
    "SELECT META().id AS id, name FROM beer-sample WHERE type='brewery' LIMIT " + 엔트리_당_페이지,
    함수(err, 양조장) {
  res.렌더링('양조장/인덱스', {'양조장':양조장});
});

다음으로 검색 방법을 변환했습니다. 원래 코드가 무엇을 달성하려고 하는지 생각하지 않고 직접 보면 의미가 명확하지 않았기 때문에 조금 더 어려웠는데, 그 모습을 살펴보면 다음과 같습니다:

var q = { 시작키 :,
  끝키 :+ JSON.parse(‘”u0FFF"'),
  stale : false,
  limit : 엔트리_당_페이지 }
db.보기( "맥주", "by_name", q).쿼리(함수(err,) {
  var= _.pluck(, 'id');
  db.getMulti(, null, 함수(err, 결과) {
    var 맥주 = [];
    에 대한(var k in 결과) {
      맥주.push({
        'id': k,
        'name': 결과[k]..이름,
        'brewery_id': 결과[k]..brewery_id
      });
    }
    res.보내기(맥주);
  });
});

다시 말하지만, 꽤 간단할 것으로 예상되는 것을 달성하기 위해 꽤 많은 코드가 필요합니다. 잘 모르시는 경우를 대비해 위의 map/reduce 쿼리는 사용자가 입력한 값으로 이름이 시작되는 맥주 목록을 검색합니다. 이를 N1QL LIKE 절로 변환하고, 추가 보너스로 검색어를 처음에 요구하지 않고 문자열의 아무 곳에나 표시할 수 있도록 할 것입니다:

db.쿼리(
    "SELECT META().id, name, brewery_id FROM beer-sample WHERE type='beer' AND LOWER(name) LIKE '%" + 용어 + "1T3PT' 제한 " + 엔트리_당_페이지,
    함수(err, 맥주) {
  res.보내기(맥주);
});

모호하게 이해할 수 있는 대량의 코드를 다시 간단하고 간결한 쿼리로 축소했습니다. 저는 이것이 N1QL의 힘을 보여주기 시작했다고 생각하며, 개인적으로 N1QL이 기대되는 이유이기도 합니다. 하지만 이 작업을 하면서 한 가지 주의할 점이 있는데, 바로 SQL과 마찬가지로 쿼리에 어떤 종류의 사용자 데이터를 전달할지 주의해야 한다는 것입니다. 악의적인 의도를 방지하기 위해 간단한 정리 함수를 작성했지만(현재 N1QL은 어쨌든 읽기 전용이지만), 정리 코드는 결코 광범위하지 않습니다. 제가 발견한 또 다른 문제는 LIKE 절이 포함된 두 번째 쿼리가 맵/축소를 사용할 때보다 N1QL 쿼리로 실행될 때 훨씬 느리게 실행된다는 것입니다. 이는 단순히 N1QL이 아직 개발자 프리뷰 버전이기 때문이며, N1QL 팀에서 해야 할 최적화가 많이 남아있기 때문이라고 생각합니다.

완전히 변환된 소스 코드를 보려면 여기(https://github.com/couchbaselabs/beersample-node/tree/n1ql)에서 제공되는 beersample-node 리포지토리의 n1ql 브랜치를 살펴보세요.  

고마워요! Brett

작성자

게시자 브렛 로슨, 수석 소프트웨어 엔지니어, Couchbase

브렛 로슨은 카우치베이스의 수석 소프트웨어 엔지니어입니다. Brett은 Couchbase Node.js 및 PHP 클라이언트의 설계와 개발을 담당하고 있으며, C 라이브러리인 libcouchbase의 설계와 개발에도 참여하고 있습니다.

댓글 하나

  1. 이 작업은 매우 기대됩니다.

  2. 문자열 연결을 사용하여 이러한 쿼리를 작성하는 예제는 사람들이 \"n1ql-injection\" 취약점에 노출될까 봐 걱정됩니다. 사실 쿼리에서 변수 대체(예: \"%d\" 또는 \"$1\" 등)를 허용하여 값이 이스케이프되도록 하는 것이 더 안전할 것입니다(이런 방식으로 작동할 수도 있지만 확인하지는 않았습니다).

    1. 안녕하세요, 저스틴,
      전적으로 동의하며, 이후 버전에서는 매개변수화 및 치환 기능이 내장되어 있습니다(현재 2.0 SDK에서는 이 기능을 사용할 수 없음). 이 기능은 곧 우아한 솔루션을 만들 수 있기를 바랍니다. N1QL이 정식 버전으로 출시되기 전에 확실히! 의견을 보내주셔서 감사합니다!
      건배, 브렛

댓글 남기기