Now I want to turn it around the other way and consider what happens when a stock price changes.
On this occasion, I have a fixed stock in mind, and want to find all the users who are watching that stock so that I can notify them that the price has changed.
Going back to curl, we can come up with a suitable query:
curl https://user-stocks.cluster-ckgvna81hufy.us-east-1.neptune.amazonaws.com:8182/openCypher -d 'query=MATCH (u:User)-[r]->(s:Stock {symbol:$symbol}) RETURN u.username, s.symbol' -d 'parameters={"symbol": "UPM6"}'If you're surprised that user003 showed up here, don't be. I obtained this stock from the list we generated in the last episode - which was a list of stocks being watched by user003. It would be very strange if it did not show up.
{
"results": [{
"u.username": "user003",
"s.symbol": "UPM6"
}, {
"u.username": "user015",
"s.symbol": "UPM6"
}]
}
So let's repeat our trick from last time and put that into code, starting with a main:
package main
import (
"fmt"
"log"
"os"
"slices"
"github.com/gmmapowell/ignorance/neptune/internal/neptune"
)
func main() {
if len(os.Args) < 2 {
log.Printf("Usage: watchers <stock>")
return
}
stock := os.Args[1]
watchers, err := neptune.FindStockWatchers("user-stocks", stock)
if err != nil {
panic(err)
}
if len(watchers) == 0 {
fmt.Printf("no watchers found\n")
return
}
slices.Sort(watchers)
fmt.Printf("Stock %s watched by:\n", stock)
for _, w := range watchers {
fmt.Printf(" %s\n", w)
}
}
NEPTUNE_FIND_WATCHERS:neptune/cmd/watchers/main.go
This is simpler than before, since we have no need to go to dynamo to find out more information about the stock - since we already started with the stock in hand.The code to query Neptune is basically just a copy-and-paste of the query to go the other way with the appropriate changes for the query above. I have highlighted the lines that are different.
package neptune
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/neptunedata"
)
func FindStockWatchers(db string, stock string) ([]string, error) {
svc, err := openNeptune(db)
if err != nil {
return nil, err
}
query := `
MATCH (u:User)-[r]->(s:Stock {symbol:$symbol})
RETURN u.username, s.symbol
`
params := fmt.Sprintf(`{"symbol": "%s"}`, stock)
linkQuery := neptunedata.ExecuteOpenCypherQueryInput{OpenCypherQuery: aws.String(query), Parameters: aws.String(params)}
out, err := svc.ExecuteOpenCypherQuery(context.TODO(), &linkQuery)
if err != nil {
return nil, err
}
results, err := unpack(out.Results)
if err != nil {
return nil, err
}
var ret []string
for _, m := range results {
ret = append(ret, m["u.username"].(string))
}
return ret, nil
}
NEPTUNE_FIND_WATCHERS:neptune/internal/neptune/findWatchers.go
Yes, I agree: if they are so similar, we should probably refactor them to extract the commonalities. On this occasion, I would rather not do that because I'm not exactly sure what the commonalities are and what patterns I would use to put them back together. In short, I'm not sure how much shorter I could make it, and I think the loss of clarity would not be worth it.And when we're done we end up with:
$ AWS_PROFILE=ziniki-admin cmd/watchers/watchers UPM6
Stock UPM6 watched by:
user003
user015
No comments:
Post a Comment