Search and query a knowledge graph service

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.

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.

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
        // define the search terms
        const search = new StreamingSearch({
          searchQuery: "solar",
          typeCategoryFilter: "both",
          // optional parameters
          returnSearchContext: false,
          start: 1, //start at the first record
          num: 200, //return 200 records.
          namedTypesFilter: ["Company", "Supplier", "Part"],
          globalIdsFilter: ["aiw-924", "esj-856", "snh-571", "hnk-9751", "pyo-7884", "hjl-2541"]
        });

        // search the knowledge graph
        KnowledgeGraphModule.executeSearchStreaming(
          // knowledge graph resources
          knowledgeGraph,
          // search parameters
          search
        ).then((streamingSearchResult) => {
          // the result of a streaming search is a readableStream which must be read to access the data.
          readStream(streamingSearchResult);
        });
Expand

Query

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.

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
        // select all Supplier's that are in the Washington DC area and the parts that they buy
        const query = `MATCH (s:Supplier)-[:buys_part]-(p:Part)
               esri.graph.ST_Intersects($geom, s.geometry)
               RETURN s,p`;

        KnowledgeGraphModule.executeQueryStreaming(knowledgeGraph, {
          openCypherQuery: query,
          bindParameters: {
            //bounding box around Washington DC
            geom: new Polygon({
              rings: [
                [
                  [38, -78],
                  [39, -78],
                  [39, -76],
                  [-38, -76],
                  [-38, -78]
                ]
              ]
            })
          }
        }).then((streamingQueryResult) => {
          // the result of a streaming query is a readableStream which must be read to access the data.
          readStream(streamingQueryResult);
        });
Expand

Working with stream results

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 search
        const 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 supplier
              let supplierParts = [];

              // each element of the result array will contain one supplier and one part it buys
              for (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 buy
              let 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 map
        function addToMap(records) {
          let features = [];
          //extract the geometry from the returned results and add it to a feature layer
          for (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 features
            if ("shape" in props) {
              features.push({
                geometry: {
                  type: props.shape.type,
                  x: props.shape.longitude,
                  y: props.shape.latitude
                },
                attributes: props
              });
            }
          }

          // create feature layer
          let 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);
        }
Expand

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.