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  }