You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by hb...@apache.org on 2022/12/29 06:35:15 UTC

[age-viewer] branch main updated: update retrieval of metadata (#75)

This is an automated email from the ASF dual-hosted git repository.

hbshin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/age-viewer.git


The following commit(s) were added to refs/heads/main by this push:
     new 92578aa  update retrieval of metadata (#75)
92578aa is described below

commit 92578aa5c03d46b6a39bc73dff77d3a761809164
Author: marodins <67...@users.noreply.github.com>
AuthorDate: Wed Dec 28 22:35:10 2022 -0800

    update retrieval of metadata (#75)
    
    * update query and processing
    
    * add new metadata queries and update database connection
    
    * retrieve metadata based on version
    
    * include blank lines
    
    * retrieve metadata each time metadata may have been updated
    
    * get new meta data when data is modified
    
    * Resolve Conflict pr 75#
    
    Co-authored-by: Hanbyeol Shin /  David Shin / 신한별 <76...@users.noreply.github.com>
---
 backend/sql/11/meta_data.sql                       | 13 ++++
 backend/sql/12/meta_data.sql                       | 13 ++++
 backend/sql/analyze_graph.sql                      |  1 +
 backend/sql/meta_data.sql                          |  7 --
 backend/sql/pg_version.sql                         |  1 +
 backend/src/models/GraphRepository.js              | 28 ++++++--
 backend/src/services/databaseService.js            | 78 +++++++++++-----------
 backend/src/tools/AGEParser.js                     |  7 +-
 backend/src/tools/SQLFlavorManager.js              |  4 +-
 .../src/components/contents/containers/Editor.js   |  1 +
 .../components/contents/presentations/Editor.jsx   |  7 +-
 frontend/src/features/editor/EditorSlice.js        |  2 +
 12 files changed, 106 insertions(+), 56 deletions(-)

diff --git a/backend/sql/11/meta_data.sql b/backend/sql/11/meta_data.sql
new file mode 100644
index 0000000..549268e
--- /dev/null
+++ b/backend/sql/11/meta_data.sql
@@ -0,0 +1,13 @@
+SELECT * FROM (
+        SELECT c.relname AS label, n.oid as namespace_id, c.reltuples AS cnt
+        FROM pg_catalog.pg_class c
+        JOIN pg_catalog.pg_namespace n
+        ON n.oid = c.relnamespace
+        WHERE c.relkind = 'r'
+        AND n.nspname = '%s'
+) as q1
+JOIN ag_graph as g ON q1.namespace_id = g.namespace
+INNER JOIN ag_label as label
+
+ON label.name = q1.label
+AND label.graph = g.oid;
diff --git a/backend/sql/12/meta_data.sql b/backend/sql/12/meta_data.sql
new file mode 100644
index 0000000..8959d54
--- /dev/null
+++ b/backend/sql/12/meta_data.sql
@@ -0,0 +1,13 @@
+SELECT * FROM (
+        SELECT c.relname AS label, n.oid as namespace_id, c.reltuples AS cnt
+        FROM pg_catalog.pg_class c
+        JOIN pg_catalog.pg_namespace n
+        ON n.oid = c.relnamespace
+        WHERE c.relkind = 'r'
+        AND n.nspname = '%s'
+) as q1
+JOIN ag_graph as g ON q1.namespace_id = g.namespace
+INNER JOIN ag_label as label
+
+ON label.name = q1.label
+AND label.graph = g.graphid;
diff --git a/backend/sql/analyze_graph.sql b/backend/sql/analyze_graph.sql
new file mode 100644
index 0000000..f36e2c4
--- /dev/null
+++ b/backend/sql/analyze_graph.sql
@@ -0,0 +1 @@
+ANALYZE;
\ No newline at end of file
diff --git a/backend/sql/meta_data.sql b/backend/sql/meta_data.sql
deleted file mode 100644
index 36ab8cf..0000000
--- a/backend/sql/meta_data.sql
+++ /dev/null
@@ -1,7 +0,0 @@
-ANALYZE;
-SELECT c.relname AS label, c.reltuples AS cnt
-FROM pg_catalog.pg_class c
-JOIN pg_catalog.pg_namespace n
-ON n.oid = c.relnamespace
-WHERE c.relkind = 'r'
-AND n.nspname = '%s';
\ No newline at end of file
diff --git a/backend/sql/pg_version.sql b/backend/sql/pg_version.sql
new file mode 100644
index 0000000..e0953c3
--- /dev/null
+++ b/backend/sql/pg_version.sql
@@ -0,0 +1 @@
+show server_version;
diff --git a/backend/src/models/GraphRepository.js b/backend/src/models/GraphRepository.js
index 38685d0..7ed0596 100644
--- a/backend/src/models/GraphRepository.js
+++ b/backend/src/models/GraphRepository.js
@@ -20,21 +20,22 @@ import PgConfig from '../config/Pg'
 
 import pg from 'pg';
 import types from 'pg-types';
-import {setAGETypes} from '../tools/AGEParser';
+import {setAGETypes, onConnectQueries} from '../tools/AGEParser';
 import { getQuery } from '../tools/SQLFlavorManager';
 
 
 class GraphRepository {
-    constructor({host, port, database, graph, user, password, graphs=[]} = {}) {
+    constructor({host, port, database, graph, user, password, graphs=[], server} = {}) {
         this._host = host;
         this._port = port;
         this._database = database;
+        this._server_version = server;
         this._graphs = graphs;
         this._graph = graph;
         this._user = user;
         this._password = password;
     }
-
+    /*
     static async getConnection({
                                    host,
                                    port,
@@ -51,13 +52,28 @@ class GraphRepository {
                 port,
             }
         )
+
         client.connect();
+        
         await setAGETypes(client, types);
         if (closeConnection === true) {
             await client.end();
         }
         return client;
     }
+    */
+    async connect(){
+        if (!this._pool) {
+            this._pool = GraphRepository.newConnectionPool(this.getPoolConnectionInfo());
+        }
+        const client = await this._pool.connect();
+        if (!this._server_version){
+            const {server_version: v} = await onConnectQueries(client);
+            this._server_version = v;
+        }
+
+        return client;
+    }
 
     static newConnectionPool(poolConnectionConfig) {
         return new pg.Pool(poolConnectionConfig);
@@ -95,9 +111,7 @@ class GraphRepository {
      * Get connectionInfo
      */
     async getConnection() {
-        if (!this._pool) {
-            this._pool = GraphRepository.newConnectionPool(this.getPoolConnectionInfo());
-        }
+
         const client = await this._pool.connect();
 
         await setAGETypes(client, types);
@@ -128,6 +142,7 @@ class GraphRepository {
             host: this._host,
             port: this._port,
             database: this._database,
+            version: this._server_version,
             user: this._user,
             password: this._password,
             max: PgConfig.max,
@@ -145,6 +160,7 @@ class GraphRepository {
         }
         return {
             host: this._host,
+            version: this._server_version,
             port: this._port,
             database: this._database,
             user: this._user,
diff --git a/backend/src/services/databaseService.js b/backend/src/services/databaseService.js
index df680ea..482f4ea 100644
--- a/backend/src/services/databaseService.js
+++ b/backend/src/services/databaseService.js
@@ -17,10 +17,11 @@
  * under the License.
  */
 
-import {getQuery} from "../tools/SQLFlavorManager";
+import { getQuery } from "../tools/SQLFlavorManager";
 import * as util from "util";
 import GraphRepository from '../models/GraphRepository';
 import { start } from "repl";
+import { get } from "http";
 
 class DatabaseService {
     constructor() {
@@ -28,21 +29,22 @@ class DatabaseService {
     }
 
     async getMetaData(graphName) {
-        const { currentGraph } = graphName;
-        await this._graphRepository.initGraphNames();
-        const {graphs} = this._graphRepository.getConnectionInfo();
-
-        if(currentGraph){
-            if(graphs.includes(currentGraph)){
-                return await this.getMetaDataSingle(currentGraph,graphs);
+        let gr = this._graphRepository;
+        await gr.initGraphNames();
+        const { graphs } = gr.getConnectionInfo();
+        await DatabaseService.analyzeGraph(gr);
+        if (graphName) {
+            if (graphs.includes(graphName)) {
+                return await this.getMetaDataSingle(graphName, graphs);
+            } else {
+                return await this.getMetaDataSingle(gr._graph, graphs);
             }
-        } else if(graphs.length > 0) {
+        } else if (graphs.length > 0) {
             return await this.graphNameInitialize(graphs);
-        } else{
+        } else {
             throw new Error('graph does not exist');
             // return await this.getMetaDataMultiple(graphs);
         }
-
     }
 
     // async getMetaDataMultiple(graphs){
@@ -53,12 +55,12 @@ class DatabaseService {
     //     return metadata;
     // }
 
-    async getMetaDataSingle(curGraph, graphs){
+    async getMetaDataSingle(curGraph, graphs) {
         let metadata = {};
         let data = {};
-        const {database} = this.getConnectionInfo();
+        const { database } = this.getConnectionInfo();
         try {
-            let {nodes, edges} = await this.readMetaData(curGraph);
+            let { nodes, edges } = await this.readMetaData(curGraph);
             data.nodes = nodes;
             data.edges = edges;
             data.propertyKeys = await this.getPropertyKeys();
@@ -67,7 +69,7 @@ class DatabaseService {
             data.role = await this.getRole();
             metadata[curGraph] = data;
             graphs.forEach((gname) => {
-                if(gname !== curGraph) metadata[gname] = {};
+                if (gname !== curGraph) metadata[gname] = {};
             })
         } catch (error) {
             throw error;
@@ -109,11 +111,16 @@ class DatabaseService {
 
         return queryResult.rows;
     }
-    
-    async readMetaData(graphName){
+
+    static async analyzeGraph(gr) {
+        await gr.execute(getQuery('analyze_graph'));
+    }
+
+    async readMetaData(graphName) {
         let gr = this._graphRepository;
-        let queryResult = await gr.execute(util.format(getQuery('meta_data'), graphName));
-        return this.parseMeta(queryResult[1].rows);
+        const { version } = gr.getConnectionInfo();
+        let queryResult = await gr.execute(util.format(getQuery('meta_data', version.split('.')[0]), graphName));
+        return this.parseMeta(queryResult.rows);
     }
 
     async getPropertyKeys() {
@@ -136,7 +143,7 @@ class DatabaseService {
         }
 
         try {
-            let client = await graphRepository.getConnection(graphRepository.getConnectionInfo(), true);
+            let client = await graphRepository.connect();
             client.release();
         } catch (e) {
             this._graphRepository = null;
@@ -169,7 +176,7 @@ class DatabaseService {
         }
 
         try {
-            let client = await GraphRepository.getConnection(graphRepository.getConnectionInfo());
+            let client = await graphRepository.getConnection();
             client.release();
         } catch (err) {
             return false;
@@ -191,7 +198,7 @@ class DatabaseService {
         return this._graphRepository;
     }
 
-    convertEdge({label, id, start, end, props}) {
+    convertEdge({ label, id, start, end, props }) {
         return {
             label: label,
             id: `${id.oid}.${id.id}`,
@@ -200,28 +207,21 @@ class DatabaseService {
             properties: props,
         };
     }
-    parseMeta(data){
+    parseMeta(data) {
         const meta = {
-            edges:[],
-            nodes:[]
+            edges: [],
+            nodes: []
         };
-        const vertex = '_ag_label_vertex';
-        const edge = '_ag_label_edge';
-        let cur = null;
+        const vertex_name = '_ag_label_vertex';
+        const edge_name = '_ag_label_edge';
+
         data.forEach((element, index) => {
-            if ( element.label === vertex ){
-                cur = 'nodes';
-            }
-            else if ( element.label === edge ){
-                cur = 'edges';
-            }
-            else{
-                if(meta[cur]){
-                    meta[cur].push(element);
-                }
-                
+            if (element.name === vertex_name || element.name === edge_name) {
+                return;
             }
 
+            if (element.kind === 'v') meta.nodes.push(element);
+            if (element.kind === 'e') meta.edges.push(element);
         });
         return meta;
     }
diff --git a/backend/src/tools/AGEParser.js b/backend/src/tools/AGEParser.js
index 2a87fb2..13b34f1 100644
--- a/backend/src/tools/AGEParser.js
+++ b/backend/src/tools/AGEParser.js
@@ -18,6 +18,7 @@
  */
 
 import antlr4 from 'antlr4';
+import { cli } from 'winston/lib/winston/config';
 import AgtypeLexer from './AgtypeLexer';
 import AgtypeParser from './AgtypeParser';
 import CustomAgTypeListener from './CustomAgTypeListener';
@@ -52,5 +53,9 @@ async function setAGETypes(client, types) {
     types.setTypeParser(oidResults.rows[0].typelem, AGTypeParse)
 }
 
+async function onConnectQueries(client){
+    const v = await client.query('show server_version;');
+    return {server_version: v.rows[0].server_version};
+}
 
-export {setAGETypes, AGTypeParse}
+export {setAGETypes, AGTypeParse, onConnectQueries}
diff --git a/backend/src/tools/SQLFlavorManager.js b/backend/src/tools/SQLFlavorManager.js
index bf04d8a..09e9192 100644
--- a/backend/src/tools/SQLFlavorManager.js
+++ b/backend/src/tools/SQLFlavorManager.js
@@ -22,8 +22,8 @@ import fs from 'fs'
 const sqlBasePath = path.join(__dirname, '../../sql');
 
 // todo: util.format -> ejs
-function getQuery(name) {
-    const sqlPath = path.join(sqlBasePath, `${name}.sql`);
+function getQuery(name, version='') {
+    const sqlPath = path.join(sqlBasePath, version, `${name}.sql`);
     if (!fs.existsSync(sqlPath)) {
         throw new Error(`SQL does not exist, name = ${name}`);
     }
diff --git a/frontend/src/components/contents/containers/Editor.js b/frontend/src/components/contents/containers/Editor.js
index 1ef5e4a..a35c5c3 100644
--- a/frontend/src/components/contents/containers/Editor.js
+++ b/frontend/src/components/contents/containers/Editor.js
@@ -33,6 +33,7 @@ const mapStateToProps = (state) => ({
   alertList: state.alerts,
   database: state.database,
   command: state.editor.command,
+  update: state.editor.updateClause,
   isActive: state.navigator.isActive,
   activeRequests: state.cypher.activeRequests,
   isLabel: state.layout.isLabel,
diff --git a/frontend/src/components/contents/presentations/Editor.jsx b/frontend/src/components/contents/presentations/Editor.jsx
index cd7f811..1930434 100644
--- a/frontend/src/components/contents/presentations/Editor.jsx
+++ b/frontend/src/components/contents/presentations/Editor.jsx
@@ -29,11 +29,13 @@ import CodeMirror from '../../editor/containers/CodeMirrorWapperContainer';
 import SideBarToggle from '../../editor/containers/SideBarMenuToggleContainer';
 import { setting } from '../../../conf/config';
 import IconPlay from '../../../icons/IconPlay';
+import { getMetaData } from '../../../features/database/MetadataSlice';
 
 const Editor = ({
   setCommand,
   activeRequests,
   command,
+  update,
   addFrame,
   trimFrame,
   addAlert,
@@ -78,7 +80,7 @@ const Editor = ({
         dispatch(() => trimFrame('ServerConnect'));
         dispatch(() => addFrame(':server connect', 'ServerConnect'));
       }
-    } else if (database.status === 'disconnected' && command.toUpperCase().match('(MATCH|CREATE).*')) {
+    } else if (database.status === 'disconnected') {
       dispatch(() => trimFrame('ServerConnect'));
       dispatch(() => addAlert('ErrorNoDatabaseConnected'));
       dispatch(() => addFrame(command, 'ServerConnect', refKey));
@@ -104,7 +106,9 @@ const Editor = ({
               setCommand(command);
             }
           }
+          return;
         }
+        if (update) dispatch(getMetaData());
       });
       activePromises[refKey] = req;
       setPromises({ ...activePromises });
@@ -243,6 +247,7 @@ Editor.propTypes = {
   executeCypherQuery: PropTypes.func.isRequired,
   addCommandHistory: PropTypes.func.isRequired,
   toggleMenu: PropTypes.func.isRequired,
+  update: PropTypes.bool.isRequired,
   setLabel: PropTypes.func.isRequired,
   isLabel: PropTypes.bool.isRequired,
   // addCommandFavorites: PropTypes.func.isRequired,
diff --git a/frontend/src/features/editor/EditorSlice.js b/frontend/src/features/editor/EditorSlice.js
index 6653bf3..8528a17 100644
--- a/frontend/src/features/editor/EditorSlice.js
+++ b/frontend/src/features/editor/EditorSlice.js
@@ -24,6 +24,7 @@ const EditorSlice = createSlice({
   name: 'editor',
   initialState: {
     command: '',
+    updateClause: false,
     commandHistory: [],
     commandFavorites: [],
   },
@@ -31,6 +32,7 @@ const EditorSlice = createSlice({
     setCommand: {
       reducer: (state, action) => {
         state.command = action.payload.command;
+        state.updateClause = action.payload.command.match(/(CREATE|REMOVE|DELETE)/g) !== null;
       },
       prepare: (command) => ({ payload: { command } }),
     },