There are two ways to retrieve records from a knowledge graph. Search allows you to run a free text search against the knowledge graph.
Query provides a more nuanced way to retrieve records in the form of an openCypher query executed against the knowledge graph.
Both search and query have two modes: non-streaming and streaming. Streaming search
and streaming query return results in small chunks allowing the client
to begin processing the data returned immediately rather than waiting for the entire result set to be returned before processing.
Streaming is faster, more efficient, and will retrieve all matching records, even if the total exceeds the search and query limits set in the service definition.
Another benefit of streaming is that the request is encoded which means that it is far smaller than a traditional
HTTP GET or JSON POST body. This is especially important when trying to do a query on a very large argument,
such as a large set of IDs or intersecting a complex geometry.
Search
Use GraphSearchStreaming to search the properties of both
entities and relationships
in the graph using the executeSearchStreaming() method.
Additional optional search parameters such as specifying a list of entity or relationship types to search within can further constrain the search.
The most efficient way to search a knowledge graph is with a streaming search.
Retrieve a more specific subset of entities and relationships or their properties using the executeQueryStreaming() method.
Querying a graph uses the Esri implementation of openCypher query which supports read-only operations.
Query provides much more flexibility in what results are returned from the graph and the structure of the returned data.
For example, consider a graph of a supply chain containing the entities manufacturing Plant which makes a Part that is bought by a Supplier; and there is a relationship produces between the plant and the
part it produces and a relationship buys_part between the supplier and the plant where it purchases the part.
To find the first ten entities of the Supplier type, you could use the query MATCH (m:Supplier) RETURN m LIMIT 10. To discover which
parts(p) are produced with by which plants (pl), you can match the entities through the produces relationship with a query such as MATCH (pl:Plant)-[ :produces]->(p) RETURN pl,p.
GraphQueryStreaming also provides the ability to bind parameters to the query such as a bounding box.
For example, to find all the suppliers located in area of Washington DC, use an intersection query and pass a polygon that covers the Washington DC area as a bind parameter.
See GraphQueryStreaming for additional query parameters.
The result of a streaming search or a streaming query is a GraphQueryStreamingResult
which contains a readable stream that must be read using a getReader() function to access the data.
The readable stream will return chunks of records until all records have been returned.
Each chunk may be processed immediately, such as adding data to a table or adding entities with geometry to a map.
Expand
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// a function to read the stream returned from the searchconst readStream = async (streamingQueryResult) => {
let time = Date.now();
let reader = streamingQueryResult.resultRowsStream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
console.log(`Stream completed and closed: ${(Date.now() - time) / 1000} seconds`, value);
break;
}
// begin working with the data from the returned chunk immediately.// list the parts bought by each supplierlet supplierParts = [];
// each element of the result array will contain one supplier and one part it buysfor (let v in value){
let supplier = value[v][0].properties.Name
let part = value [v][1].properties.Name
if(!(supplier in supplierParts)){
supplierParts[supplier] = [];
}
// collect parts by supplier that buys them supplierParts[supplier].push(part);
}
// make a table that lists the suppliers and the parts they buylet table = document.getElementById('supplierTableBody');
for (let supplier in supplierParts){
table.appendChild(`<tr><td>${supplier}</td><td>${supplierParts.supplier.join(', ')}</td>`);
}
// Entities that have geometry can be drawn on the map addToMap(value);
// Since }
// if there is an error in returning the stream or the stream is aborted } catch (err) {
if (err.name === "AbortError") {
console.log("Request aborted as expected");
} else {
throw err;
}
}
};
// function to add entities with geometry to a mapfunctionaddToMap(records) {
let features = [];
//extract the geometry from the returned results and add it to a feature layerfor (let i = 0; i < records.length; i++) {
let item = records[i][0];
let props = item.properties;
// if the returned item contains geometry,//extract the geometry information to create featuresif ("shape"in props) {
features.push({
geometry: {
type: props.shape.type,
x: props.shape.longitude,
y: props.shape.latitude
},
attributes: props
});
}
}
// create feature layerlet featureLayer = new FeatureLayer({
source: features,
renderer: {
type: "unique-value", // autocasts as new UniqueValueRenderer()defaultSymbol: {
type: "simple-marker", // autocasts as new SimpleMarkerSymbol()size: 2,
color: "#009816",
outline: null }
}
});
map.add(featureLayer);
}