반응형

검색의 기본, 여러 데이터 중에 특정 대상만 불러오는것

기본이자 가장 어려운 파트라고도 할 수 있다.

 

특히, 보기좋은 코드가 개발자를 기분좋게하는데 검색쿼리를 짜다보면 자칫 코드가 길어질수있다.

여러가지 조건들이 붙기 때문이다.

 

그와중이 addFields를 남발하여  길어져버린 나의 코드가 눈에띄었다.

어디에 주로 addFields를 썼나 보니,

$lookup을 통해 다른 collection과 join한뒤 특정 document만 불러오고자 할때 리스트를 벗겨내는 과정에서 남용했다.

 

{
  $lookup: {
    from: 'users',
    localField: 'userId',
    foreginField: '_id',
    as: 'user'
  }
}

// 결과 => [{ _id: ObjectId('...'), name: '...', ... }]
// 원하는 데이터는 => { _id: ObjectId('...'), name: '...', ... }
// 따라서, 사용했던 코드

{
  $lookup: {
    from: 'users',
    localField: 'userId',
    foreginField: '_id',
    as: 'user'
  }
},
{
  $addFields: {
    user: {
      $arrayElemAt: ['$user', 0]
    }
  }
}

// 결과 => { _id: ObjectId('...'), name: '...', ... }

 

원하는 document만 불러오기 위해 코드가 무려 7줄이나 길어지는 문제가 발생했다.

$addFields는 이런식으로 쓰라고 있는 기능도 아닐것으로 생각이 든다.

$addFields는 저장된 데이터는 아니지만 클라이언트에 필요한 데이터, 예를들어 sum, count 등의 데이터들을 추가하기 위해 존재할것이다.

 

그렇다면, 무엇이 적절할까. 정답은 이미 나와있다.

 

{
  $lookup: {
    from: 'users',
    localField: 'userId',
    foreignField: '_id',
    as: 'user'
  }
},
{
  $project: {
    ...,
    user: { $arrayElemAt: ['$user', 0] }
  }
}

 

7줄의 코드가 아닌 단 한줄의 코드면 충분하다.

 

반응형
반응형

몽고DB에서 어떤 Array 필드에 아무런 값도 존재하지 않는 경우에는 필터링하는 방법에 대해 알아보겠습니다.

 

일반적으로 필드에 값이 존재하는지 판단할때는 $exists를 사용합니다.

...,
{
  $match: {
    name: { $exists: true }
  }
},
...

이와 같이 아주 간단히 처리할수 있습니다.

 

그치만,  필드 타입이  Array인 경우 또 다른 조치를 취해주어야합니다.

빈 값이라도 []가 들어가있다면 true로 인식하기 때문입니다.

 

따라서, 다음과 같이 처리할수 있습니다.

// 첫번째 방법
...,
{
  $match: {
    foods: {
      $exists: true,
      $ne: []
    }
  },
},
...

// 두번째 방법
...,
{
  $match: {
    foods: {
      $exists: true,
      $not: { $size: 0 }
    }
  },
},
...

두 방법다 유효하며, 저는 두번째 방법을 즐겨씁니다.

두 방법의 연산속도의 차이나 이런 부분에 대해서는 아직 잘 모르지만,

일반적으로 단순값을 통한 비교(여기서는 [] )보다는 아래와 같은 방식이 더 정확하다고 생각하기 때문입니다.

반응형
반응형

mongo에서 join을 위해 aggregate하는 경우

원하는 값만 얻기위해 $project를 쓰고는 한다.

 

한걸음 나아가 $project를 응용하여 사용하는법을 알아보고자 한다.

 

이번에 다소 특수한 경우를 처리해야하는 상황이 발생했다.

예를들어, aggregate한 데이터들 중

...
$project: {
  "오늘의 요리사": {
    "반찬 담당자": [...], // 10명
    "찌개 담당자": [...], // 10명
    "밥 담당자": [...], // 3명
    ...
  }
}
...

위와 같은 상황이다. (이는 단지 예시이다)

나의 경우에는 각 데이터들의 개수가 1:1:1로 유지되어야하는데 밥 담당자의 크기가 다른 데이터들과 달라 null값에 대한 처리를 해줘야하는 경우였다.

 

검색을 해본 결과, 무려 $map이라는 기능이 있음을 발견했다. 친숙한 키워드가 나타나자 너무 행복했다.

...
$project: {
  "오늘의 요리사": {
    "반찬 담당자": "오늘의 요리사.반찬 담당자",
    "찌개 담당자": "오늘의 요리사.찌개 담당자,
    "밥 담당자": {
      $map: {
        $input: "오늘의 요리사",
        as: "e",
        in: {
          $cond: {
            if: { $eq: [ "$$e.밥 담당자", null ] },
            then: { "밥 담당자": "-" },
            else: { "밥 담당자": "$$e.밥 담당자" }
          }
        }
      }
    },
    ...
  }
}
...

위와 같이 조건식을 적용하여 해결할 수 있었다.

예시가 다소 이상하여 언제쓰는건지 헷갈릴수 있으나,

이런식의 array타입에 대한 조건식은 분명히 많이쓰일것으로 생각되어 정리해본다.

반응형
반응형

NoSQL을 다루다보면 제목과 같은 일이 제법 빈번히 발생한다.

const ProductSchmea = new mongoose.Schema({
  ...
  comments: [
  	{
      userId: ...,
      comment: ...,
      createdAt: ...,
      updatedAt: ...,
    }
  ]
});

 

위와 같은 설계를 하는일이 제법 많기때문이다.

상품에 대한 댓글을 위처럼 관리하면 매우 쉽기 때문이다.

그런데 comments를 수정하려면 어떻게 할까...? 가 문제다.

 

먼저, comments를 추가하는것은 제법 간단하다.

...
try {
  const commentInfo = {
    userId: ...,
    comment: ...,
  };
  
  await Prodcut.findOneAndUpdate({ _id: id }, { $push: { comments: commentInfo } }).exec();
} catch(error) {
  ...
}
...

이런식으로 push해서 넣으면 끝..!

 

대망의 수정이다.

알고나면, ? 쉽네 뭐야 어떻게 된거지? 라고 할만큼 쉽다.

하지만, 모르면 도저히 그 방법을 찾을수가없다.

 

...

try {
  await Product.findOneAndUpdate(
    { _id: id, 'comments._id': commentId }, 
    { $set: { 'comments.$.comment': '수정해버림' } 
  }).exec();
  
} catch(error) {
  ...
}

...

포인트는 두가지이다.

1. comment._id를 알아야한다는 것.

2. comments.$.comment

여기서 .$은 javascript의 [n]을 대신해준다.

 

위의 정보는 은근히 찾기 어려워 이렇게 간단히 주석을 남긴다.

반응형

'개발, 코딩 > RDB|SQL|NoSQL' 카테고리의 다른 글

Mongo, $project에서 array타입 null체크하여 처리하기  (0) 2020.04.21
$text - mongo  (0) 2020.04.07
react-native, webview 마스터  (0) 2019.11.19
mongodb, 배열처리하기  (0) 2019.11.19
AWS, s3 이미지 업로드  (0) 2019.11.19
반응형

NoSQL에서 배열은 DB의 자료형으로써 매우 자주 쓰이는 편입니다.

 

어떻게 array에 특정 조건을 걸어서 값을 필터링하는지 간단히 살펴보겠습니다.

 

* 키워드는 elemMatch

const filtered_examples = await ExampleCollection.aggregate([
	{
		$lookup: {
			from: 'joinedCollection',
			localField: 'localField,
			foreignField: 'joinedCollectionField',
			as: 'retunName',
		},
	},
	{
		$match: {
			examples: { $elemMatch: { name: 'johndoe' } },
		},
	},
])

 

반응형
반응형

javascript에서는  typeof를 이용해 array 타입을 분별해 낼 수 없다.

typeof가 array를 object로 보기 때문이다.

따라서, array타입을 체크하기 위해서는 Array.isArray([1,2,3]);과 같은 방법으로 판별하도록 한다.

반응형
반응형

const user_list = [{ id: 1 }, { id: 2, }, { id: 1 }, { id: 3, } ];

const unique_user = user_list.reduce((prev, now) => {

  if (!prev.some(obj => obj.id !== now.id })) {

    prev.push(now);

  }

  return prev;

}, []);

반응형