diff --git a/search/document_store.go b/search/document_store.go index bd5e4e32606b2f835d222c4f0ca5f08a88ace940..bec67551c3ff54122193b621b13254bcae621d23 100644 --- a/search/document_store.go +++ b/search/document_store.go @@ -90,7 +90,7 @@ func (w *writer) DocumentStore(name string, tmpl interface{}) (DocumentStore, er if err != nil { return nil, errors.Wrapf(err, "failed to marshal index mappings") } - logger.Debugf("%s Index Mappings: %s", structType, string(ds.jsonMappings)) + logger.Infof("%s Index Mappings: %s", structType, string(ds.jsonMappings)) //define search response type //similar to SearchResponseBody diff --git a/search/document_store_test.go b/search/document_store_test.go index 788d43af4788469828e1854b4e1e65f6e0b7b6bc..2c168d1e37f20c761709d9d74730083135308ef4 100644 --- a/search/document_store_test.go +++ b/search/document_store_test.go @@ -1,8 +1,11 @@ package search_test import ( + "fmt" "math/rand" + "strings" "testing" + "time" "github.com/google/uuid" "gitlab.com/uafrica/go-utils/logger" @@ -32,7 +35,7 @@ func testDocuments(t *testing.T, c search.Config) { } indexName := "go-utils-search-docs-test" - ds, err := a.DocumentStore(indexName, testDocument{}) + ds, err := a.DocumentStore(indexName, SearchOrder{}) if err != nil { t.Fatalf("failed to create document store: %+v", err) } @@ -64,10 +67,32 @@ func testDocuments(t *testing.T, c search.Config) { for i := 0; i < N; i++ { //make a random document id := uuid.New().String() - doc := testDocument{ - Buyer: buyers[rand.Intn(len(buyers))], - Seller: sellers[rand.Intn(len(sellers))], - Items: []docItem{}, + buyer := buyers[rand.Intn(len(buyers))] + seller := sellers[rand.Intn(len(sellers))] + doc := SearchOrder{ + ID: int64(i + 1), + AccountID: int64(i + 2), + AccountOrderNumber: int64(i * 2), + ChannelID: int64(i), + ChannelOrderNumber: fmt.Sprintf("CHO-%d", i), + ChannelOrderReference: fmt.Sprintf("REF-%05d", i), + CustomerName: buyer, + CustomerEmail: strings.ToLower(buyer + "@home.net"), + CustomerPhone: "123456789", + DeliveryAddress: "My Street", + Currency: "ZAR", + Items: []SearchOrderItem{}, + TotalPrice: 0, + TotalWeightKg: 0, + TotalQty: 0, + TotalFulfilledQty: 0, + Status: OrderStatusNew, + PaymentStatus: OrderPaymentStatusUnpaid, + TimeCreated: time.Now(), + TimeModified: nil, + Tags: "1(blue),2(red)", + BuyerSelectedShippingCost: 1.23, + BuyerSelectedShippingProvider: seller, } for i := 0; i < rand.Intn(5)+1; i++ { itemInfo := itemInfos[nextItem] @@ -75,14 +100,15 @@ func testDocuments(t *testing.T, c search.Config) { if nextItem >= len(itemInfos) { nextItem = 0 } - item := docItem{ + item := SearchOrderItem{ + SKU: fmt.Sprintf("SKU-%s-%03d", itemInfo.Name[:3], i), Description: itemInfo.Name, - UnitCost: itemInfo.Cost, + UnitPrice: itemInfo.Cost, Qty: rand.Intn(3) + 1, } doc.Items = append(doc.Items, item) - doc.TotalCost += item.UnitCost * float64(item.Qty) - doc.TotalItems += item.Qty + doc.TotalPrice += item.UnitPrice * float64(item.Qty) + doc.TotalQty += item.Qty } //add to the search @@ -117,22 +143,81 @@ func testDocuments(t *testing.T, c search.Config) { t.Logf("Done") } -type testDocument struct { - //UUID string `json:"uuid"` - Buyer string `json:"buyer" search:"keyword"` - Seller string `json:"seller" search:"keyword"` - Items []docItem `json:"items"` - TotalCost float64 `json:"total_cost"` - TotalItems int `json:"total_items"` -} +// type testDocument struct { +// //UUID string `json:"uuid"` +// Buyer string `json:"buyer" search:"keyword"` +// Seller string `json:"seller" search:"keyword"` +// Items []docItem `json:"items"` +// TotalCost float64 `json:"total_cost"` +// TotalItems int `json:"total_items"` +// } -type docItem struct { - Description string `json:"description" search:"keyword"` - UnitCost float64 `json:"unit_cost"` - Qty int `json:"qty"` -} +// type docItem struct { +// Description string `json:"description" search:"keyword"` +// UnitCost float64 `json:"unit_cost"` +// Qty int `json:"qty"` +// } type testItemInfo struct { Name string Cost float64 } + +type SearchOrder struct { + ID int64 `json:"id"` + AccountID int64 `json:"account_id" search:"keyword"` + AccountOrderNumber int64 `json:"account_order_number"` + ChannelID int64 `json:"channel,omitempty" search:"keyword"` + ChannelOrderNumber string `json:"channel_order_number,omitempty"` + ChannelOrderReference string `json:"channel_order_reference,omitempty"` + CustomerName string `json:"customer_name,omitempty"` + CustomerEmail string `json:"customer_email,omitempty"` + CustomerPhone string `json:"customer_phone,omitempty"` + DeliveryAddress string `json:"delivery_address,omitempty"` + Currency string `json:"currency" search:"keyword"` + Items []SearchOrderItem `json:"items,omitempty"` + TotalPrice float64 `json:"total_price"` + TotalWeightKg float64 `json:"total_weight_kg"` + TotalQty int `json:"total_qty"` + TotalFulfilledQty int `json:"total_fulfilled_qty"` + Status OrderStatus `json:"status" search:"keyword"` + PaymentStatus OrderPaymentStatus `json:"payment_status" search:"keyword"` + TimeCreated time.Time `json:"time_created"` + TimeModified *time.Time `json:"time_modified,omitempty"` + Tags string `json:"tags" search:"keyword"` //CSV or tags sorted, so [A,B] and [B,A] both -> "A,B" keyword + BuyerSelectedShippingCost float64 `json:"buyer_selected_shipping_cost,omitempty"` + BuyerSelectedShippingProvider string `json:"buyer_selected_shipping_provider,omitempty"` +} + +type SearchOrderItem struct { + SKU string `json:"sku" search:"keyword"` + Description string `json:"description" search:"keyword"` + Vendor string `json:"vendor" search:"keyword"` + UnitPrice float64 `json:"unit_price"` + UnitWeightKg float64 `json:"unit_weight_kg"` + Qty int `json:"qty"` + FulfilledQty int `json:"fulfilled_qty"` + TotalPrice float64 `json:"total_price"` + TotalWeightKg float64 `json:"total_weight_kg"` +} + +type OrderStatus string + +const ( + OrderStatusNew OrderStatus = "new" + OrderStatusCompleted OrderStatus = "completed" + OrderStatusCancelled OrderStatus = "cancelled" +) + +type OrderPaymentStatus string + +const ( + OrderPaymentStatusUnpaid OrderPaymentStatus = "unpaid" + OrderPaymentStatusPending OrderPaymentStatus = "pending" + OrderPaymentStatusPartiallyPaid OrderPaymentStatus = "partially-paid" + OrderPaymentStatusPaid OrderPaymentStatus = "paid" + OrderPaymentStatusPartiallyRefunded OrderPaymentStatus = "partially-refunded" + OrderPaymentStatusRefunded OrderPaymentStatus = "refunded" + OrderPaymentStatusVoided OrderPaymentStatus = "voided" + OrderPaymentStatusAuthorised OrderPaymentStatus = "authorised" +) diff --git a/search/time_series.go b/search/time_series.go index 294dab773b3c658be5350d7f647da303c38385b8..4420e3f13fe04db4a2ad64f5a21b8d309d2c9aa2 100644 --- a/search/time_series.go +++ b/search/time_series.go @@ -152,10 +152,23 @@ func structMappingProperties(structType reflect.Type) (map[string]MappingPropert fieldMapping = MappingProperty{Type: "long"} case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: fieldMapping = MappingProperty{Type: "long"} + case reflect.Float32, reflect.Float64: + fieldMapping = MappingProperty{Type: "float"} case reflect.Bool: fieldMapping = MappingProperty{Type: "boolean"} case reflect.String: fieldMapping = MappingProperty{Type: "text"} + + case reflect.Slice: + //do not indicate slice, just map slice items as sub-items + subStructProperties, err := structMappingProperties(structField.Type.Elem()) + if err != nil { + return nil, errors.Wrapf(err, "failed to map %s.%s", structType, structField.Name) + } + fieldMapping = MappingProperty{ + Properties: subStructProperties, + } + default: if structField.Type == reflect.TypeOf(time.Now()) { fieldMapping = MappingProperty{Type: "date"}