func (g *graphObject) Vertex(call otto.FunctionCall) otto.Value { qv, err := toQuadValues(exportArgs(call.ArgumentList)) if err != nil { //TODO(dennwc): pass error to caller return otto.NullValue() } return outObj(call, &pathObject{ wk: g.wk, finals: true, path: path.StartMorphism(qv...), }) }
func buildPathFromObject(obj *otto.Object) *path.Path { var p *path.Path val, _ := obj.Get("_gremlin_type") stringArgs := propertiesOf(obj, "string_args") gremlinType := val.String() if prev, _ := obj.Get("_gremlin_prev"); !prev.IsObject() { switch gremlinType { case "vertex": return path.StartMorphism(stringArgs...) case "morphism": return path.StartMorphism() default: panic("No base gremlin path other than 'vertex' or 'morphism'") } } else { p = buildPathFromObject(prev.Object()) } if p == nil { return nil } switch gremlinType { case "Is": return p.Is(stringArgs...) case "In": preds, tags, ok := getViaData(obj) if !ok { return nil } return p.InWithTags(tags, preds...) case "Out": preds, tags, ok := getViaData(obj) if !ok { return nil } return p.OutWithTags(tags, preds...) case "Both": preds, _, ok := getViaData(obj) if !ok { return nil } return p.Both(preds...) case "Follow": subobj := getFirstArgAsMorphismChain(obj) if subobj == nil { return nil } return p.Follow(buildPathFromObject(subobj)) case "FollowR": subobj := getFirstArgAsMorphismChain(obj) if subobj == nil { return nil } return p.FollowReverse(buildPathFromObject(subobj)) case "And", "Intersect": subobj := getFirstArgAsVertexChain(obj) if subobj == nil { return nil } return p.And(buildPathFromObject(subobj)) case "Union", "Or": subobj := getFirstArgAsVertexChain(obj) if subobj == nil { return nil } return p.Or(buildPathFromObject(subobj)) case "Back": if len(stringArgs) != 1 { return nil } return p.Back(stringArgs[0]) case "Tag", "As": return p.Tag(stringArgs...) case "Has": if len(stringArgs) < 2 { return nil } return p.Has(stringArgs[0], stringArgs[1:]...) case "Save", "SaveR": if len(stringArgs) > 2 || len(stringArgs) == 0 { return nil } tag := stringArgs[0] if len(stringArgs) == 2 { tag = stringArgs[1] } if gremlinType == "SaveR" { return p.SaveReverse(stringArgs[0], tag) } return p.Save(stringArgs[0], tag) case "Except", "Difference": subobj := getFirstArgAsVertexChain(obj) if subobj == nil { return nil } return p.Except(buildPathFromObject(subobj)) case "InPredicates": return p.InPredicates() case "OutPredicates": return p.OutPredicates() case "LabelContext": labels, tags, ok := getViaData(obj) if !ok { return nil } return p.LabelContextWithTags(tags, labels...) default: panic(fmt.Sprint("Unimplemented Gremlin function", gremlinType)) } }
func recommendationStory(sr *server, keys []string, limit, offset int) (recResp, error) { start := time.Now() g := sr.graph s := storyByKey(g, keys[0]) log.Printf("Finding recommendations for \"%s\"...", s.Title) recStories := make(map[string]float64) // Fetch users first. paths := []struct { desc string path *path.Path }{ { StoryFavoritedBy, path.StartMorphism(keys...).Out(StoryFavoritedBy), }, { StoryAuthor, path.StartMorphism(keys...).Out(StoryAuthor), }, } var users []string for _, path := range paths { startOpt := time.Now() it, _ := path.path.BuildIteratorOn(g).Optimize() defer it.Close() log.Printf("%s: BuildIterator().Optimize() took %s", path.desc, time.Now().Sub(startOpt)) startFetch := time.Now() nexter := it.(graph.Nexter) for nexter.Next() { users = append(users, g.NameOf(it.Result())) } log.Printf("%s: cayley.RawNext(it) took %s", path.desc, time.Now().Sub(startFetch)) } // Fetch users' stories and favorites. paths2 := []struct { desc string path *path.Path }{ { UserFavoriteStory, path.StartMorphism(users...).Out(UserFavoriteStory), }, { UserStory, path.StartMorphism(users...).Out(UserStory), }, } for _, path := range paths2 { startOpt := time.Now() it, _ := path.path.BuildIteratorOn(g).Optimize() defer it.Close() log.Printf("%s: BuildIterator().Optimize() took %s", path.desc, time.Now().Sub(startOpt)) startFetch := time.Now() nexter := it.(graph.Nexter) for nexter.Next() { stID := g.NameOf(it.Result()) recStories[stID]++ } log.Printf("%s: cayley.RawNext(it) took %s", path.desc, time.Now().Sub(startFetch)) } // Remove favorites pointing to original story. for _, key := range keys { delete(recStories, key) } favorites := 0 for _, count := range recStories { favorites += int(count) } stories := make([]string, 0, len(recStories)) for st := range recStories { stories = append(stories, st) } /* // Weight stories by sum of shared favorites * log(# favorites) / # favorites it2, _ := cayley.StartPath(g, stories...).Save(StoryFavorites, StoryFavorites).BuildIterator().Optimize() defer it2.Close() for i := 0; cayley.RawNext(it2); i++ { story := stories[i] results := map[string]graph.Value{} it2.TagResults(results) favorites := float64(atoi(g.NameOf(results[StoryFavorites]))) if favorites == 0 { continue } val := recStories[story] recStories[story] = val * math.Log(val) / favorites } */ rsl := sortMap(recStories) if len(rsl) != len(stories) { log.Fatalf("len(rsl) = %d, len(stories) = %d", len(rsl), len(stories)) } storyCount := len(rsl) if len(rsl) > (limit + offset) { rsl = rsl[offset : offset+limit] } else if len(rsl) > offset { rsl = rsl[offset:] } else { rsl = nil } startStories := time.Now() sOut := storiesByKeys(g, rsl) log.Printf("storiesByKeys(len = %d) took %s", len(rsl), time.Now().Sub(startStories)) for i, st := range sOut { st.annotate() st.Score = float32(recStories[rsl[i]]) } s.annotate() resp := recResp{ sOut, nil, &s, respStats{ storyCount, favorites, len(users), }, } log.Printf("recommendationStory(%q) took %s, stats %+v", s.Title, time.Now().Sub(start), resp.Stats) return resp, nil }
func (g *graphObject) Morphism(call otto.FunctionCall) otto.Value { return outObj(call, &pathObject{ wk: g.wk, path: path.StartMorphism(), }) }