How will we entered the data according to the number of result and also according the count field? - mongodb

By using go api I'm retrieving the an array object. like given below:-
[
{0 1 Sunday 1 21600 25200 1}
{0 1 Sunday 2 28800 32400 2}
{0 1 Sunday 3 36000 39600 1}
]
This data will be arranged using struct:-
type ProviderSpot struct {
Id int `json:"_id" bson:"_id"`
PId int `json:"pid" bson:"pid"`
Day string `json:"day" bson:"day"`
TimeSlug int `json:"time_slug" bson:"time_slug"`
StartTime int64 `json:"start_time" bson:"start_time"`
EndTime int64 `json:"end_time" bson:"end_time"`
Count int `json:"count" bson:"count"`
}
type ProviderSpots []ProviderSpot
See in the array object I have an count values in each object 1,2,1 then I have to store this record like that those record having count they will store in the available_spot only one time means that the upper record will save in the collection only one time having there count value 1 after that the left record will remains there count value 0,1,0 then
those record having there count field value more than 0 they will save that number of times having count value in the addition_spot. The code of golang I'm using is this:-
func SaveProviderSpot(c *gin.Context) {
response := ResponseController{}
values := c.PostForm("array")
var err error
byt := []byte(values)
var result models.ProviderSpots
if err = json.Unmarshal(byt, &result); err != nil{
fmt.Println(err)
}
fmt.Println(result)
for i := 0; i < len(result); i++ {
lastValue :=result[i].Count-1
if lastValue != -1 {
providerspot.PId = result[i].PId
providerspot.Day = result[i].Day
providerspot.TimeSlug = result[i].TimeSlug
providerspot.StartTime = result[i].StartTime
providerspot.EndTime = result[i].EndTime
providerspot.Count = result[i].Count - lastValue
id, _ := models.GetAutoIncrementCounter(config.ProvidersSpotsCounterId, config.ProvidersSpotsCollection)
providerspot.Id = id
fmt.Println("Here We go now :- ", &providerspot)
err = models.AddProviderSpot(&providerspot)
}
}
}
Give some example of this which will solve this. Thanks for your valueable time spending on this question.

I solved that question answer but Can anyone tell me that will right for my code or not:-
func SaveProviderSpot(c *gin.Context) {
response := ResponseController{}
values := c.PostForm("array")
var err error
byt := []byte(values)
var result models.ProviderSpots
if err = json.Unmarshal(byt, &result); err != nil{
fmt.Println(err)
}
fmt.Println(result)
for i := 0; i < len(result); i++ {
for j := 1; j <= result[i].Count; j++ {
// lastValue := result[i].Count-1
// if lastValue != -1 {
if j == 1{
providerspot.PId = result[i].PId
providerspot.Day = result[i].Day
providerspot.TimeSlug = result[i].TimeSlug
providerspot.StartTime = result[i].StartTime
providerspot.EndTime = result[i].EndTime
providerspot.Count = 1//result[i].Count - lastValue
id, _ := models.GetAutoIncrementCounter(config.ProvidersSpotsCounterId, config.ProvidersSpotsCollection)
providerspot.Id = id
fmt.Println("Here We go now :- ", &providerspot)
err = models.AddProviderSpot(&providerspot)
}else{
providerspot.PId = result[i].PId
providerspot.Day = result[i].Day
providerspot.TimeSlug = result[i].TimeSlug
providerspot.StartTime = result[i].StartTime
providerspot.EndTime = result[i].EndTime
providerspot.Count = 1//result[i].Count - lastValue
id, _ := models.GetAutoIncrementCounter(config.AdditionalProviderCounterSpot, config.AdditionalProviderSpot)
providerspot.Id = id
err = models.AddAdditionalProviderSpot(&providerspot)
}
}
}
}
This will do want I want but I'm confused that it is right for me or not.

Related

How to use pgcrypto with go-pg for column encryption?

Could you help me please with my problem. I'm trying to use column encryption for postgres and there is one small question: how I can achieve transformation value of column (ex: "test_value") to PGP_SYM_ENCRYPT('test_value', 'KEY') in "insert" sql query?
As I understood, the custom types can be the solution for me, but some things isn't clear... Maybe anyone has an example for my case?
(I see this aws docs about pgcrypto using: https://docs.aws.amazon.com/dms/latest/sql-server-to-aurora-postgresql-migration-playbook/chap-sql-server-aurora-pg.security.columnencryption.html)
What I did:
type sstring struct {
string
}
var _ types.ValueAppender = (*sstring)(nil)
func (tm sstring) AppendValue(b []byte, flags int) ([]byte, error) {
if flags == 1 {
b = append(b, '\'')
}
b = []byte("PGP_SYM_ENCRYPT('123456', 'AES_KEY')")
if flags == 1 {
b = append(b, '\'')
}
return b, nil
}
var _ types.ValueScanner = (*sstring)(nil)
func (tm *sstring) ScanValue(rd types.Reader, n int) error {
if n <= 0 {
tm.string = ""
return nil
}
tmp, err := rd.ReadFullTemp()
if err != nil {
return err
}
tm.string = string(tmp)
return nil
}
type model struct {
ID uint `pg:"id"`
Name string `pg:"name"`
Crypto sstring `pg:"crypto,type:sstring"`
tableName struct{} `pg:"models"`
}
----------
_, err := r.h.ModelContext(ctx, model).Insert()
And... the process just do nothing. Do not respond, do not fall, do not create row in sql table.. Nothing.
Anyway. My question is how to implement wrap some column by sql function using pg-go orm.
I tried to use https://github.com/go-pg/pg/blob/v10/example_custom_test.go#L13-L49 as custom type handler example.. But smth went wrong. =(

How to append to bson object

I have an endpoint where users can filter a mongo collection using query parameters. If I have just one query parameter e.g. title, I can do this -
filter := bson.M{}
if params.Title != "" {
filter = bson.M{"title": params.Title}
}
However, if I have more than one query parameter, I can't seem to get how to append to the bson object.
I tried this -
filter := []bson.M{}
if params.Title != "" {
filter = append(filter, bson.M{"title": params.Title})
}
if params.Description != "" {
filter = append(filter, bson.M{"description": params.Description})
}
but I got this error - cannot transform type []primitive.M to a BSON Document: WriteArray can only write a Array while positioned on a Element or Value but is positioned on a TopLevel
How do I solve this?
bson.M{} is underlined map[string]interface{} in go-mongo-driver. So if you need to add more elemnets, you can not append. Just assign that value to map's key as below.
filter := bson.M{}
if params.Title != "" {
//filter = bson.M{"title": params.Title}
filter["title"] = params.Title
}
if params.Description != "" {
filter["description"] = params.Description
}
Consider a collection test with a document: { "_id" : 1, "Title" : "t-1", "Description" : "d-1" }
And, you can use the following:
title := "t-1"
description := "" // or "d-1"
filter := bson.M{}
if Title != "" {
filter["Title"] = title
}
if Description != "" {
filter["Description"] = description
}
//fmt.Println(filter);
var result bson.M
collection := client.Database("test").Collection("test")
err := collection.FindOne(context.TODO(), filter).Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found a single document: %+v\n", result)

go insert composite type array unsupported type

sql
CREATE TABLE public.tiantang_page (
href varchar NOT NULL,
status int4 NOT NULL,
description varchar NOT NULL,
urls url[] NULL
);
CREATE TYPE url AS (
url varchar,
status int4);
insert composite type array
type url struct {
url string
status int
}
var urls [1]url
urls[0] = url{
url: "",
status: 0,
}
update := "UPDATE \"public\".\"tiantang_page\" SET \"urls\"=$1 where \"href\"=$2;"
r, err := db.Exec(update, pq.Array(urls),href)
if err != nil {
log.Fatal(err)
}
error
sql: converting argument $1 type: unsupported type parsetest.url, a struct
library
https://godoc.org/github.com/lib/pq
Note that custom composite types are not fully supported by lib/pq.
If all you want is to be able to store the urls then the simplest approach would be to implement the driver.Valuer interface on the url type and then use it as you do with pq.Array:
func (u url) Value() (driver.Value, error) {
return fmt.Sprintf("(%s,%d)", u.url, u.status), nil
}
// ...
r, err := db.Exec(update, pq.Array(urls), href)
more info on that can be found here: https://github.com/lib/pq/issues/544
Note that I haven't tried this with arrays, only with slices, so you may have to switch from using an array to using a slice, i.e. instead of var urls [1]url you would use var urls = make([]url, 1).
If you also want to be able to retrieve the array of urls back from the db, then you'll have to implement the sql.Scanner interface, however here the pq.Array is not very reliable and you'll have to implement the scanner on the slice type and do all the parsing yourself.
The general format of composite types is (val1, val2, ...) note that you have to put double quotes around values that contain commas or parentheses. For example to construct a value of the url type you would use the literal expression: (http://example.com,4). More info in the docs.
The format for an array of composite types is {"(val1, val2, ...)" [, ...]}, note that in this case if you need to put double quotes around the values you need to escape them. For example {"(http://example.com,4)","(\"http://example.com/?list=foo,bar,baz\",3)"}
So as you can see the more complex the data in the composite type the more complex will be the parsing as well.
Here's a crude example (does not handle quoted values):
type urlslice []url
func (s *urlslice) Scan(src interface{}) error {
var a []byte // the pq array as bytes
switch v := src.(type) {
case []byte:
a = v
case string:
a = []byte(v)
case nil:
*s = nil
return nil
default:
return fmt.Errorf("urlslice.Scan unexpected src type %T", src)
}
a = a[1 : len(a)-1] // drop curly braces
for i := 0; i < len(a); i++ {
if a[i] == '"' && (len(a) > (i+1) && a[i+1] == '(') { // element start?
i += 2 // move past `"(`
j := i // start of url.url
u := url{}
for ; i < len(a) && a[i] != ','; i++ {
}
u.url = string(a[j:i])
i += 1 // move past `,`
j = i // start of url.status
for ; i < len(a) && a[i] != ')'; i++ {
}
i64, err := strconv.ParseInt(string(a[j:i]), 10, 64)
if err != nil {
return err
}
u.status = int(i64)
*s = append(*s, u)
i += 2 // move past `)",`
}
}
return nil
}
for completeness, here's the Valuer interface implemented by the slice type, again not handling proper quoting of values that may require it:
func (s urlslice) Value() (driver.Value, error) {
data := []byte{'{'}
for _, url := range s {
data = append(data, '"', '(')
data = append(data, []byte(url.url)...)
data = append(data, ',')
data = strconv.AppendInt(data, int64(url.status), 10)
data = append(data, ')', '"', ',')
}
data[len(data)-1] = '}' // replace last ',' with '}' to close the array
return data, nil
}
With the urlslice implementing the two interfaces directly you can stop using pq.Array.
var urls = urlslice{{
url: "http://example.com",
status: 4,
}}
update := `UPDATE "public"."tiantang_page" SET "urls"=$1 where "href"=$2`
r, err := db.Exec(update, urls, href)
if err != nil {
log.Fatal(err)
}
var urls2 urlslice
selurls := `SELECT "urls" FROM "public"."tiantang_page" where "href" = $1`
if err := db.QueryRow(selurls, href).Scan(&urls2); err != nil {
log.Fatal(err)
}
Please keep in mind that both of the above examples should be considered only as hints of the direction to take in solving this problem. Not only are the two examples incomplete in that they don't handle quoted values, but they are also not very elegant implementations.
Reasonably complete composite literal parser:
type parseState int
const (
state_initial parseState = iota // start
state_value_start // no bytes read from value yet
state_value // unquoted value
state_quoted // inside quote
state_value_end // after a close quote
state_end // after close paren
)
func parseComposite(in []byte) ([]string, error) {
state := state_initial
ret := []string{}
val := []byte{}
for _, b := range in {
switch state {
case state_initial:
if b != '(' {
return nil, fmt.Errorf("initial character not ')': %v", in)
} else {
state = state_value_start
}
case state_value_start:
if b == '"' {
state = state_quoted
continue
}
fallthrough
case state_value:
if b == ',' {
ret = append(ret, string(val))
val = nil
state = state_value_start
} else if b == ')' {
ret = append(ret, string(val))
val = nil
state = state_end
} else {
val = append(val, b)
}
case state_quoted:
if b == '"' {
ret = append(ret, string(val))
val = nil
state = state_value_end
} else {
val = append(val, b)
}
case state_value_end:
if b == ',' {
state = state_value_start
} else if b == ')' {
state = state_end
} else {
return nil, fmt.Errorf("invalid delimiter after closing quote: %v", in)
}
case state_end:
return nil, fmt.Errorf("trailing bytes: %v", in)
}
}
if state != state_end {
return nil, fmt.Errorf("unterminated value: %v", in)
}
return ret, nil
}

How to use mongodb/mongo-go-driver to perform efficient paging

I read in the following article that it is more efficient to use the natural ordering of _id to perform pagination because skip always starts from the beginning of the collection.
Fast and Efficient Pagination in MongoDB
// Page 1
db.students.find().limit(10)
// Page 2
last_id = ... # logic to get last_id
db.students.find({'_id': {'$gt': last_id}}).limit(10)
But I have no idea how to perform the above using the mongodb/mongo-go-driver.
you can create a new func, dont forget to pass http.writer to read parameter.
func Pagination(r *http.Request, FindOptions *options.FindOptions) (int64, int64) {
if r.URL.Query().Get("page") != "" && r.URL.Query().Get("limit") != "" {
page, _ := strconv.ParseInt(r.URL.Query().Get("page"), 10, 32)
limit, _ := strconv.ParseInt(r.URL.Query().Get("limit"), 10, 32)
if page == 1 {
FindOptions.SetSkip(0)
FindOptions.SetLimit(limit)
return page, limit
}
FindOptions.SetSkip((page - 1) * limit)
FindOptions.SetLimit(limit)
return page, limit
}
FindOptions.SetSkip(0)
FindOptions.SetLimit(0)
return 0, 0
}
just call
Pagination(r, options)
example
options := options.Find()
page, limit := parameter.Pagination(r, options)
// page, limit as response for header payload
The cursor.skip() method requires the server to scan from the beginning of the input results set before beginning to return results. As the offset increases, cursor.skip() will become slower. While range queries can use indexes to avoid scanning unwanted documents, typically yielding better performance as the offset grows compared to using cursor.skip() for pagination. See more information on MongoDB: Pagination Example
Using the current version of mongo-go-driver (v0.0.15).An example to perform pagination showing latest entry first:
func Paginate(collection *mongo.Collection, startValue objectid.ObjectID, nPerPage int64) ([]bson.Document, *bson.Value, error) {
// Query range filter using the default indexed _id field.
filter := bson.VC.DocumentFromElements(
bson.EC.SubDocumentFromElements(
"_id",
bson.EC.ObjectID("$gt", startValue),
),
)
var opts []findopt.Find
opts = append(opts, findopt.Sort(bson.NewDocument(bson.EC.Int32("_id", -1))))
opts = append(opts, findopt.Limit(nPerPage))
cursor, _ := collection.Find(context.Background(), filter, opts...)
var lastValue *bson.Value
var results []bson.Document
for cursor.Next(context.Background()) {
elem := bson.NewDocument()
err := cursor.Decode(elem)
if err != nil {
return results, lastValue, err
}
results = append(results, *elem)
lastValue = elem.Lookup("_id")
}
return results, lastValue, nil
}
An example to call the pagination function above:
database := client.Database("databaseName")
collection := database.Collection("collectionName")
startObjectID, _ := objectid.FromHex("5bbafea2b5e14ee3a298fa4a")
// Paginate only the latest 20 documents
elements, lastID, err := Paginate(collection, startObjectID, 20)
for _, e := range elements {
fmt.Println(&e)
}
// Last seen ObjectID can be used to call next Paginate()
fmt.Println("Last seen ObjectID: ", lastID.ObjectID())
Note that you can also substitute the _id field with another indexed field.
Set page=0 and limit=0 if no pagination is required.
func GetUsers (page, limit int) {
filter := bson.D{{}} // selects all documents
options := new(options.FindOptions)
if limit != 0 {
if page == 0 {
page = 1
}
options.SetSkip(int64((page - 1) * limit))
options.SetLimit(int64(limit))
}
cursor, err := mongoCollection.Find(context.TODO(), filter, options)
...
}

raw socket didn't receive icmp response

I'm trying to send an icmp message whose TTL is just 1, and expect to receive a time exceeded message. that message does come(I see it from wireshark), but my program blocks on syscall.Recvfrom. Anyone knows why?
icmp.go
package main
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"os"
"syscall"
)
type ICMP struct {
Type uint8
Code uint8
Checksum uint16
Identifier uint16
SeqNo uint16
}
func Checksum(data []byte) uint16 {
var (
sum uint32
length int = len(data)
index int
)
for length > 1 {
sum += uint32(data[index])<<8 + uint32(data[index+1])
index += 2
length -= 2
}
if length > 0 {
sum += uint32(data[index])
}
sum += (sum >> 16)
return uint16(^sum)
}
func main() {
h := Header{
Version: 4,
Len: 20,
TotalLen: 20 + 8,
TTL: 1,
Protocol: 1,
// Dst:
}
argc := len(os.Args)
if argc < 2 {
fmt.Println("usage: program + host")
return
}
ipAddr, _ := net.ResolveIPAddr("ip", os.Args[1])
h.Dst = ipAddr.IP
icmpReq := ICMP{
Type: 8,
Code: 0,
Identifier: 0,
SeqNo: 0,
}
out, err := h.Marshal()
if err != nil {
fmt.Println("ip header error", err)
return
}
var icmpBuf bytes.Buffer
binary.Write(&icmpBuf, binary.BigEndian, icmpReq)
icmpReq.Checksum = Checksum(icmpBuf.Bytes())
icmpBuf.Reset()
binary.Write(&icmpBuf, binary.BigEndian, icmpReq)
fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
addr := syscall.SockaddrInet4{
Port: 0,
}
copy(addr.Addr[:], ipAddr.IP[12:16])
pkg := append(out, icmpBuf.Bytes()...)
fmt.Println("ip length", len(pkg))
if err := syscall.Sendto(fd, pkg, 0, &addr); err != nil {
fmt.Println("Sendto err:", err)
}
var recvBuf []byte
if nBytes, rAddr, err := syscall.Recvfrom(fd, recvBuf, 0); err == nil {
fmt.Printf("recv %d bytes from %v\n", nBytes, rAddr)
}
}
additionally, I use header.go and helper.go from https://github.com/golang/net/tree/master/ipv4
As Andy pointed out, the raw(7) man page says:
An IPPROTO_RAW socket is send only. If you really want to receive
all IP packets, use a packet(7) socket with the ETH_P_IP protocol.
Note that packet sockets don't reassemble IP fragments, unlike raw
sockets.
I know I can receive ICMP reply if I set IPPROTO_ICMP as the protocol when I create the socket, but I need to set TTL to 1 which must be done in IP layer. Therefore I send the ICMP request with IPPROTO_RAW socket, after that I use net.ListenIP to receive ICMP messages. Here is the code:
package main
import (
"bytes"
"encoding/binary"
"log"
"net"
"os"
"syscall"
)
const icmpID uint16 = 43565 // use a magic number for now
type ICMP struct {
Type uint8
Code uint8
Checksum uint16
Identifier uint16
SeqNo uint16
}
func Checksum(data []byte) uint16 {
var (
sum uint32
length int = len(data)
index int
)
for length > 1 {
sum += uint32(data[index])<<8 + uint32(data[index+1])
index += 2
length -= 2
}
if length > 0 {
sum += uint32(data[index])
}
sum += (sum >> 16)
return uint16(^sum)
}
func main() {
h := Header{
Version: 4,
Len: 20,
TotalLen: 20 + 8,
TTL: 1,
Protocol: 1,
}
argc := len(os.Args)
if argc < 2 {
log.Println("usage: program + host")
return
}
ipAddr, _ := net.ResolveIPAddr("ip", os.Args[1])
h.Dst = ipAddr.IP
icmpReq := ICMP{
Type: 8,
Code: 0,
Identifier: icmpID,
SeqNo: 1,
}
out, err := h.Marshal()
if err != nil {
log.Println("ip header error", err)
return
}
var icmpBuf bytes.Buffer
binary.Write(&icmpBuf, binary.BigEndian, icmpReq)
icmpReq.Checksum = Checksum(icmpBuf.Bytes())
icmpBuf.Reset()
binary.Write(&icmpBuf, binary.BigEndian, icmpReq)
fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
addr := syscall.SockaddrInet4{
Port: 0,
}
copy(addr.Addr[:], ipAddr.IP[12:16])
pkg := append(out, icmpBuf.Bytes()...)
if err := syscall.Sendto(fd, pkg, 0, &addr); err != nil {
log.Println("Sendto err:", err)
}
laddr, err := net.ResolveIPAddr("ip4:icmp", "0.0.0.0")
if err != nil {
log.Fatal(err)
}
c, err := net.ListenIP("ip4:icmp", laddr)
if err != nil {
log.Fatal(err)
}
for {
buf := make([]byte, 2048)
n, raddr, err := c.ReadFrom(buf)
if err != nil {
log.Println(err)
continue
}
icmpType := buf[0]
if icmpType == 11 {
if n == 36 { // Time exceeded messages
// A time exceeded message contain IP header(20 bytes) and first 64 bits of the original payload
id := binary.BigEndian.Uint16(buf[32:34])
log.Println("recv id", id)
if id == icmpID {
log.Println("recv Time Exceeded from", raddr)
}
}
}
}
}
Actually, I am writing a traceroute in go, if anyone is interested about that, the whole code is in github.
I think you need to give IPPROTO_ICMP as the protocol when you create your socket. The raw(7) man page says that an IPPROTO_RAW socket is send only. Also, if you use IPPROTO_ICMP, you don't give the IP header. (Note: I haven't actually tried this in Go.)