github.com/cayleygraph/cayley@v0.7.7/query/gizmo/finals.go (about) 1 // Copyright 2017 The Cayley Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gizmo 16 17 import ( 18 "github.com/dop251/goja" 19 20 "github.com/cayleygraph/cayley/graph/iterator" 21 "github.com/cayleygraph/quad" 22 ) 23 24 const TopResultTag = "id" 25 26 // GetLimit is the same as All, but limited to the first N unique nodes at the end of the path, and each of their possible traversals. 27 func (p *pathObject) GetLimit(limit int) error { 28 it := p.buildIteratorTree() 29 it = iterator.Tag(it, TopResultTag) 30 p.s.limit = limit 31 p.s.count = 0 32 return p.s.runIterator(it) 33 } 34 35 // All executes the query and adds the results, with all tags, as a string-to-string (tag to node) map in the output set, one for each path that a traversal could take. 36 func (p *pathObject) All() error { 37 return p.GetLimit(p.s.limit) 38 } 39 40 func (p *pathObject) toArray(call goja.FunctionCall, withTags bool) goja.Value { 41 args := exportArgs(call.Arguments) 42 if len(args) > 1 { 43 return throwErr(p.s.vm, errArgCount2{Expected: 1, Got: len(args)}) 44 } 45 limit := -1 46 if len(args) > 0 { 47 limit, _ = toInt(args[0]) 48 } 49 it := p.buildIteratorTree() 50 it = iterator.Tag(it, TopResultTag) 51 var ( 52 array interface{} 53 err error 54 ) 55 if !withTags { 56 array, err = p.s.runIteratorToArrayNoTags(it, limit) 57 } else { 58 array, err = p.s.runIteratorToArray(it, limit) 59 } 60 if err != nil { 61 return throwErr(p.s.vm, err) 62 } 63 return p.s.vm.ToValue(array) 64 } 65 66 // ToArray executes a query and returns the results at the end of the query path as an JS array. 67 // 68 // Example: 69 // // javascript 70 // // bobFollowers contains an Array of followers of bob (alice, charlie, dani). 71 // var bobFollowers = g.V("<bob>").In("<follows>").ToArray() 72 func (p *pathObject) ToArray(call goja.FunctionCall) goja.Value { 73 return p.toArray(call, false) 74 } 75 76 // TagArray is the same as ToArray, but instead of a list of top-level nodes, returns an Array of tag-to-string dictionaries, much as All would, except inside the JS environment. 77 // 78 // Example: 79 // // javascript 80 // // bobTags contains an Array of followers of bob (alice, charlie, dani). 81 // var bobTags = g.V("<bob>").Tag("name").In("<follows>").TagArray() 82 // // nameValue should be the string "<bob>" 83 // var nameValue = bobTags[0]["name"] 84 func (p *pathObject) TagArray(call goja.FunctionCall) goja.Value { 85 return p.toArray(call, true) 86 } 87 func (p *pathObject) toValue(withTags bool) (interface{}, error) { 88 it := p.buildIteratorTree() 89 it = iterator.Tag(it, TopResultTag) 90 const limit = 1 91 if !withTags { 92 array, err := p.s.runIteratorToArrayNoTags(it, limit) 93 if err != nil { 94 return nil, err 95 } 96 if len(array) == 0 { 97 return nil, nil 98 } 99 return array[0], nil 100 } else { 101 array, err := p.s.runIteratorToArray(it, limit) 102 if err != nil { 103 return nil, err 104 } 105 if len(array) == 0 { 106 return nil, nil 107 } 108 return array[0], nil 109 } 110 } 111 112 // ToValue is the same as ToArray, but limited to one result node. 113 func (p *pathObject) ToValue() (interface{}, error) { 114 return p.toValue(false) 115 } 116 117 // TagValue is the same as TagArray, but limited to one result node. Returns a tag-to-string map. 118 func (p *pathObject) TagValue() (interface{}, error) { 119 return p.toValue(true) 120 } 121 122 // Map is a alias for ForEach. 123 func (p *pathObject) Map(call goja.FunctionCall) goja.Value { 124 return p.ForEach(call) 125 } 126 127 // ForEach calls callback(data) for each result, where data is the tag-to-string map as in All case. 128 // Signature: (callback) or (limit, callback) 129 // 130 // Arguments: 131 // 132 // * `limit` (Optional): An integer value on the first `limit` paths to process. 133 // * `callback`: A javascript function of the form `function(data)` 134 // 135 // Example: 136 // // javascript 137 // // Simulate query.All().All() 138 // graph.V("<alice>").ForEach(function(d) { g.Emit(d) } ) 139 func (p *pathObject) ForEach(call goja.FunctionCall) goja.Value { 140 it := p.buildIteratorTree() 141 it = iterator.Tag(it, TopResultTag) 142 if n := len(call.Arguments); n != 1 && n != 2 { 143 return throwErr(p.s.vm, errArgCount{Got: len(call.Arguments)}) 144 } 145 callback := call.Argument(len(call.Arguments) - 1) 146 args := exportArgs(call.Arguments[:len(call.Arguments)-1]) 147 limit := -1 148 if len(args) != 0 { 149 limit, _ = toInt(args[0]) 150 } 151 err := p.s.runIteratorWithCallback(it, callback, call, limit) 152 if err != nil { 153 return throwErr(p.s.vm, err) 154 } 155 return goja.Null() 156 } 157 158 // Count returns a number of results and returns it as a value. 159 // 160 // Example: 161 // // javascript 162 // // Save count as a variable 163 // var n = g.V().count() 164 // // Send it as a query result 165 // g.emit(n) 166 func (p *pathObject) Count() (int64, error) { 167 it := p.buildIteratorTree() 168 return p.s.countResults(it) 169 } 170 171 // Backwards compatibility 172 func (p *pathObject) CapitalizedGetLimit(limit int) error { 173 return p.GetLimit(limit) 174 } 175 func (p *pathObject) CapitalizedAll() error { 176 return p.All() 177 } 178 func (p *pathObject) CapitalizedtoArray(call goja.FunctionCall, withTags bool) goja.Value { 179 return p.toArray(call, withTags) 180 } 181 func (p *pathObject) CapitalizedToArray(call goja.FunctionCall) goja.Value { 182 return p.ToArray(call) 183 } 184 func (p *pathObject) CapitalizedTagArray(call goja.FunctionCall) goja.Value { 185 return p.TagArray(call) 186 } 187 func (p *pathObject) CapitalizedtoValue(withTags bool) (interface{}, error) { 188 return p.toValue(withTags) 189 } 190 func (p *pathObject) CapitalizedToValue() (interface{}, error) { 191 return p.ToValue() 192 } 193 func (p *pathObject) CapitalizedTagValue() (interface{}, error) { 194 return p.TagValue() 195 } 196 func (p *pathObject) CapitalizedMap(call goja.FunctionCall) goja.Value { 197 return p.Map(call) 198 } 199 func (p *pathObject) CapitalizedForEach(call goja.FunctionCall) goja.Value { 200 return p.ForEach(call) 201 } 202 func (p *pathObject) CapitalizedCount() (int64, error) { 203 return p.Count() 204 } 205 206 func quadValueToString(v quad.Value) string { 207 if s, ok := v.(quad.String); ok { 208 return string(s) 209 } 210 return quad.StringOf(v) 211 }