diff --git a/.goxc.json b/.goxc.json index 538f98c..d655358 100644 --- a/.goxc.json +++ b/.goxc.json @@ -1,6 +1,6 @@ { "Arch": "arm amd64 386", "Os": "linux darwin windows", - "ResourcesInclude": "README.md,static,templates,LICENSE,AUTHORS,CONTRIBUTORS,docs,cayley.cfg.example,30kmoviedata.nq.gz,testdata.nq", + "ResourcesInclude": "README.md,static,templates,LICENSE,AUTHORS,CONTRIBUTORS,docs,cayley.cfg.example,data", "ConfigVersion": "0.9" } diff --git a/README.md b/README.md index 2a2be8e..909cb21 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ If you prefer to build from source, see the documentation on the wiki at [How to `cd` to the directory and give it a quick test with: ``` -./cayley repl --dbpath=testdata.nq +./cayley repl --dbpath=data/testdata.nq ``` You should see a `cayley>` REPL prompt. Go ahead and give it a try: @@ -66,13 +66,13 @@ cayley> graph.Vertex("dani").Out("follows").All() For somewhat more interesting data, a sample of 30k movies from Freebase comes in the checkout. ``` -./cayley repl --dbpath=30kmoviedata.nq.gz +./cayley repl --dbpath=data/30kmoviedata.nq.gz ``` To run the web frontend, replace the "repl" command with "http" ``` -./cayley http --dbpath=30kmoviedata.nq.gz +./cayley http --dbpath=data/30kmoviedata.nq.gz ``` And visit port 64210 on your machine, commonly [http://localhost:64210](http://localhost:64210) diff --git a/api_example.nq b/api_example.nq deleted file mode 100644 index fd62252..0000000 --- a/api_example.nq +++ /dev/null @@ -1,11 +0,0 @@ - . - . - "cool_person" . - . - . - . - . - "cool_person" . - . - . - "cool_person" . diff --git a/cayley_test.go b/cayley_test.go index 3c4c766..95a2c84 100644 --- a/cayley_test.go +++ b/cayley_test.go @@ -415,7 +415,7 @@ func prepare(t testing.TB) { cfg.DatabaseType = *backend switch *backend { case "memstore": - cfg.DatabasePath = "30kmoviedata.nq.gz" + cfg.DatabasePath = "data/30kmoviedata.nq.gz" case "leveldb", "bolt": cfg.DatabasePath = "/tmp/cayley_test_" + *backend cfg.DatabaseOptions = map[string]interface{}{ @@ -450,7 +450,7 @@ func prepare(t testing.TB) { } if needsLoad { - err = load(handle.QuadWriter, cfg, "30kmoviedata.nq.gz", "cquad") + err = load(handle.QuadWriter, cfg, "data/30kmoviedata.nq.gz", "cquad") if err != nil { t.Fatalf("Failed to load %q: %v", cfg.DatabasePath, err) } diff --git a/30kmoviedata.nq.gz b/data/30kmoviedata.nq.gz similarity index 100% rename from 30kmoviedata.nq.gz rename to data/30kmoviedata.nq.gz diff --git a/data/testdata.nq b/data/testdata.nq new file mode 100644 index 0000000..4d211c0 --- /dev/null +++ b/data/testdata.nq @@ -0,0 +1,11 @@ + . + . + "cool_person" . + . + . + . + . + "cool_person" . + . + . + "cool_person" . \ No newline at end of file diff --git a/docs/GremlinAPI.md b/docs/GremlinAPI.md index 2c03c0e..7aab860 100644 --- a/docs/GremlinAPI.md +++ b/docs/GremlinAPI.md @@ -49,26 +49,32 @@ Both `.Morphism()` and `.Vertex()` create path objects, which provide the follow For these examples, suppose we have the following graph: ``` - +---+ +---+ - | A |------- ->| F |<-- - +---+ \------>+---+-/ +---+ \--+---+ - ------>|#B#| | | E | - +---+-------/ >+---+ | +---+ - | C | / v - +---+ -/ +---+ - ---- +---+/ |#G#| - \-->|#D#|------------->+---+ - +---+ + +-------+ +------+ + | alice |----- ->| fred |<-- + +-------+ \---->+-------+-/ +------+ \-+-------+ + ----->| #bob# | | | emily | + +---------+--/ --->+-------+ | +-------+ + | charlie | / v + +---------+ / +--------+ + \--- +--------+ | #greg# | + \-->| #dani# |------------>+--------+ + +--------+ ``` Where every link is a "follows" relationship, and the nodes with an extra `#` in the name have an extra `status` link. As in, ``` -D -- status --> cool_person +dani -- status --> cool_person ``` - Perhaps these are the influencers in our community. + +To load above graph into cayley and reproduce the following examples: + +`./cayley repl --dbpath=data/testdata.nq` for a REPL prompt, or + +`./cayley http --dbpath=data/testdata.nq` for the web frontend. + ### Basic Traversals ####**`path.Out([predicatePath], [tags])`** @@ -89,18 +95,18 @@ Out is the work-a-day way to get between nodes, in the forward direction. Starti Example: ```javascript -// The working set of this is B and D -g.V("C").Out("follows") -// The working set of this is F, as A follows B and B follows F. -g.V("A").Out("follows").Out("follows") -// Finds all things D points at. Result is B G and cool_person -g.V("D").Out() -// Finds all things D points at on the status linkage. -// Result is B G and cool_person -g.V("D").Out(["follows", "status"]) -// Finds all things D points at on the status linkage, given from a separate query path. +// The working set of this is bob and dani +g.V("charlie").Out("follows") +// The working set of this is fred, as alice follows bob and bob follows fred. +g.V("alice").Out("follows").Out("follows") +// Finds all things dani points at. Result is bob, greg and cool_person +g.V("dani").Out() +// Finds all things dani points at on the status linkage. +// Result is bob, greg and cool_person +g.V("dani").Out(["follows", "status"]) +// Finds all things dani points at on the status linkage, given from a separate query path. // Result is {"id": cool_person, "pred": "status"} -g.V("D").Out(g.V("status"), "pred") +g.V("dani").Out(g.V("status"), "pred") ``` ####**`path.In([predicatePath], [tags])`** @@ -121,12 +127,12 @@ Same as Out, but in the other direction. Starting with the nodes in `path` on t Example: ```javascript -// Find the cool people, B G and D +// Find the cool people, bob, greg and dani g.V("cool_person").In("status") -// Find who follows B, in this case, A, C, and D -g.V("B").In("follows") -// Find who follows the people E follows, namely, E and B -g.V("E").Out("follows").In("follows") +// Find who follows bob, in this case, alice, charlie, and dani +g.V("bob").In("follows") +// Find who follows the people emily follows, namely, emily and bob +g.V("emily").Out("follows").In("follows") ``` ####**`path.Both([predicatePath], [tags])`** @@ -148,8 +154,8 @@ Note: Less efficient, for the moment, as it's implemented with an Or, but useful Example: ```javascript -// Find all followers/followees of F. Returns B E and G -g.V("F").Both("follows") +// Find all followers/followees of fred. Returns bob, emily and greg +g.V("fred").Both("follows") ``` @@ -163,9 +169,9 @@ Filter all paths to ones which, at this point, are on the given `node`. Example: ```javascript -// Starting from all nodes in the graph, find the paths that follow B. -// Results in three paths for B (from A C and D) -g.V().Out("follows").Is("B") +// Starting from all nodes in the graph, find the paths that follow bob. +// Results in three paths for bob (from alic, charlie and dani) +g.V().Out("follows").Is("bob") ``` ####**`path.Has(predicate, object)`** @@ -181,10 +187,10 @@ Usually useful for starting with all nodes, or limiting to a subset depending on Example: ```javascript -// Start from all nodes that follow B -- results in A C and D -g.V().Has("follows", "B") -// People C follows who then follow F. Results in B. -g.V("C").Out("follows").Has("follows", "F") +// Start from all nodes that follow bob -- results in alice, charlie and dani +g.V().Has("follows", "bob") +// People charlie follows who then follow fred. Results in bob. +g.V("charlie").Out("follows").Has("follows", "fred") ``` ### Tagging @@ -203,7 +209,7 @@ In order to save your work or learn more about how a path got to the end, we hav Example: ```javascript // Start from all nodes, save them into start, follow any status links, and return the result. -// Results are: {"id": "cool_person", "start": "B"}, {"id": "cool_person", "start": "G"}, {"id": "cool_person", "start": "D"} +// Results are: {"id": "cool_person", "start": "bob"}, {"id": "cool_person", "start": "greg"}, {"id": "cool_person", "start": "dani"} g.V().Tag("start").Out("status") ``` @@ -220,11 +226,11 @@ Example: ```javascript // Start from all nodes, save them into start, follow any status links, jump back to the starting node, and find who follows them. Return the result. // Results are: -// {"id": "A", "start": "B"}, -// {"id": "C", "start": "B"}, -// {"id": "D", "start": "B"}, -// {"id": "C", "start": "D"}, -// {"id": "D", "start": "G"} +// {"id": "alice", "start": "bob"}, +// {"id": "charlie", "start": "bob"}, +// {"id": "dani", "start": "bob"}, +// {"id": "charlie", "start": "dani"}, +// {"id": "dani", "start": "greg"} g.V().Tag("start").Out("status").Back("start").In("follows") ``` @@ -239,12 +245,12 @@ From the current node as the subject, save the object of all quads with `predica Example: ```javascript -// Start from D and B and save who they follow into "target" +// Start from dani and bob and save who they follow into "target" // Returns: -// {"id" : "D", "target": "B" }, -// {"id" : "D", "target": "G" }, -// {"id" : "B", "target": "F" }, -g.V("D", "B").Save("follows", "target") +// {"id" : "dani", "target": "bob" }, +// {"id" : "dani", "target": "greg" }, +// {"id" : "bob", "target": "fred" }, +g.V("dani", "bob").Save("follows", "target") ``` ### Joining @@ -262,11 +268,11 @@ Filters all paths by the result of another query path (efficiently computed). This is essentially a join where, at the stage of each path, a node is shared. Example: ```javascript -var cFollows = g.V("C").Out("follows") -var dFollows = g.V("D").Out("follows") -// People followed by both C (B and D) and D (B and G) -- returns B. +var cFollows = g.V("charlie").Out("follows") +var dFollows = g.V("dani").Out("follows") +// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob. cFollows.Intersect(dFollows) -// Equivalently, g.V("C").Out("follows").And(g.V("D").Out("follows")) +// Equivalently, g.V("charlie").Out("follows").And(g.V("dani").Out("follows")) ``` ####**`path.Union(query)`** @@ -284,9 +290,9 @@ See also: `path.Tag()` Example: ```javascript -var cFollows = g.V("C").Out("follows") -var dFollows = g.V("D").Out("follows") -// People followed by both C (B and D) and D (B and G) -- returns B (from C), B (from D), D and G. +var cFollows = g.V("charlie").Out("follows") +var dFollows = g.V("dani").Out("follows") +// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob (from charlie), bob (from dani), dani and greg. cFollows.Union(dFollows) ``` ####**`path.Except(query)`** @@ -303,11 +309,11 @@ In a set-theoretic sense, this is (A - B). While `g.V().Except(path)` to achieve Example: ```javascript -var cFollows = g.V("C").Out("follows") -var dFollows = g.V("D").Out("follows") -// People followed by both C (B and D) and D (B and G) -- returns B. -cFollows.Except(dFollows) // The set (D) -- what C follows that D does not also follow. -// Equivalently, g.V("C").Out("follows").Except(g.V("D").Out("follows")) +var cFollows = g.V("charlie").Out("follows") +var dFollows = g.V("dani").Out("follows") +// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob. +cFollows.Except(dFollows) // The set (dani) -- what charlie follows that dani does not also follow. +// Equivalently, g.V("charlie").Out("follows").Except(g.V("dani").Out("follows")) ``` @@ -327,10 +333,10 @@ Starts as if at the g.M() and follows through the morphism path. Example: ```javascript: friendOfFriend = g.Morphism().Out("follows").Out("follows") -// Returns the followed people of who C follows -- a simplistic "friend of my friend" +// Returns the followed people of who charlie follows -- a simplistic "friend of my friend" // and whether or not they have a "cool" status. Potential for recommending followers abounds. -// Returns B and G -g.V("C").Follow(friendOfFriend).Has("status", "cool_person") +// Returns bob and greg +g.V("charlie").Follow(friendOfFriend).Has("status", "cool_person") ``` ####**`path.FollowR(morphism)`** @@ -348,7 +354,7 @@ Example: ```javascript: friendOfFriend = g.Morphism().Out("follows").Out("follows") // Returns the third-tier of influencers -- people who follow people who follow the cool people. -// Returns E B C (from B) and C (from G) +// Returns emily, bob, charlie (from bob) and charlie (from greg) g.V().Has("status", "cool_person").FollowR(friendOfFriend) ``` @@ -384,8 +390,8 @@ Returns: Array Executes a query and returns the results at the end of the query path. Example: ```javascript -// fooNames contains an Array of names for foo. -var fooNames = g.V("foo").Out("name").ToArray() +// bobFollowers contains an Array of followers of bob (alice, charlie, dani). +var bobFollowers = g.V("bob").In("follows").ToArray() ``` ####**`query.ToValue()`** @@ -394,7 +400,7 @@ Arguments: None Returns: String -As `.ToArray` above, but limited to one result node -- a string. Like `.Limit(1)` for the above case. +As `.ToArray` above, but limited to one result node -- a string. Like `.Limit(1)` for the above case (alice). ####**`query.TagArray()`** @@ -405,10 +411,10 @@ Returns: Array of string-to-string objects As `.ToArray` above, but instead of a list of top-level strings, returns an Array of tag-to-string dictionaries, much as `.All` would, except inside the Javascript environment. Example: ```javascript -// fooNames contains an Array of names for foo. -var fooTags = g.V("foo").Tag("foo_value").Out("name").ToArray() -// fooValue should be the string "foo" -var fooValue = fooTags[0]["foo_value"] +// bobFollowers contains an Array of followers of bob (alice, charlie, dani). +var bobFollowers = g.V("bob").Tag("name").In("follows").ToArray() +// nameValue should be the string "bob" +var nameValue = bobTags[0]["name"] ``` ####**`query.TagValue()`** diff --git a/docs/Overview.md b/docs/Overview.md index 852a4da..ada5960 100644 --- a/docs/Overview.md +++ b/docs/Overview.md @@ -29,13 +29,13 @@ You can repeat the `--db` and `--dbpath` flags from here forward instead of the First we load the data. ```bash -./cayley load --config=cayley.cfg.overview --quads=30kmoviedata.nq.gz +./cayley load --config=cayley.cfg.overview --quads=data/30kmoviedata.nq.gz ``` And wait. It will load. If you'd like to watch it load, you can run ```bash -./cayley load --config=cayley.cfg.overview --quads=30kmoviedata.nq.gz --alsologtostderr +./cayley load --config=cayley.cfg.overview --quads=data/30kmoviedata.nq.gz --alsologtostderr ``` And watch the log output go by. diff --git a/query/gremlin/gremlin_test.go b/query/gremlin/gremlin_test.go index 10dfe2b..bd4d072 100644 --- a/query/gremlin/gremlin_test.go +++ b/query/gremlin/gremlin_test.go @@ -28,29 +28,30 @@ import ( // This is a simple test graph. // -// +---+ +---+ -// | A |------- ->| F |<-- -// +---+ \------>+---+-/ +---+ \--+---+ -// ------>|#B#| | | E | -// +---+-------/ >+---+ | +---+ -// | C | / v -// +---+ -/ +---+ -// ---- +---+/ |#G#| -// \-->|#D#|------------->+---+ -// +---+ +// +-------+ +------+ +// | alice |----- ->| fred |<-- +// +-------+ \---->+-------+-/ +------+ \-+-------+ +// ----->| #bob# | | | emily | +// +---------+--/ --->+-------+ | +-------+ +// | charlie | / v +// +---------+ / +--------+ +// \--- +--------+ | #greg# | +// \-->| #dani# |------------>+--------+ +// +--------+ // + var simpleGraph = []quad.Quad{ - {"A", "follows", "B", ""}, - {"C", "follows", "B", ""}, - {"C", "follows", "D", ""}, - {"D", "follows", "B", ""}, - {"B", "follows", "F", ""}, - {"F", "follows", "G", ""}, - {"D", "follows", "G", ""}, - {"E", "follows", "F", ""}, - {"B", "status", "cool", "status_graph"}, - {"D", "status", "cool", "status_graph"}, - {"G", "status", "cool", "status_graph"}, + {"alice", "follows", "bob", ""}, + {"charlie", "follows", "bob", ""}, + {"charlie", "follows", "dani", ""}, + {"dani", "follows", "bob", ""}, + {"bob", "follows", "fred", ""}, + {"fred", "follows", "greg", ""}, + {"dani", "follows", "greg", ""}, + {"emily", "follows", "fred", ""}, + {"bob", "status", "cool_person", "status_graph"}, + {"dani", "status", "cool_person", "status_graph"}, + {"greg", "status", "cool_person", "status_graph"}, } func makeTestSession(data []quad.Quad) *Session { @@ -72,67 +73,67 @@ var testQueries = []struct { { message: "get a single vertex", query: ` - g.V("A").All() + g.V("alice").All() `, - expect: []string{"A"}, + expect: []string{"alice"}, }, { message: "use .Out()", query: ` - g.V("A").Out("follows").All() + g.V("alice").Out("follows").All() `, - expect: []string{"B"}, + expect: []string{"bob"}, }, { message: "use .In()", query: ` - g.V("B").In("follows").All() + g.V("bob").In("follows").All() `, - expect: []string{"A", "C", "D"}, + expect: []string{"alice", "charlie", "dani"}, }, { message: "use .Both()", query: ` - g.V("F").Both("follows").All() + g.V("fred").Both("follows").All() `, - expect: []string{"B", "G", "E"}, + expect: []string{"bob", "greg", "emily"}, }, { message: "use .Tag()-.Is()-.Back()", query: ` - g.V("B").In("follows").Tag("foo").Out("status").Is("cool").Back("foo").All() + g.V("bob").In("follows").Tag("foo").Out("status").Is("cool_person").Back("foo").All() `, - expect: []string{"D"}, + expect: []string{"dani"}, }, { message: "separate .Tag()-.Is()-.Back()", query: ` - x = g.V("C").Out("follows").Tag("foo").Out("status").Is("cool").Back("foo") - x.In("follows").Is("D").Back("foo").All() + x = g.V("charlie").Out("follows").Tag("foo").Out("status").Is("cool_person").Back("foo") + x.In("follows").Is("dani").Back("foo").All() `, - expect: []string{"B"}, + expect: []string{"bob"}, }, { message: "do multiple .Back()s", query: ` - g.V("E").Out("follows").As("f").Out("follows").Out("status").Is("cool").Back("f").In("follows").In("follows").As("acd").Out("status").Is("cool").Back("f").All() + g.V("emily").Out("follows").As("f").Out("follows").Out("status").Is("cool_person").Back("f").In("follows").In("follows").As("acd").Out("status").Is("cool_person").Back("f").All() `, tag: "acd", - expect: []string{"D"}, + expect: []string{"dani"}, }, { message: "use Except to filter out a single vertex", query: ` - g.V("A", "B").Except(g.V("A")).All() + g.V("alice", "bob").Except(g.V("alice")).All() `, - expect: []string{"B"}, + expect: []string{"bob"}, }, { message: "use chained Except", query: ` - g.V("A", "B", "C").Except(g.V("B")).Except(g.V("C")).All() + g.V("alice", "bob", "charlie").Except(g.V("bob")).Except(g.V("charlie")).All() `, - expect: []string{"A"}, + expect: []string{"alice"}, }, // Morphism tests. @@ -140,17 +141,17 @@ var testQueries = []struct { message: "show simple morphism", query: ` grandfollows = g.M().Out("follows").Out("follows") - g.V("C").Follow(grandfollows).All() + g.V("charlie").Follow(grandfollows).All() `, - expect: []string{"G", "F", "B"}, + expect: []string{"greg", "fred", "bob"}, }, { message: "show reverse morphism", query: ` grandfollows = g.M().Out("follows").Out("follows") - g.V("F").FollowR(grandfollows).All() + g.V("fred").FollowR(grandfollows).All() `, - expect: []string{"A", "C", "D"}, + expect: []string{"alice", "charlie", "dani"}, }, // Intersection tests. @@ -158,59 +159,59 @@ var testQueries = []struct { message: "show simple intersection", query: ` function follows(x) { return g.V(x).Out("follows") } - follows("D").And(follows("C")).All() + follows("dani").And(follows("charlie")).All() `, - expect: []string{"B"}, + expect: []string{"bob"}, }, { message: "show simple morphism intersection", query: ` grandfollows = g.M().Out("follows").Out("follows") function gfollows(x) { return g.V(x).Follow(grandfollows) } - gfollows("A").And(gfollows("C")).All() + gfollows("alice").And(gfollows("charlie")).All() `, - expect: []string{"F"}, + expect: []string{"fred"}, }, { message: "show double morphism intersection", query: ` grandfollows = g.M().Out("follows").Out("follows") function gfollows(x) { return g.V(x).Follow(grandfollows) } - gfollows("E").And(gfollows("C")).And(gfollows("B")).All() + gfollows("emily").And(gfollows("charlie")).And(gfollows("bob")).All() `, - expect: []string{"G"}, + expect: []string{"greg"}, }, { message: "show reverse intersection", query: ` grandfollows = g.M().Out("follows").Out("follows") - g.V("G").FollowR(grandfollows).Intersect(g.V("F").FollowR(grandfollows)).All() + g.V("greg").FollowR(grandfollows).Intersect(g.V("fred").FollowR(grandfollows)).All() `, - expect: []string{"C"}, + expect: []string{"charlie"}, }, { message: "show standard sort of morphism intersection, continue follow", query: `gfollowers = g.M().In("follows").In("follows") - function cool(x) { return g.V(x).As("a").Out("status").Is("cool").Back("a") } - cool("G").Follow(gfollowers).Intersect(cool("B").Follow(gfollowers)).All() + function cool(x) { return g.V(x).As("a").Out("status").Is("cool_person").Back("a") } + cool("greg").Follow(gfollowers).Intersect(cool("bob").Follow(gfollowers)).All() `, - expect: []string{"C"}, + expect: []string{"charlie"}, }, // Gremlin Has tests. { message: "show a simple Has", query: ` - g.V().Has("status", "cool").All() + g.V().Has("status", "cool_person").All() `, - expect: []string{"G", "D", "B"}, + expect: []string{"greg", "dani", "bob"}, }, { message: "show a double Has", query: ` - g.V().Has("status", "cool").Has("follows", "F").All() + g.V().Has("status", "cool_person").Has("follows", "fred").All() `, - expect: []string{"B"}, + expect: []string{"bob"}, }, // Tag tests. @@ -220,20 +221,20 @@ var testQueries = []struct { g.V().Save("status", "somecool").All() `, tag: "somecool", - expect: []string{"cool", "cool", "cool"}, + expect: []string{"cool_person", "cool_person", "cool_person"}, }, { message: "show a simple saveR", query: ` - g.V("cool").SaveR("status", "who").All() + g.V("cool_person").SaveR("status", "who").All() `, tag: "who", - expect: []string{"G", "D", "B"}, + expect: []string{"greg", "dani", "bob"}, }, { message: "show an out save", query: ` - g.V("D").Out(null, "pred").All() + g.V("dani").Out(null, "pred").All() `, tag: "pred", expect: []string{"follows", "follows", "status"}, @@ -241,7 +242,7 @@ var testQueries = []struct { { message: "show a tag list", query: ` - g.V("D").Out(null, ["pred", "foo", "bar"]).All() + g.V("dani").Out(null, ["pred", "foo", "bar"]).All() `, tag: "foo", expect: []string{"follows", "follows", "status"}, @@ -249,16 +250,16 @@ var testQueries = []struct { { message: "show a pred list", query: ` - g.V("D").Out(["follows", "status"]).All() + g.V("dani").Out(["follows", "status"]).All() `, - expect: []string{"B", "G", "cool"}, + expect: []string{"bob", "greg", "cool_person"}, }, { message: "show a predicate path", query: ` - g.V("D").Out(g.V("follows"), "pred").All() + g.V("dani").Out(g.V("follows"), "pred").All() `, - expect: []string{"B", "G"}, + expect: []string{"bob", "greg"}, }, } diff --git a/testdata.nq b/testdata.nq deleted file mode 100644 index c551ccb..0000000 --- a/testdata.nq +++ /dev/null @@ -1,9 +0,0 @@ - . - . - . - . - . - "cool" . - "not cool" . - "cool" . - "not cool" .