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 } }),
},