mongoDB 실시간 데이터 조회

2023. 1. 13. 14:01개발/DB

https://www.mongodb.com/

 

MongoDB: The Developer Data Platform

Get your ideas to market faster with a developer data platform built on the leading modern database. MongoDB makes working with data easy.

www.mongodb.com

평소에 데이터베이스로 mongoDB를 사용한다.

noSQL 형식의 데이터베이스로

편리하게 사용하고있다.

 

이외에 다뤄본 데이터베이스로

Google firebase의 Realtime database가 있다.

 

https://firebase.google.com/?gclid=Cj0KCQiA_P6dBhD1ARIsAAGI7HBpcW1sNnRdCTZOiWFgla_vrkziux_9Y7n5UGT6D0xxHkDHERut5b0aArCfEALw_wcB&gclsrc=aw.ds 

 

Firebase

Firebase는 고품질 앱을 빠르게 개발하고 비즈니스를 성장시키는 데 도움이 되는 Google의 모바일 플랫폼입니다.

firebase.google.com

위와 같이 실시간으로 데이터의 값이

화면 상에서 변한다.

 

mongoDB와의 차이점은

mognoDB는 

위와 같이 refresh 버튼을 눌러줘야

새로고침 되며 값이 최신화 된다.

 

하지만 Realtime database는 실시간 모니터링이 되며

데이터값이 변하면 즉각적으로 화면상에 나타난다.

 

그런 기능을 mongoDB 에서도 구현하고 싶어서

여러가지 시도해보다가

한 가지 기능을 구현하게 됐다.


Realtime database의 기능 모방(?)

 

앞서 말했듯이

Realtime database는

실시간으로 데이터값이 변하며

 

화면 상에서 데이터를 임의로 변경했을 때

그 데이터베이스와 연동되어 있는 장치들에서

즉각적으로 동작이 발생한다.

 

mongoDB에서도 이러한 기능을 구현하고 싶어졌다.

mognoDB 에서 데이터 값을 변경했을 때

뭔가 출력이 발생하도록 구현한다.

위와 같은 데이터들을 

이처럼 변경했을 때

위 데이터베이스에 연결된 무언가가

인식하고 동작하도록 한다.

 

Node-red를 활용해서 구현한다.


mongoDB find.toarray

https://www.mongodb.com/docs/manual/crud/

 

MongoDB CRUD Operations — MongoDB Manual

Docs Home → MongoDB Manual CRUD operations create, read, update, and delete documents.Create or insert operations add new documents to a collection. If the collection does not currently exist, insert operations will create the collection.MongoDB provides

www.mongodb.com

mongoDB 에는 다양한 operation들이 있다.

insertOne, findoneAndUpdate, find,remove,replace... 등등

데이터베이스에 접근한 뒤

여러가지 기능을 사용할 수 있다.

 

그 중에서도 나는 find() 기능을 사용할 것이다.

실시간으로 mongoDB의 데이터를 확인하기 위해

find() 기능이 필수적이다.


Node-red 구성 & mongoDB 데이터 추출

flow를 위와 같이 구성했다.

var newMsg = {};
newMsg.collection = '1_ammoniaSensor';
newMsg.operation  = 'find.toArray';
newMsg.payload    = {};
newMsg.projection = { 'mac' : 1 , '_id' : 0 };
return newMsg;

function에 위와 같이 mongoDB에 동작을 입력해줬다.

1_ammoniaSensor 명의 collection에 접근해서

모든 데이터를 추출하도록 했다.

암모니아 센서의 데이터값들에

접근하도록 설정했다.

이런 식으로 mac, ammonia, time 등 모든 데이터들이

출력되는 걸 확인했다.


추출된 데이터 가공

var newMsg =[];

for (var i = 0; i < msg.payload.length; i++) {
newMsg.push(msg.payload[i].ammonia);
}

msg.payload = newMsg;

return  msg;

function 노드를 뒷단에 추가했다.

불필요한 mac, time, id 데이터를 제외하고

ammonia 값만 추출해서

newMsg 배열에 담았다.

위와 같이 암모니아 값만 추출했다.


데이터 비교

위와 같이 구성했다.

우측 상단의 'global.set' 노드에서

추출된 암모니아 값을 전역변수로 지정한다.

 

그 후 '데이터 비교' 노드에서

var Ammonia = global.get("Ammonia");
var newMsg={};
var count=0;

for(var i=0; i<msg.payload.length; i++){
    if(msg.payload[i]!==Ammonia[i]){
        count++;
    }
}
newMsg.payload=count;
return newMsg;

전역변수 Ammonia를 받아온다.

그 후, 미리 저장된 암모니아 값과

새로 들어온 암모니아 값에 변화가 있을 시

count 값을 증가시킨다. 

 

1. inject를 해서 현재 데이터베이스의 

암모니아 값을 전역변수로 저장

 

2. mongoDB 에서 임의로 데이터 변경

 

3. 다시 inject 해서 새로 변경된

데이터 추출

 

여기서

delay노드로 인해

데이터 임의로 변경 후 새로 추출된 데이터가

전역변수로 지정되기 전에

'데이터 비교' 노드가 먼저 실행되면서

 

1번에 의해 저장된 값과 비교한다.


결과

1번 실행 시 0이 출력

( 데이터의 변화가 없기 때문에 count 가 증가하지 않음 )

 

그리고 나서 데이터 변경 후

inject를 실행하면

 

위와 같이 count가 증가되며 1이 출력


출력 메시지 수정

var Ammonia = global.get("Ammonia");
var newMsg={};

for(var i=0; i<msg.payload.length; i++){
    if(msg.payload[i]!==Ammonia[i]){
        newMsg.payload={"collection": "1_ammoniaSensor", "index":i, "ammonia": msg.payload[i]};
    }
}
return newMsg;

데이터 비교 노드를 수정했다.

count를 출력하지 않고

collection, 변경된 데이터 순서, 변경된 값

을 출력하도록 했다.

1번째 값을 726으로 변경한 뒤 확인해봤다.

위와 같이 index 0과

ammonia 726이 출력된다.

추가적으로 4번째 값을 764로 변경해보았다.

index 3 과 ammonia 764가 출력된다.


단점

하지만 위와 같이 구성하면

확인하고싶을 때 마다

inject를 실행해줘야 한다.

 

따라서 inject가 주기적으로 실행되도록 수정했다.

위와 같이 2초마다 자동 inject 되도록 수정했다.

하지만 위와 같이 설정하면

이처럼 undefined 값이 출력된다.

데이터가 변하지 않았는데 계속 확인하기 때문이다.

 

따라서 메시지 출력이 되기 전에

filter 처리를 해서 값이

이전 메시지와 변화가 있을 때만 출력되도록 한다.


filter 처리

위와 같이 filter 노드를 추가해

메시지 값이 변했을 때만 출력하도록 한다.

 

위와 같이 메시지가 출력되지 않다가

첫번째 데이터를 727으로 수정하면

즉각적으로 변경된 데이터를 확인할 수 있다.

 

더보기 -> Node-red 플로우

더보기

[
    {
        "id": "46ed3356b6c0cbcb",
        "type": "tab",
        "label": "realtime Database 흉내",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "32c40aac33c59403",
        "type": "inject",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": "0",
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 110,
        "y": 200,
        "wires": [
            [
                "4d81ec542dfe43eb"
            ]
        ]
    },
    {
        "id": "4d81ec542dfe43eb",
        "type": "function",
        "z": "46ed3356b6c0cbcb",
        "name": "find.toArray",
        "func": "var newMsg = {};\nnewMsg.collection = '1_ammoniaSensor';\nnewMsg.operation  = 'find.toArray';\nnewMsg.payload    = {};\nnewMsg.projection = { 'mac' : 1 , '_id' : 0 };\nreturn newMsg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 250,
        "y": 200,
        "wires": [
            [
                "234f7ad1bc0e450b"
            ]
        ]
    },
    {
        "id": "65c1e0489893e7e6",
        "type": "function",
        "z": "46ed3356b6c0cbcb",
        "name": "ammonia 값 추출",
        "func": "var newMsg =[];\n\nfor (var i = 0; i < msg.payload.length; i++) {\nnewMsg.push(msg.payload[i].ammonia);\n}\n\nmsg.payload = newMsg;\n\nreturn  msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 610,
        "y": 200,
        "wires": [
            [
                "90ef1e97fa7ab886",
                "f4b578035d1c38db"
            ]
        ]
    },
    {
        "id": "53f39e70839372ab",
        "type": "function",
        "z": "46ed3356b6c0cbcb",
        "name": "global.set",
        "func": "global.set(\"Ammonia\",msg.payload);\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 840,
        "y": 140,
        "wires": [
            []
        ]
    },
    {
        "id": "531949fbceb35664",
        "type": "function",
        "z": "46ed3356b6c0cbcb",
        "name": "데이터 비교",
        "func": "var Ammonia = global.get(\"Ammonia\");\nvar newMsg={};\n\nfor(var i=0; i<msg.payload.length; i++){\n    if(msg.payload[i]!==Ammonia[i]){\n        newMsg.payload={\"collection\": \"1_ammoniaSensor\", \"index\":i, \"ammonia\": msg.payload[i]};\n    }\n}\nreturn newMsg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 170,
        "y": 260,
        "wires": [
            [
                "a3c095c6b7ddd4d7"
            ]
        ]
    },
    {
        "id": "90ef1e97fa7ab886",
        "type": "delay",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "pauseType": "delay",
        "timeout": "0.2",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 690,
        "y": 140,
        "wires": [
            [
                "53f39e70839372ab"
            ]
        ]
    },
    {
        "id": "c7d022067781fcf4",
        "type": "link in",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "links": [
            "f4b578035d1c38db"
        ],
        "x": 55,
        "y": 260,
        "wires": [
            [
                "531949fbceb35664"
            ]
        ]
    },
    {
        "id": "f4b578035d1c38db",
        "type": "link out",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "mode": "link",
        "links": [
            "c7d022067781fcf4"
        ],
        "x": 735,
        "y": 200,
        "wires": []
    },
    {
        "id": "9b98e432924cd515",
        "type": "debug",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 450,
        "y": 260,
        "wires": []
    },
    {
        "id": "44811b1f055e1d92",
        "type": "inject",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": "0",
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 110,
        "y": 400,
        "wires": [
            [
                "316f637bf341b76a"
            ]
        ]
    },
    {
        "id": "316f637bf341b76a",
        "type": "function",
        "z": "46ed3356b6c0cbcb",
        "name": "find.toArray",
        "func": "var newMsg = {};\nnewMsg.collection = '2_dustSensor';\nnewMsg.operation  = 'find.toArray';\n// newMsg.payload    = { 'name' : 'hi' };\nnewMsg.payload    = {};\nnewMsg.projection = { 'mac' : 1 , '_id' : 0 };\nreturn newMsg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 250,
        "y": 400,
        "wires": [
            [
                "fe3e8406e35aa420"
            ]
        ]
    },
    {
        "id": "d6701960dd8942af",
        "type": "function",
        "z": "46ed3356b6c0cbcb",
        "name": "dust 값 추출",
        "func": "var newMsg =[];\n\nfor (var i = 0; i < msg.payload.length; i++) {\nnewMsg.push(msg.payload[i].dust);\n}\n\nmsg.payload = newMsg;\n\nreturn  msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 590,
        "y": 400,
        "wires": [
            [
                "dd8165bc014d56d9",
                "2b69754ac5db73b4"
            ]
        ]
    },
    {
        "id": "dc4895061ab0a1e6",
        "type": "function",
        "z": "46ed3356b6c0cbcb",
        "name": "global.set",
        "func": "global.set(\"dust\",msg.payload);\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 900,
        "y": 340,
        "wires": [
            []
        ]
    },
    {
        "id": "1c26b3d12d0b9c28",
        "type": "function",
        "z": "46ed3356b6c0cbcb",
        "name": "데이터 비교",
        "func": "var dust = global.get(\"dust\")||0;\nvar newMsg={};\n\nfor(var i=0; i<msg.payload.length; i++){\n    if(msg.payload[i]!==dust[i]){\n        newMsg.payload={\"collection\": \"2_dustSensor\", \"index\":i, \"dust\": msg.payload[i]};\n    }\n}\n\nreturn newMsg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 170,
        "y": 460,
        "wires": [
            [
                "e8dffb9630d54580"
            ]
        ]
    },
    {
        "id": "dd8165bc014d56d9",
        "type": "delay",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "pauseType": "delay",
        "timeout": "0.2",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 750,
        "y": 340,
        "wires": [
            [
                "dc4895061ab0a1e6"
            ]
        ]
    },
    {
        "id": "e8dffb9630d54580",
        "type": "rbe",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": true,
        "property": "payload",
        "topi": "topic",
        "x": 310,
        "y": 460,
        "wires": [
            [
                "ea0c3494214a5dd9"
            ]
        ]
    },
    {
        "id": "b6a1ce960ef6d324",
        "type": "link in",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "links": [
            "2b69754ac5db73b4"
        ],
        "x": 55,
        "y": 460,
        "wires": [
            [
                "1c26b3d12d0b9c28"
            ]
        ]
    },
    {
        "id": "2b69754ac5db73b4",
        "type": "link out",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "mode": "link",
        "links": [
            "b6a1ce960ef6d324"
        ],
        "x": 695,
        "y": 400,
        "wires": []
    },
    {
        "id": "ea0c3494214a5dd9",
        "type": "debug",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 450,
        "y": 460,
        "wires": []
    },
    {
        "id": "b91b0c0a8c8b88bb",
        "type": "comment",
        "z": "46ed3356b6c0cbcb",
        "name": "암모니아",
        "info": "",
        "x": 100,
        "y": 140,
        "wires": []
    },
    {
        "id": "14ac143068224cb8",
        "type": "comment",
        "z": "46ed3356b6c0cbcb",
        "name": "미세먼지",
        "info": "",
        "x": 100,
        "y": 340,
        "wires": []
    },
    {
        "id": "a3c095c6b7ddd4d7",
        "type": "rbe",
        "z": "46ed3356b6c0cbcb",
        "name": "",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": true,
        "property": "payload",
        "topi": "topic",
        "x": 310,
        "y": 260,
        "wires": [
            [
                "9b98e432924cd515"
            ]
        ]
    },
    {
        "id": "234f7ad1bc0e450b",
        "type": "mongodb2 in",
        "z": "46ed3356b6c0cbcb",
        "service": "",
        "name": "",
        "collection": "",
        "operation": "",
        "x": 430,
        "y": 200,
        "wires": [
            [
                "65c1e0489893e7e6"
            ]
        ]
    },
    {
        "id": "fe3e8406e35aa420",
        "type": "mongodb2 in",
        "z": "46ed3356b6c0cbcb",
        "service": "",
        "name": "",
        "collection": "",
        "operation": "",
        "x": 410,
        "y": 400,
        "wires": [
            [
                "d6701960dd8942af"
            ]
        ]
    }
]


이렇게 구성하면

mongoDB 의 데이터가 변할 때 마다

연결된 장치가 즉각적으로 반응해서

Realtime database와 비슷한 동작을 할 수 있다.

 

msg.payload 대신 MQTT 노드를 사용해

데이터를 그 순간마다 출력하면 된다.

 

하지만 단점으로는

inject가 무한반복 되기 때문에

서버에 무리가 간다는 것이다.

'개발 > DB' 카테고리의 다른 글

mongoDB 설치 및 port configuration  (0) 2022.11.14