Skip to content

Commit

Permalink
added NeoJSON[Object|Array]>>#findPath[s]: for searching an object graph
Browse files Browse the repository at this point in the history
  • Loading branch information
svenvc committed Apr 19, 2024
1 parent c09b91c commit 4fea257
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
26 changes: 26 additions & 0 deletions repository/Neo-JSON-Core/NeoJSONArray.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,32 @@ NeoJSONArray >> atPath: keyCollection put: newValue [
^ target at: keyCollection last put: newValue
]

{ #category : #accessing }
NeoJSONArray >> findPath: conditionBlock [
"Find and return the first path to an object in the graph that I represent
for which conditionBlock holds. Return nil when not found."

self doWithIndex: [ :each :index |
(each isCollection and: [ each isString not ]) ifTrue: [
(each findPath: conditionBlock) ifNotNil: [ :subPath |
^ { index } , subPath ] ] ].
^ nil
]

{ #category : #accessing }
NeoJSONArray >> findPaths: conditionBlock [
"Find and return all paths to objects in the graph that I represent
for which conditionBlock holds. Return an empty collection when none are found."

| found |
found := OrderedCollection new.
self doWithIndex: [ :each :index |
(each isCollection and: [ each isString not ]) ifTrue: [
(each findPaths: conditionBlock) do: [ :subPath |
found add: { index } , subPath ] ] ].
^ found
]

{ #category : #print }
NeoJSONArray >> printOn: stream [
"I use my JSON representation when printing myself"
Expand Down
28 changes: 28 additions & 0 deletions repository/Neo-JSON-Core/NeoJSONObject.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,34 @@ NeoJSONObject >> doesNotUnderstand: message [
ifFalse: [ super doesNotUnderstand: message ]
]

{ #category : #accessing }
NeoJSONObject >> findPath: conditionBlock [
"Find and return the first path to an object in the graph that I represent
for which conditionBlock holds. Return nil when not found."

(conditionBlock value: self) ifTrue: [ ^ #() ].
self keysAndValuesDo: [ :key :value |
(value isCollection and: [ value isString not ]) ifTrue: [
(value findPath: conditionBlock) ifNotNil: [ :subPath |
^ { key } , subPath ] ] ].
^ nil
]

{ #category : #accessing }
NeoJSONObject >> findPaths: conditionBlock [
"Find and return all paths to objects in the graph that I represent
for which conditionBlock holds. Return an empty collection when none are found."

| found |
found := OrderedCollection new.
(conditionBlock value: self) ifTrue: [ found add: #() ].
self keysAndValuesDo: [ :key :value |
(value isCollection and: [ value isString not ]) ifTrue: [
(value findPaths: conditionBlock) do: [ :subPath |
found add: { key } , subPath ] ] ].
^ found
]

{ #category : #accessing }
NeoJSONObject >> name [
"Overwritten to make this accessor available as key"
Expand Down
56 changes: 56 additions & 0 deletions repository/Neo-JSON-Tests/NeoJSONObjectTests.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ Class {
#category : #'Neo-JSON-Tests'
}

{ #category : #accessing }
NeoJSONObjectTests >> exampleJSONSchema [
^ NeoJSONObject fromString: '{
"$id" : "https://example.com/schemas/address",
"type" : "object",
"properties" : {
"street_address" : { "$anchor" : "street_address", "type" : "string" },
"city" : { "type" : "string" },
"state" : { "type" : "string" }
},
"required" : ["street_address", "city", "state"],
"$defs" : {
"country" : { "type" : "object", "$anchor" : "country", "enum" : ["us","canada","gb"] }
}
}'
]

{ #category : #testing }
NeoJSONObjectTests >> testAtAt [
| object |
Expand Down Expand Up @@ -86,6 +103,45 @@ NeoJSONObjectTests >> testCRUD [
self assert: object isEmpty
]

{ #category : #testing }
NeoJSONObjectTests >> testFindPath [
| json path |
json := self exampleJSONSchema.

path := json findPath: [ :object | (object at: '$anchor') = 'street_address' ].
self deny: path isNil.
self assert: path equals: #(properties street_address).
self assert: ((json atPath: path) at: '$anchor') equals: 'street_address'.

path := json findPath: [ :object | (object at: 'foo') = 'bar' ].
self assert: path isNil.

path := json findPath: [ :object | (object at: '$anchor') = 'country' ].
self deny: path isNil.
self assert: path equals: #('$defs' country).
self assert: ((json atPath: path) at: '$anchor') equals: 'country'
]

{ #category : #testing }
NeoJSONObjectTests >> testFindPaths [
| json paths |
json := self exampleJSONSchema.

paths := json findPaths: [ :object | object includesKey: '$anchor' ].
self deny: paths isEmpty.
self assert: paths asArray equals: #((properties street_address) ('$defs' country)).
self assert: ((json atPath: paths first) at: '$anchor') equals: 'street_address'.
self assert: ((json atPath: paths second) at: '$anchor') equals: 'country'.

paths := json findPaths: [ :object | (object at: 'foo') = 'bar' ].
self assert: paths isEmpty.

paths := json findPaths: [ :object | object includesKey: #type ].
self deny: paths isEmpty.
paths do: [ :path |
self assert: (#(string object) includes: ((json atPath: path) at: #type)) ]
]

{ #category : #testing }
NeoJSONObjectTests >> testJSON [
| data json object |
Expand Down

0 comments on commit 4fea257

Please sign in to comment.