Couchbase sort options - go

I am using Go (1.10.3) with the Gin Framework. I am connecting to a Couchbase database (5.1.1) but I am having some issues with sorting the data.
So this is the code I am working with right now,
//Build Search Sort
func searchSort(c *gin.Context) (cbft.SearchSortField, *cbft.SearchSortScore, string) {
mapData := getQuery(c)
sortData := mapData["sort"]
var sortType string
var sortScore *cbft.SearchSortScore
var sortOps *cbft.SearchSortField
if sortData == "amount:asc" {
fmt.Println( sortData )
sortType = "asc"
sortScore = cbft.NewSearchSortScore().Descending(false)
sortOps = cbft.NewSearchSortField("AmountBase").Descending(false)
}
if sortData == "amount:desc" {
fmt.Println( sortData )
sortType = "desc"
sortScore = cbft.NewSearchSortScore().Descending(true)
sortOps = cbft.NewSearchSortField("AmountBase").Descending(true)
}
return sortOps, sortScore
}
Which I load into the query like so,
sortOpsOne, sortOpsTwo := searchSort(c)
ftSearch := gocb.NewSearchQuery("Search", searchOps).Sort(sortOpsOne).Sort(sortOpsTwo).Limit(size).Skip(from)
To explain, the above loads a get string request, e.g. sort=amount:asc to sort the JSON reply by the AmountBase field I have.
However this does not seem to be working at all, I started out with just the sortOps field but I added the sortScore field as the sort field was not doing anything.
Adding the descending(false) / descending(true) to the end of the sort field does not seem to do anything either.
The search is being run on a FTS index based from the data I have loaded into my bucket.
I am not sure what I am doing wrong?
It seems not to be working with descending options the ascending query seems to be working. :)

Related

How to make pagination from inline buttons Telegram in Golang

I am using the library https://github.com/go-telegram-bot-api/telegram-bot-api
But I do not understand how I can implement the output of buttons and arrows. A lot of data with pagination comes from my api, but I don't understand how to make pages in telegrams. If someone gives an example, I will be very grateful!
This is perfectly implemented in python: https://pypi.org/project/python-telegram-bot-pagination/
But i need for Golang :(
As I checked there were no plugins or wrappers for telegram-bot-api package, so you're gonna have to handle this manually.
Suppose we have this data:
var data = []string{"DummyData1", "DummyData2", "DummyData3", "DummyData4", "DummyData5", "DummyData6", "DummyData7", "DummyData8", "DummyData9", "DummyData10"}
If we're going to show 2 items on each page for 10 items, we would have 5 pages:
var count = 2
var maxPages = len(data) / count // = 5
First we should have a function that calculates data slice and gives us the inlineKeyboardMarkup:
func DummyDataTextMarkup(currentPage, count int) (text string, markup tgbotapi.InlineKeyboardMarkup) {
text = strings.Join(data[currentPage*count:currentPage*count+count], "\n")
var rows []tgbotapi.InlineKeyboardButton
if currentPage > 0 {
rows = append(rows, tgbotapi.NewInlineKeyboardButtonData("Previous", fmt.Sprintf("pager:prev:%d:%d", currentPage, count)))
}
if currentPage < maxPages-1 {
rows = append(rows, tgbotapi.NewInlineKeyboardButtonData("Next", fmt.Sprintf("pager:next:%d:%d", currentPage, count)))
}
markup = tgbotapi.NewInlineKeyboardMarkup(rows)
return
}
Then there is going to be a function to send/edit the calculated data and keyboard by passing chatId, currentPage, count and messageId:
func SendDummyData(chatId int64, currentPage, count int, messageId *int) {
text, keyboard := DummyDataTextMarkup(currentPage, count)
var cfg tgbotapi.Chattable
if messageId == nil {
msg := tgbotapi.NewMessage(chatId, text)
msg.ReplyMarkup = keyboard
cfg = msg
} else {
msg := tgbotapi.NewEditMessageText(chatId, *messageId, text)
msg.ReplyMarkup = &keyboard
cfg = msg
}
bot.Send(cfg)
}
(Note: If we pass a messageId to this function it's going to edit our message with new data)
The next step is to have a CallbackQueryHandler to handle a user's click on inline buttons:
func CallbackQueryHandler(query *tgbotapi.CallbackQuery) {
split := strings.Split(query.Data, ":")
if split[0] == "pager" {
HandleNavigationCallbackQuery(query.Message.MessageID, split[1:]...)
return
}
}
func HandleNavigationCallbackQuery(messageId int, data ...string) {
pagerType := data[0]
currentPage, _ := strconv.Atoi(data[1])
itemsPerPage, _ := strconv.Atoi(data[2])
if pagerType == "next" {
nextPage := currentPage + 1
if nextPage < maxPages {
SendDummyData(chatId, nextPage, itemsPerPage, &messageId)
}
}
if pagerType == "prev" {
previousPage := currentPage - 1
if previousPage >= 0 {
SendDummyData(chatId, previousPage, itemsPerPage, &messageId)
}
}
}
(Note: The first function CallbackQueryHandler is a global callback handler that calls our desired callbackhandler by splitting its query with : and getting the handler's name, here it is pager as we have defined and its handler is called HandleNavigationCallbackQuery).
The last step would be to call CallbackQueryHandler in your update loop as well as sending the initial data to your desired chat:
var chatId = int64(0) // <--- Place Chat Id Here
SendDummyData(chatId, 0, 2, nil) // Send initial data
for update := range updates {
if update.CallbackQuery != nil {
CallbackQueryHandler(update.CallbackQuery)
continue
}
}
You can check the full example on my GitHub's gist here

Golang implementing pagination on map[string]interface{} data

I have a json file (nested json) that I am unmarshalling its content into a map[string]interface. Now I have to implement pagination as the data is large. The client side will send as a query parameter the desired page, how can I slice the data I have?
This is a snippet of the data I am dealing with:
"packages":{
"pkg1": {
"meta": {
"description": "description1",
"name": "pkg1.1"
},
"name": "pkg1.1"
},
"pkg2": {
"meta": {
"description": "description2",
"name": "pkg2.2"
},
"name": "pkg2.2"
},
}
So what I did is that I recursively iterated through the data and created an array of a custom type containing the data I need (name, description) for each entry so that I can use it for pagination. Here is the code I used:
type Object struct {
name string
description string
}
func iterate(aMap map[string]interface{}, result *[]Object){
for key, val := range aMap {
switch val.(type) {
case map[string]interface{}:
if(key == "meta"){
switch reflect.TypeOf(val).Kind() {
case reflect.Map:
s := reflect.ValueOf(val)
var tmpData Object
if(s.MapIndex(reflect.ValueOf("name")).IsValid()){
tmpData.name = s.MapIndex(reflect.ValueOf("name")).Interface().(string)
}
if(s.MapIndex(reflect.ValueOf("description")).IsValid()){
tmpData.description = s.MapIndex(reflect.ValueOf("description")).Interface().(string)
}
*result = append(*result, tmpData)
}
}
iterate(val.(map[string]interface{}), result)
default: //DO NOTHING!!
}
}
}
If you're doing pagination, somewhere the data must be represented as a list instead of an object? I assume at some place in your JSON, you have a list of items, otherwise pagination doesn't make sense.
It shouldn't be very hard, something simple like this should work:
const (
itemsPerPage = 10
)
var data []map[string]interface{}
// pages start at 1, can't be 0 or less.
func GetDataPage(page int) []map[string]interface{} {
start := (page - 1) * itemsPerPage
stop := start + itemsPerPage
if start > len(data) {
return nil
}
if stop > len(data) {
stop = len(data)
}
return data[start:stop]
}
You are unmarshalling your json into a map which has no order by itself. In order to be able to paginate your results you need to order them in some way.
One way of doing it is to sort your data and then store it into an array. But in order to paginate you need to have ordered data and that is not possible with a map.

Two filters with an OR-condition on RethinkDB

I've a struct :
type Talk struct {
Id string `gorethink:"id,omitempty"`
MatchId string
UserIdX string
UserIdY string
UserNameX string
UserNameY string
CreatedAt time.Time
}
My current Talk struct looks like this:
{
"CreatedAt": Wed Sep 14 2016 21:36:26 GMT+02:00 ,
"MatchId": "172d51fa-438b-49a5-bbe5-422377f09336" ,
"UserIdX": "acc4e0b6-d33b-4755-9c0a-ae5309c2ba75" ,
"UserIdY": "03f76d8b-ed6a-4c0f-9cde-27b17c9e7cdb" ,
"UserNameX": "Barbara" ,
"UserNameY": "Louis" ,
"id": "ead3f1b0-b242-4c6d-8027-a59572b58649"
}
How can I retrieve a talk, with a single query, where:
(UserIdX == talk.UserIdX AND UserIdY == talk.UserIdY) OR (UserIdX ==
talk.UserIdY AND UserIdY == talk.UserIdX)
I actually do it like the following:
func (talk *Talk) GetTalkByUsersId() bool {
talk1 := new(Talk)
talk2 := new(Talk)
curs, _ := r.Table("Talks").
Filter(r.Row.Field("UserIdX").Eq(talk.UserIdX)).
Filter(r.Row.Field("UserIdY").Eq(talk.UserIdY)).
Run(api.Sess)
curs2, _ := r.Table("Talks").
Filter(r.Row.Field("UserIdX").Eq(talk.UserIdY)).
Filter(r.Row.Field("UserIdY").Eq(talk.UserIdX)).
Run(api.Sess)
curs.One(&talk1)
curs2.One(&talk2)
if talk1.Id == "" && talk2.Id == "" {
return false
}
if talk1.Id != "" {
talk.copyTalk(talk1)
} else {
talk.copyTalk(talk2)
}
return true
}
How can I get this to work in a much simpler way?
I'm going to summon #daniel-cannon here but I think this is what you're looking for and will vastly simplify this query and reduce it to just a single query. But just two tips:
r.And and r.Or and their two use cases are very much your friends for doing some tricky logical conditions.
https://www.rethinkdb.com/api/javascript/#and
https://www.rethinkdb.com/api/javascript/#or
You can provide anonymous functions to .Filter to do things that would be kind of ugly with the simplified r.Row pattern.
r.Table("talks").Filter(func(talk r.Term) r.Term {
return r.Or(
r.And(talk.Field("UserIdX").Eq(UserIdX), talk.Field("UserIdX").Eq(UserIdY)),
r.And(talk.Field("UserIdY").Eq(UserIdX), talk.Field("UserIdX").Eq(UserIdY)),
)
}).Run(api.Sess)
I hope this helps!

iOS 9 Realmswift trouble with query

I am trying to get a Results<News> of news objects from realm of all objects that have 'mytag'
The News object looks something like
dynamic var id = 0
dynamic var title = ""
dynamic var date = NSDate()
dynamic var modified = NSDate()
dynamic var protected = true
dynamic var category : Category?
dynamic var image : Image?
let content = List<Content>()
let tags = List<Tag>()
I have a Results<Tag> with all my tags. Tag has a boolean my to see if it belongs to my tags.
This way I could get personal news.
However, I don't understand how to query this. I have some knowledge of SQL, but i cant seem to figure it out using contains or in
I tried a workaround but it seems Results does not have an append function.
Here's my current workaround:
func retrieveMyNewsSortedByDate() -> Results<News> {
let myTags = TagDataService().myTagsList() // retunrs a List<Tag>
print("My news items");
let items = database().objects(News).filter("tags IN %#", myTags).sorted("date") // how to query or query with news and tag table
let myTagItems = List<News>()
for tag in myTags {
for news in items{
for newsTag in news.tags {
if newsTag == tag {
myTagItems.append(news) // Results does not have .append or .addobject
}
}
}
}
mytagItems = Results(myTagItems)
return myTagItems
}
However, now I would have a very inefficient way that also outputs list that I can't seem to cast to Results. How do I do this?
well, it was quite easy in the end :D
func retrieveMyNewsSortedByDate() -> Results<News> {
let myTags = TagDataService().myTagsList()
let items = database().objects(News).filter("ANY tags IN %#", myTags).sorted("date")
return items
}

Retrieve ALAsset or PHAsset from file URL

Selecting images in Photos.app to pass to an action extension seems to yield paths to images on disk (e.g.: file:///var/mobile/Media/DCIM/109APPLE/IMG_9417.JPG). Is there a way to get the corresponding ALAsset or PHAsset?
The URL looks like it corresponds to the PHImageFileURLKey entry you get from calling PHImageManager.requestImageDataForAsset. I'd hate to have to iterate through all PHAssets to find it.
I did what I didn't want to do and threw this dumb search approach together. It works, although it's horrible, slow and gives me memory issues when the photo library is large.
As a noob to both Cocoa and Swift I'd appreciate refinement tips. Thanks!
func PHAssetForFileURL(url: NSURL) -> PHAsset? {
var imageRequestOptions = PHImageRequestOptions()
imageRequestOptions.version = .Current
imageRequestOptions.deliveryMode = .FastFormat
imageRequestOptions.resizeMode = .Fast
imageRequestOptions.synchronous = true
let fetchResult = PHAsset.fetchAssetsWithOptions(nil)
for var index = 0; index < fetchResult.count; index++ {
if let asset = fetchResult[index] as? PHAsset {
var found = false
PHImageManager.defaultManager().requestImageDataForAsset(asset,
options: imageRequestOptions) { (_, _, _, info) in
if let urlkey = info["PHImageFileURLKey"] as? NSURL {
if urlkey.absoluteString! == url.absoluteString! {
found = true
}
}
}
if (found) {
return asset
}
}
}
return nil
}
So this is commentary on ("refinement tips") to your auto-answer. SO comments don't cut it for code samples, so here we go.
You can replace your for-index loop with a simpler for-each loop. E.g. something like:
for asset in PHAsset.fetchAssetsWithOptions(nil)
As of the last time I checked, the key in info["PHImageFileURLKey"] is undocumented. Be apprised. I don't think it will get you rejected, but the behavior could change at any time.

Resources