Skip to content

Commit

Permalink
Merge pull request #957 from huboneo/feature-export-data-json
Browse files Browse the repository at this point in the history
Export query result records as JSON
  • Loading branch information
huboneo authored Aug 30, 2019
2 parents 2aad3e3 + 5bb6808 commit dcc689b
Show file tree
Hide file tree
Showing 7 changed files with 654 additions and 4 deletions.
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
'/dist/',
'/node_modules/'
],
transformIgnorePatterns: [`/node_modules/(?!lodash-es)`],
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|html)$':
'<rootDir>/test_utils/__mocks__/fileMock.js',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
"firebase": "^5.8.3",
"isomorphic-fetch": "^2.2.1",
"jsonic": "^0.3.0",
"lodash-es": "^4.17.15",
"mockdate": "^2.0.5",
"neo4j-driver": "^1.7.5",
"react": "^16.9.0",
Expand Down
113 changes: 112 additions & 1 deletion src/browser/modules/Stream/CypherFrame/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import bolt from 'services/bolt/bolt'
import { v1 as neo4j } from 'neo4j-driver'
import {
entries,
get,
includes,
isObjectLike,
lowerCase,
map,
reduce
} from 'lodash-es'

import bolt from 'services/bolt/bolt'
import * as viewTypes from 'shared/modules/stream/frameViewTypes'
import {
recursivelyExtractGraphItems,
Expand Down Expand Up @@ -287,3 +297,104 @@ const arrayifyPath = (types = neo4j.types, path) => {
].filter(part => part !== null)
})
}

/**
* Converts a raw Neo4j record into a JSON friendly format, mimicking APOC output
* @param {Record} record
* @return {*}
*/
export function recordToJSONMapper (record) {
const keys = get(record, 'keys', [])

return reduce(
keys,
(agg, key) => {
const field = record.get(key)

return {
...agg,
[key]: mapNeo4jValuesToPlainValues(field)
}
},
{}
)
}

/**
* Recursively converts Neo4j values to plain values, leaving other types untouched
* @param {*} values
* @return {*}
*/
function mapNeo4jValuesToPlainValues (values) {
if (!isObjectLike(values)) {
return values
}

if (Array.isArray(values)) {
return map(values, mapNeo4jValuesToPlainValues)
}

if (isNeo4jValue(values)) {
return neo4jValueToPlainValue(values)
}

// could be a Node or Relationship
const elementType = lowerCase(get(values, 'constructor.name', ''))

if (includes(['relationship', 'node'], elementType)) {
return {
elementType,
...mapNeo4jValuesToPlainValues({ ...values })
}
}

return reduce(
entries(values),
(agg, [key, value]) => ({
...agg,
[key]: mapNeo4jValuesToPlainValues(value)
}),
{}
)
}

/**
* Recursively convert Neo4j value to plain value, leaving other types untouched
* @param {*} value
* @return {*}
*/
function neo4jValueToPlainValue (value) {
switch (get(value, 'constructor')) {
case neo4j.types.Date:
case neo4j.types.DateTime:
case neo4j.types.Duration:
case neo4j.types.LocalDateTime:
case neo4j.types.LocalTime:
case neo4j.types.Time:
return value.toString()
case neo4j.types.Integer: // not exposed in typings but still there
return value.inSafeRange() ? value.toInt() : value.toNumber()
default:
return value
}
}

/**
* checks if a value is a neo4j value
* @param value
* @return {boolean}
*/
function isNeo4jValue (value) {
switch (get(value, 'constructor')) {
case neo4j.types.Date:
case neo4j.types.DateTime:
case neo4j.types.Duration:
case neo4j.types.LocalDateTime:
case neo4j.types.LocalTime:
case neo4j.types.Time:
case neo4j.types.Integer: // not exposed in typings but still there
return true
default:
return false
}
}
Loading

0 comments on commit dcc689b

Please sign in to comment.