Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/browser/modules/D3Visualization/components/Explorer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import neoGraphStyle from '../graphStyle'
import { InspectorComponent } from './Inspector'
import { LegendComponent } from './Legend'
import { StyledFullSizeContainer } from './styled'
import { getMaxFieldItems } from 'shared/modules/settings/settingsDuck'
import { connect } from 'react-redux'

const deduplicateNodes = nodes => {
return nodes.reduce(
Expand Down Expand Up @@ -231,6 +233,7 @@ export class ExplorerComponent extends Component {
setGraph={this.props.setGraph}
/>
<InspectorComponent
hasTruncatedFields={this.props.hasTruncatedFields}
fullscreen={this.props.fullscreen}
hoveredItem={this.state.hoveredItem}
selectedItem={this.state.selectedItem}
Expand All @@ -241,4 +244,6 @@ export class ExplorerComponent extends Component {
)
}
}
export const Explorer = ExplorerComponent
export const Explorer = connect(state => ({
maxFieldItems: getMaxFieldItems(state)
}))(ExplorerComponent)
9 changes: 9 additions & 0 deletions src/browser/modules/D3Visualization/components/Inspector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ import { GrassEditor } from './GrassEditor'
import { RowExpandToggleComponent } from './RowExpandToggle'
import ClickableUrls from '../../../components/clickable-urls'
import numberToUSLocale from 'shared/utils/number-to-US-locale'
import { StyledTruncatedMessage } from 'browser/modules/Stream/styled'
import { Icon } from 'semantic-ui-react'
import Ellipsis from 'browser-components/Ellipsis'

const mapItemProperties = itemProperties =>
itemProperties
Expand Down Expand Up @@ -149,6 +152,12 @@ export class InspectorComponent extends Component {
<StyledInlineList className="list-inline">
<StyledInspectorFooterRowListPair className="pair" key="pair">
<StyledInspectorFooterRowListValue className="value">
{this.props.hasTruncatedFields && (
<StyledTruncatedMessage>
<Icon name="warning sign" /> Record fields have been
truncated.&nbsp;
</StyledTruncatedMessage>
)}
{description}
</StyledInspectorFooterRowListValue>
</StyledInspectorFooterRowListPair>
Expand Down
7 changes: 7 additions & 0 deletions src/browser/modules/Sidebar/Settings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ const visualSettings = [
tooltip: "Max number of rows to render in 'Rows' result view"
}
},
{
maxFieldItems: {
displayName: 'Record field limit',
tooltip:
'Max number of items in a field per record being handled. When reached, items get truncated.'
}
},
{
autoComplete: {
displayName: 'Connect result nodes',
Expand Down
14 changes: 10 additions & 4 deletions src/browser/modules/Stream/CypherFrame/AsciiView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ import {
stringifyResultArray
} from './helpers'
import { stringModifier } from 'services/bolt/cypherTypesFormatting'
import { getMaxFieldItems } from 'shared/modules/settings/settingsDuck'
import { connect } from 'react-redux'

export class AsciiView extends Component {
export class AsciiViewComponent extends Component {
state = {
serializedRows: [],
bodyMessage: ''
Expand Down Expand Up @@ -72,8 +74,8 @@ export class AsciiView extends Component {
return !this.equalProps(props) || !shallowEquals(state, this.state)
}

makeState(props) {
const { result, maxRows } = props
makeState (props) {
const { result, maxRows, maxFieldItems } = props
const { bodyMessage = null } =
getBodyAndStatusBarMessages(result, maxRows) || {}
this.setState({ bodyMessage })
Expand All @@ -82,7 +84,7 @@ export class AsciiView extends Component {
const serializedRows =
stringifyResultArray(
stringModifier,
transformResultRecordsToResultArray(records)
transformResultRecordsToResultArray(records, maxFieldItems)
) || []
this.setState({ serializedRows })
const maxColWidth = asciitable.maxColumnWidth(serializedRows)
Expand Down Expand Up @@ -110,6 +112,10 @@ export class AsciiView extends Component {
}
}

export const AsciiView = connect(state => ({
maxFieldItems: getMaxFieldItems(state)
}))(AsciiViewComponent)

export class AsciiStatusbar extends Component {
state = {
maxSliderWidth: 140,
Expand Down
2 changes: 1 addition & 1 deletion src/browser/modules/Stream/CypherFrame/AsciiView.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import React from 'react'
import { render } from '@testing-library/react'
import neo4j from 'neo4j-driver'

import { AsciiView, AsciiStatusbar } from './AsciiView'
import { AsciiViewComponent as AsciiView, AsciiStatusbar } from './AsciiView'

describe('AsciiViews', () => {
describe('AsciiView', () => {
Expand Down
41 changes: 33 additions & 8 deletions src/browser/modules/Stream/CypherFrame/CodeView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import {
StyledTd,
StyledExpandable
} from '../styled'
import { TableStatusbar } from './TableView'
import { TableStatusbar, TableStatusbarComponent } from './TableView'
import { getMaxFieldItems } from 'shared/modules/settings/settingsDuck'
import { connect } from 'react-redux'
import { map, take } from 'lodash-es'

class ExpandableContent extends Component {
state = {}
Expand All @@ -54,16 +57,33 @@ class ExpandableContent extends Component {
}
}

export class CodeView extends Component {
shouldComponentUpdate(props) {
return !this.props.result || !deepEquals(props.result, this.props.result)
const fieldLimiterFactory = maxFieldItems => (key, val) => {
if (!maxFieldItems || key !== '_fields') {
return val
}

render() {
const { request = {}, query } = this.props
return map(val, field => {
return Array.isArray(field) ? take(field, maxFieldItems) : field
})
}

export class CodeViewComponent extends Component {
shouldComponentUpdate (props) {
return !this.props.result || !deepEquals(props.result, this.props.result)
}
render () {
const { request = {}, query, maxFieldItems } = this.props
if (request.status !== 'success') return null
const resultJson = JSON.stringify(request.result.records, null, 2)
const summaryJson = JSON.stringify(request.result.summary, null, 2)
const resultJson = JSON.stringify(
request.result.records,
fieldLimiterFactory(maxFieldItems),
2
)
Comment on lines +77 to +81
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this only does JSON.stringify and only executes when the user switches to this tab, can we skip the truncation here?
The purpose of the code tab/view is to give the developer a window into what's being returned from the driver without any formatting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately not, even JSON.stringify() gets overwhelmed with large result fields

const summaryJson = JSON.stringify(
request.result.summary,
fieldLimiterFactory(maxFieldItems),
2
)
return (
<PaddedDiv>
<StyledTable>
Expand Down Expand Up @@ -97,4 +117,9 @@ export class CodeView extends Component {
}
}

export const CodeView = connect(state => ({
maxFieldItems: getMaxFieldItems(state)
}))(CodeViewComponent)

export const CodeStatusbarComponent = TableStatusbarComponent
export const CodeStatusbar = TableStatusbar
5 changes: 4 additions & 1 deletion src/browser/modules/Stream/CypherFrame/CodeView.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import React from 'react'
import { render } from '@testing-library/react'
import neo4j from 'neo4j-driver'

import { CodeView, CodeStatusbar } from './CodeView'
import {
CodeViewComponent as CodeView,
CodeStatusbarComponent as CodeStatusbar
} from './CodeView'

describe('CodeViews', () => {
describe('CodeView', () => {
Expand Down
56 changes: 43 additions & 13 deletions src/browser/modules/Stream/CypherFrame/TableView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import { HTMLEntities } from 'services/santize.utils'
import {
StyledStatsBar,
PaddedTableViewDiv,
StyledBodyMessage
StyledBodyMessage,
StyledTruncatedMessage
} from '../styled'
import Ellipsis from 'browser-components/Ellipsis'
import {
Expand All @@ -40,16 +41,21 @@ import { shallowEquals, stringifyMod } from 'services/utils'
import {
getBodyAndStatusBarMessages,
getRecordsToDisplayInTable,
transformResultRecordsToResultArray
transformResultRecordsToResultArray,
resultHasTruncatedFields
} from './helpers'
import { stringModifier } from 'services/bolt/cypherTypesFormatting'
import ClickableUrls, {
convertUrlsToHrefTags
} from '../../../components/clickable-urls'
import { getMaxFieldItems } from 'shared/modules/settings/settingsDuck'
import { connect } from 'react-redux'
import { Icon } from 'semantic-ui-react'

const renderCell = entry => {
const renderCell = (entry, maxFieldItems) => {
if (Array.isArray(entry)) {
const children = entry.map((item, index) => (
const entryToUse = maxFieldItems ? entry.slice(0, maxFieldItems) : entry
const children = entryToUse.map((item, index) => (
<span key={index}>
{renderCell(item)}
{index === entry.length - 1 ? null : ', '}
Expand All @@ -75,12 +81,12 @@ export const renderObject = entry => {
/>
)
}
const buildData = entries => {
const buildData = (entries, maxFieldItems) => {
return entries.map(entry => {
if (entry !== null) {
return (
<StyledTd className="table-properties" key={v4()}>
{renderCell(entry)}
{renderCell(entry, maxFieldItems)}
</StyledTd>
)
}
Expand All @@ -91,15 +97,15 @@ const buildData = entries => {
)
})
}
const buildRow = item => {
const buildRow = (item, maxFieldItems) => {
return (
<StyledBodyTr className="table-row" key={v4()}>
{buildData(item)}
{buildData(item, maxFieldItems)}
</StyledBodyTr>
)
}

export class TableView extends Component {
export class TableViewComponent extends Component {
state = {
columns: [],
data: [],
Expand Down Expand Up @@ -155,7 +161,9 @@ export class TableView extends Component {
</StyledTh>
))
const tableBody = (
<tbody>{this.state.data.map(item => buildRow(item))}</tbody>
<tbody>
{this.state.data.map(item => buildRow(item, this.props.maxFieldItems))}
</tbody>
)
return (
<PaddedTableViewDiv>
Expand All @@ -170,7 +178,11 @@ export class TableView extends Component {
}
}

export class TableStatusbar extends Component {
export const TableView = connect(state => ({
maxFieldItems: getMaxFieldItems(state)
}))(TableViewComponent)

export class TableStatusbarComponent extends Component {
state = {
statusBarMessage: ''
}
Expand All @@ -193,14 +205,32 @@ export class TableStatusbar extends Component {
this.props.result,
this.props.maxRows
)
if (statusBarMessage !== undefined) this.setState({ statusBarMessage })
const hasTruncatedFields = resultHasTruncatedFields(
props.result,
props.maxFieldItems
)
if (statusBarMessage !== undefined) {
this.setState({ statusBarMessage, hasTruncatedFields })
}
}

render() {
return (
<StyledStatsBar>
<Ellipsis>{this.state.statusBarMessage}</Ellipsis>
<Ellipsis>
{this.state.hasTruncatedFields && (
<StyledTruncatedMessage>
<Icon name="warning sign" /> Record fields have been
truncated.&nbsp;
</StyledTruncatedMessage>
)}
{this.state.statusBarMessage}
</Ellipsis>
</StyledStatsBar>
)
}
}

export const TableStatusbar = connect(state => ({
maxFieldItems: getMaxFieldItems(state)
}))(TableStatusbarComponent)
6 changes: 5 additions & 1 deletion src/browser/modules/Stream/CypherFrame/TableView.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import React from 'react'
import { render } from '@testing-library/react'
import neo4j from 'neo4j-driver'

import { TableView, TableStatusbar, renderObject } from './TableView'
import {
TableViewComponent as TableView,
TableStatusbarComponent as TableStatusbar,
renderObject
} from './TableView'

describe('TableViews', () => {
describe('TableView', () => {
Expand Down
21 changes: 17 additions & 4 deletions src/browser/modules/Stream/CypherFrame/VisualizationView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { StyledVisContainer } from './VisualizationView.styled'

import { CYPHER_REQUEST } from 'shared/modules/cypher/cypherDuck'
import { NEO4J_BROWSER_USER_ACTION_QUERY } from 'services/bolt/txMetadata'
import { getMaxFieldItems } from 'shared/modules/settings/settingsDuck'
import { resultHasTruncatedFields } from 'browser/modules/Stream/CypherFrame/helpers'

export class Visualization extends Component {
state = {
Expand Down Expand Up @@ -67,11 +69,18 @@ export class Visualization extends Component {
nodes,
relationships
} = bolt.extractNodesAndRelationshipsFromRecordsForOldVis(
props.result.records
props.result.records,
true,
props.maxFieldItems
)
const hasTruncatedFields = resultHasTruncatedFields(
props.result,
props.maxFieldItems
)
this.setState({
nodes,
relationships,
hasTruncatedFields,
updated: new Date().getTime()
})
}
Expand Down Expand Up @@ -115,7 +124,8 @@ export class Visualization extends Component {
: 0
const resultGraph = bolt.extractNodesAndRelationshipsFromRecordsForOldVis(
response.result.records,
false
false,
this.props.maxFieldItems
)
this.autoCompleteRelationships(
this.graph._nodes,
Expand Down Expand Up @@ -150,7 +160,8 @@ export class Visualization extends Component {
resolve({
...bolt.extractNodesAndRelationshipsFromRecordsForOldVis(
response.result.records,
false
false,
this.props.maxFieldItems
)
})
}
Expand All @@ -171,6 +182,7 @@ export class Visualization extends Component {
<StyledVisContainer fullscreen={this.props.fullscreen}>
<ExplorerComponent
maxNeighbours={this.props.maxNeighbours}
hasTruncatedFields={this.state.hasTruncatedFields}
initialNodeDisplay={this.props.initialNodeDisplay}
graphStyleData={this.props.graphStyleData}
updateStyle={this.props.updateStyle}
Expand All @@ -192,7 +204,8 @@ export class Visualization extends Component {

const mapStateToProps = state => {
return {
graphStyleData: grassActions.getGraphStyleData(state)
graphStyleData: grassActions.getGraphStyleData(state),
maxFieldItems: getMaxFieldItems(state)
}
}

Expand Down
Loading