diff --git a/changelog/unreleased/enhancement-store-and-index-location-metadata.md b/changelog/unreleased/enhancement-store-and-index-location-metadata.md new file mode 100644 index 00000000000..ffc804b55d4 --- /dev/null +++ b/changelog/unreleased/enhancement-store-and-index-location-metadata.md @@ -0,0 +1,6 @@ +Enhancement: Store and index metadata + +location metadata is now extracted and stored by the search service. +It is available for driveItems in a folder listing using the Graph API. + +https://github.com/owncloud/ocis/pull/7886 diff --git a/protogen/gen/ocis/messages/search/v0/search.pb.go b/protogen/gen/ocis/messages/search/v0/search.pb.go index 267fcf0e65f..0349d3e1772 100644 --- a/protogen/gen/ocis/messages/search/v0/search.pb.go +++ b/protogen/gen/ocis/messages/search/v0/search.pb.go @@ -306,6 +306,69 @@ func (x *Audio) GetYear() int32 { return 0 } +type GeoCoordinates struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Altitude *float64 `protobuf:"fixed64,1,opt,name=altitude,proto3,oneof" json:"altitude,omitempty"` + Latitude *float64 `protobuf:"fixed64,2,opt,name=latitude,proto3,oneof" json:"latitude,omitempty"` + Longitude *float64 `protobuf:"fixed64,3,opt,name=longitude,proto3,oneof" json:"longitude,omitempty"` +} + +func (x *GeoCoordinates) Reset() { + *x = GeoCoordinates{} + if protoimpl.UnsafeEnabled { + mi := &file_ocis_messages_search_v0_search_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GeoCoordinates) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GeoCoordinates) ProtoMessage() {} + +func (x *GeoCoordinates) ProtoReflect() protoreflect.Message { + mi := &file_ocis_messages_search_v0_search_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GeoCoordinates.ProtoReflect.Descriptor instead. +func (*GeoCoordinates) Descriptor() ([]byte, []int) { + return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{3} +} + +func (x *GeoCoordinates) GetAltitude() float64 { + if x != nil && x.Altitude != nil { + return *x.Altitude + } + return 0 +} + +func (x *GeoCoordinates) GetLatitude() float64 { + if x != nil && x.Latitude != nil { + return *x.Latitude + } + return 0 +} + +func (x *GeoCoordinates) GetLongitude() float64 { + if x != nil && x.Longitude != nil { + return *x.Longitude + } + return 0 +} + type Entity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -326,12 +389,13 @@ type Entity struct { Tags []string `protobuf:"bytes,13,rep,name=tags,proto3" json:"tags,omitempty"` Highlights string `protobuf:"bytes,14,opt,name=highlights,proto3" json:"highlights,omitempty"` Audio *Audio `protobuf:"bytes,15,opt,name=audio,proto3" json:"audio,omitempty"` + Location *GeoCoordinates `protobuf:"bytes,16,opt,name=location,proto3" json:"location,omitempty"` } func (x *Entity) Reset() { *x = Entity{} if protoimpl.UnsafeEnabled { - mi := &file_ocis_messages_search_v0_search_proto_msgTypes[3] + mi := &file_ocis_messages_search_v0_search_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -344,7 +408,7 @@ func (x *Entity) String() string { func (*Entity) ProtoMessage() {} func (x *Entity) ProtoReflect() protoreflect.Message { - mi := &file_ocis_messages_search_v0_search_proto_msgTypes[3] + mi := &file_ocis_messages_search_v0_search_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -357,7 +421,7 @@ func (x *Entity) ProtoReflect() protoreflect.Message { // Deprecated: Use Entity.ProtoReflect.Descriptor instead. func (*Entity) Descriptor() ([]byte, []int) { - return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{3} + return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{4} } func (x *Entity) GetRef() *Reference { @@ -465,6 +529,13 @@ func (x *Entity) GetAudio() *Audio { return nil } +func (x *Entity) GetLocation() *GeoCoordinates { + if x != nil { + return x.Location + } + return nil +} + type Match struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -479,7 +550,7 @@ type Match struct { func (x *Match) Reset() { *x = Match{} if protoimpl.UnsafeEnabled { - mi := &file_ocis_messages_search_v0_search_proto_msgTypes[4] + mi := &file_ocis_messages_search_v0_search_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -492,7 +563,7 @@ func (x *Match) String() string { func (*Match) ProtoMessage() {} func (x *Match) ProtoReflect() protoreflect.Message { - mi := &file_ocis_messages_search_v0_search_proto_msgTypes[4] + mi := &file_ocis_messages_search_v0_search_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -505,7 +576,7 @@ func (x *Match) ProtoReflect() protoreflect.Message { // Deprecated: Use Match.ProtoReflect.Descriptor instead. func (*Match) Descriptor() ([]byte, []int) { - return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{4} + return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{5} } func (x *Match) GetEntity() *Entity { @@ -588,7 +659,17 @@ var file_ocis_messages_search_v0_search_proto_rawDesc = []byte{ 0x5f, 0x69, 0x73, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x22, 0xb8, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x22, 0x9d, + 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, + 0x73, 0x12, 0x1f, 0x0a, 0x08, 0x61, 0x6c, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x08, 0x61, 0x6c, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x88, + 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x01, 0x48, 0x01, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x48, 0x02, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, + 0x75, 0x64, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x61, 0x6c, 0x74, 0x69, 0x74, + 0x75, 0x64, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, + 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x22, 0xfd, 0x04, 0x0a, 0x06, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, @@ -624,17 +705,22 @@ var file_ocis_messages_search_v0_search_proto_rawDesc = []byte{ 0x68, 0x74, 0x73, 0x12, 0x34, 0x0a, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x41, 0x75, 0x64, - 0x69, 0x6f, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x22, 0x56, 0x0a, 0x05, 0x4d, 0x61, 0x74, - 0x63, 0x68, 0x12, 0x37, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x45, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, - 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, - 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x76, 0x32, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, - 0x69, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, - 0x63, 0x68, 0x2f, 0x76, 0x30, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x6f, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x12, 0x43, 0x0a, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x63, + 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x74, 0x65, 0x73, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x56, + 0x0a, 0x05, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x37, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, + 0x30, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, + 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, + 0x69, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, + 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x76, 0x30, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -649,28 +735,30 @@ func file_ocis_messages_search_v0_search_proto_rawDescGZIP() []byte { return file_ocis_messages_search_v0_search_proto_rawDescData } -var file_ocis_messages_search_v0_search_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_ocis_messages_search_v0_search_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_ocis_messages_search_v0_search_proto_goTypes = []interface{}{ (*ResourceID)(nil), // 0: ocis.messages.search.v0.ResourceID (*Reference)(nil), // 1: ocis.messages.search.v0.Reference (*Audio)(nil), // 2: ocis.messages.search.v0.Audio - (*Entity)(nil), // 3: ocis.messages.search.v0.Entity - (*Match)(nil), // 4: ocis.messages.search.v0.Match - (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp + (*GeoCoordinates)(nil), // 3: ocis.messages.search.v0.GeoCoordinates + (*Entity)(nil), // 4: ocis.messages.search.v0.Entity + (*Match)(nil), // 5: ocis.messages.search.v0.Match + (*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp } var file_ocis_messages_search_v0_search_proto_depIdxs = []int32{ 0, // 0: ocis.messages.search.v0.Reference.resource_id:type_name -> ocis.messages.search.v0.ResourceID 1, // 1: ocis.messages.search.v0.Entity.ref:type_name -> ocis.messages.search.v0.Reference 0, // 2: ocis.messages.search.v0.Entity.id:type_name -> ocis.messages.search.v0.ResourceID - 5, // 3: ocis.messages.search.v0.Entity.last_modified_time:type_name -> google.protobuf.Timestamp + 6, // 3: ocis.messages.search.v0.Entity.last_modified_time:type_name -> google.protobuf.Timestamp 0, // 4: ocis.messages.search.v0.Entity.parent_id:type_name -> ocis.messages.search.v0.ResourceID 2, // 5: ocis.messages.search.v0.Entity.audio:type_name -> ocis.messages.search.v0.Audio - 3, // 6: ocis.messages.search.v0.Match.entity:type_name -> ocis.messages.search.v0.Entity - 7, // [7:7] is the sub-list for method output_type - 7, // [7:7] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 3, // 6: ocis.messages.search.v0.Entity.location:type_name -> ocis.messages.search.v0.GeoCoordinates + 4, // 7: ocis.messages.search.v0.Match.entity:type_name -> ocis.messages.search.v0.Entity + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_ocis_messages_search_v0_search_proto_init() } @@ -716,7 +804,7 @@ func file_ocis_messages_search_v0_search_proto_init() { } } file_ocis_messages_search_v0_search_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Entity); i { + switch v := v.(*GeoCoordinates); i { case 0: return &v.state case 1: @@ -728,6 +816,18 @@ func file_ocis_messages_search_v0_search_proto_init() { } } file_ocis_messages_search_v0_search_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Entity); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ocis_messages_search_v0_search_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Match); i { case 0: return &v.state @@ -741,13 +841,14 @@ func file_ocis_messages_search_v0_search_proto_init() { } } file_ocis_messages_search_v0_search_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_ocis_messages_search_v0_search_proto_msgTypes[3].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ocis_messages_search_v0_search_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/protogen/gen/ocis/messages/search/v0/search.pb.web.go b/protogen/gen/ocis/messages/search/v0/search.pb.web.go index 1ed7a0bf377..cbcd502c50f 100644 --- a/protogen/gen/ocis/messages/search/v0/search.pb.web.go +++ b/protogen/gen/ocis/messages/search/v0/search.pb.web.go @@ -118,6 +118,42 @@ func (m *Audio) UnmarshalJSON(b []byte) error { var _ json.Unmarshaler = (*Audio)(nil) +// GeoCoordinatesJSONMarshaler describes the default jsonpb.Marshaler used by all +// instances of GeoCoordinates. This struct is safe to replace or modify but +// should not be done so concurrently. +var GeoCoordinatesJSONMarshaler = new(jsonpb.Marshaler) + +// MarshalJSON satisfies the encoding/json Marshaler interface. This method +// uses the more correct jsonpb package to correctly marshal the message. +func (m *GeoCoordinates) MarshalJSON() ([]byte, error) { + if m == nil { + return json.Marshal(nil) + } + + buf := &bytes.Buffer{} + + if err := GeoCoordinatesJSONMarshaler.Marshal(buf, m); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +var _ json.Marshaler = (*GeoCoordinates)(nil) + +// GeoCoordinatesJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all +// instances of GeoCoordinates. This struct is safe to replace or modify but +// should not be done so concurrently. +var GeoCoordinatesJSONUnmarshaler = new(jsonpb.Unmarshaler) + +// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method +// uses the more correct jsonpb package to correctly unmarshal the message. +func (m *GeoCoordinates) UnmarshalJSON(b []byte) error { + return GeoCoordinatesJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m) +} + +var _ json.Unmarshaler = (*GeoCoordinates)(nil) + // EntityJSONMarshaler describes the default jsonpb.Marshaler used by all // instances of Entity. This struct is safe to replace or modify but // should not be done so concurrently. diff --git a/protogen/gen/ocis/messages/settings/v0/settings.pb.go b/protogen/gen/ocis/messages/settings/v0/settings.pb.go index 9f0f3d92c64..bae3a9c8710 100644 --- a/protogen/gen/ocis/messages/settings/v0/settings.pb.go +++ b/protogen/gen/ocis/messages/settings/v0/settings.pb.go @@ -489,13 +489,13 @@ type Bundle struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: yaml:"id" - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // @gotags: yaml:"name" - Type Bundle_Type `protobuf:"varint,3,opt,name=type,proto3,enum=ocis.messages.settings.v0.Bundle_Type" json:"type,omitempty"` // @gotags: yaml:"type" - Extension string `protobuf:"bytes,4,opt,name=extension,proto3" json:"extension,omitempty"` // @gotags: yaml:"extension" - DisplayName string `protobuf:"bytes,5,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // @gotags: yaml:"display_name" - Settings []*Setting `protobuf:"bytes,6,rep,name=settings,proto3" json:"settings,omitempty"` // @gotags: yaml:"settings" - Resource *Resource `protobuf:"bytes,7,opt,name=resource,proto3" json:"resource,omitempty"` // @gotags: yaml:"resource" + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` // @gotags: yaml:"id" + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty" yaml:"name"` // @gotags: yaml:"name" + Type Bundle_Type `protobuf:"varint,3,opt,name=type,proto3,enum=ocis.messages.settings.v0.Bundle_Type" json:"type,omitempty" yaml:"type"` // @gotags: yaml:"type" + Extension string `protobuf:"bytes,4,opt,name=extension,proto3" json:"extension,omitempty" yaml:"extension"` // @gotags: yaml:"extension" + DisplayName string `protobuf:"bytes,5,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty" yaml:"display_name"` // @gotags: yaml:"display_name" + Settings []*Setting `protobuf:"bytes,6,rep,name=settings,proto3" json:"settings,omitempty" yaml:"settings"` // @gotags: yaml:"settings" + Resource *Resource `protobuf:"bytes,7,opt,name=resource,proto3" json:"resource,omitempty" yaml:"resource"` // @gotags: yaml:"resource" } func (x *Bundle) Reset() { @@ -584,10 +584,10 @@ type Setting struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: yaml:"id" - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // @gotags: yaml:"name" - DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // @gotags: yaml:"display_name" - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // @gotags: yaml:"description" + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` // @gotags: yaml:"id" + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty" yaml:"name"` // @gotags: yaml:"name" + DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty" yaml:"display_name"` // @gotags: yaml:"display_name" + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty" yaml:"description"` // @gotags: yaml:"description" // Types that are assignable to Value: // // *Setting_IntValue @@ -597,7 +597,7 @@ type Setting struct { // *Setting_MultiChoiceValue // *Setting_PermissionValue Value isSetting_Value `protobuf_oneof:"value"` - Resource *Resource `protobuf:"bytes,11,opt,name=resource,proto3" json:"resource,omitempty"` // @gotags: yaml:"resource" + Resource *Resource `protobuf:"bytes,11,opt,name=resource,proto3" json:"resource,omitempty" yaml:"resource"` // @gotags: yaml:"resource" } func (x *Setting) Reset() { @@ -721,27 +721,27 @@ type isSetting_Value interface { } type Setting_IntValue struct { - IntValue *Int `protobuf:"bytes,5,opt,name=int_value,json=intValue,proto3,oneof"` // @gotags: yaml:"int_value" + IntValue *Int `protobuf:"bytes,5,opt,name=int_value,json=intValue,proto3,oneof" yaml:"int_value"` // @gotags: yaml:"int_value" } type Setting_StringValue struct { - StringValue *String `protobuf:"bytes,6,opt,name=string_value,json=stringValue,proto3,oneof"` // @gotags: yaml:"string_value" + StringValue *String `protobuf:"bytes,6,opt,name=string_value,json=stringValue,proto3,oneof" yaml:"string_value"` // @gotags: yaml:"string_value" } type Setting_BoolValue struct { - BoolValue *Bool `protobuf:"bytes,7,opt,name=bool_value,json=boolValue,proto3,oneof"` // @gotags: yaml:"bool_value" + BoolValue *Bool `protobuf:"bytes,7,opt,name=bool_value,json=boolValue,proto3,oneof" yaml:"bool_value"` // @gotags: yaml:"bool_value" } type Setting_SingleChoiceValue struct { - SingleChoiceValue *SingleChoiceList `protobuf:"bytes,8,opt,name=single_choice_value,json=singleChoiceValue,proto3,oneof"` // @gotags: yaml:"single_choice_value" + SingleChoiceValue *SingleChoiceList `protobuf:"bytes,8,opt,name=single_choice_value,json=singleChoiceValue,proto3,oneof" yaml:"single_choice_value"` // @gotags: yaml:"single_choice_value" } type Setting_MultiChoiceValue struct { - MultiChoiceValue *MultiChoiceList `protobuf:"bytes,9,opt,name=multi_choice_value,json=multiChoiceValue,proto3,oneof"` // @gotags: yaml:"multi_choice_value" + MultiChoiceValue *MultiChoiceList `protobuf:"bytes,9,opt,name=multi_choice_value,json=multiChoiceValue,proto3,oneof" yaml:"multi_choice_value"` // @gotags: yaml:"multi_choice_value" } type Setting_PermissionValue struct { - PermissionValue *Permission `protobuf:"bytes,10,opt,name=permission_value,json=permissionValue,proto3,oneof"` // @gotags: yaml:"permission_value" + PermissionValue *Permission `protobuf:"bytes,10,opt,name=permission_value,json=permissionValue,proto3,oneof" yaml:"permission_value"` // @gotags: yaml:"permission_value" } func (*Setting_IntValue) isSetting_Value() {} @@ -761,11 +761,11 @@ type Int struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Default int64 `protobuf:"varint,1,opt,name=default,proto3" json:"default,omitempty"` // @gotags: yaml:"default" - Min int64 `protobuf:"varint,2,opt,name=min,proto3" json:"min,omitempty"` // @gotags: yaml:"min" - Max int64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty"` // @gotags: yaml:"max" - Step int64 `protobuf:"varint,4,opt,name=step,proto3" json:"step,omitempty"` // @gotags: yaml:"step" - Placeholder string `protobuf:"bytes,5,opt,name=placeholder,proto3" json:"placeholder,omitempty"` // @gotags: yaml:"placeholder" + Default int64 `protobuf:"varint,1,opt,name=default,proto3" json:"default,omitempty" yaml:"default"` // @gotags: yaml:"default" + Min int64 `protobuf:"varint,2,opt,name=min,proto3" json:"min,omitempty" yaml:"min"` // @gotags: yaml:"min" + Max int64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty" yaml:"max"` // @gotags: yaml:"max" + Step int64 `protobuf:"varint,4,opt,name=step,proto3" json:"step,omitempty" yaml:"step"` // @gotags: yaml:"step" + Placeholder string `protobuf:"bytes,5,opt,name=placeholder,proto3" json:"placeholder,omitempty" yaml:"placeholder"` // @gotags: yaml:"placeholder" } func (x *Int) Reset() { @@ -840,11 +840,11 @@ type String struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Default string `protobuf:"bytes,1,opt,name=default,proto3" json:"default,omitempty"` // @gotags: yaml:"default" - Required bool `protobuf:"varint,2,opt,name=required,proto3" json:"required,omitempty"` // @gotags: yaml:"required" - MinLength int32 `protobuf:"varint,3,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` // @gotags: yaml:"min_length" - MaxLength int32 `protobuf:"varint,4,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` // @gotags: yaml:"max_length" - Placeholder string `protobuf:"bytes,5,opt,name=placeholder,proto3" json:"placeholder,omitempty"` // @gotags: yaml:"placeholder" + Default string `protobuf:"bytes,1,opt,name=default,proto3" json:"default,omitempty" yaml:"default"` // @gotags: yaml:"default" + Required bool `protobuf:"varint,2,opt,name=required,proto3" json:"required,omitempty" yaml:"required"` // @gotags: yaml:"required" + MinLength int32 `protobuf:"varint,3,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty" yaml:"min_length"` // @gotags: yaml:"min_length" + MaxLength int32 `protobuf:"varint,4,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty" yaml:"max_length"` // @gotags: yaml:"max_length" + Placeholder string `protobuf:"bytes,5,opt,name=placeholder,proto3" json:"placeholder,omitempty" yaml:"placeholder"` // @gotags: yaml:"placeholder" } func (x *String) Reset() { @@ -919,8 +919,8 @@ type Bool struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Default bool `protobuf:"varint,1,opt,name=default,proto3" json:"default,omitempty"` // @gotags: yaml:"default" - Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"` // @gotags: yaml:"label" + Default bool `protobuf:"varint,1,opt,name=default,proto3" json:"default,omitempty" yaml:"default"` // @gotags: yaml:"default" + Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty" yaml:"label"` // @gotags: yaml:"label" } func (x *Bool) Reset() { @@ -974,7 +974,7 @@ type SingleChoiceList struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Options []*ListOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` // @gotags: yaml:"options" + Options []*ListOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty" yaml:"options"` // @gotags: yaml:"options" } func (x *SingleChoiceList) Reset() { @@ -1021,7 +1021,7 @@ type MultiChoiceList struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Options []*ListOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` // @gotags: yaml:"options" + Options []*ListOption `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty" yaml:"options"` // @gotags: yaml:"options" } func (x *MultiChoiceList) Reset() { @@ -1068,9 +1068,9 @@ type ListOption struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value *ListOptionValue `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` // @gotags: yaml:"value" - Default bool `protobuf:"varint,2,opt,name=default,proto3" json:"default,omitempty"` // @gotags: yaml:"default" - DisplayValue string `protobuf:"bytes,3,opt,name=display_value,json=displayValue,proto3" json:"display_value,omitempty"` // @gotags: yaml:"display_value" + Value *ListOptionValue `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty" yaml:"value"` // @gotags: yaml:"value" + Default bool `protobuf:"varint,2,opt,name=default,proto3" json:"default,omitempty" yaml:"default"` // @gotags: yaml:"default" + DisplayValue string `protobuf:"bytes,3,opt,name=display_value,json=displayValue,proto3" json:"display_value,omitempty" yaml:"display_value"` // @gotags: yaml:"display_value" } func (x *ListOption) Reset() { @@ -1131,8 +1131,8 @@ type Permission struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Operation Permission_Operation `protobuf:"varint,1,opt,name=operation,proto3,enum=ocis.messages.settings.v0.Permission_Operation" json:"operation,omitempty"` // @gotags: yaml:"operation" - Constraint Permission_Constraint `protobuf:"varint,2,opt,name=constraint,proto3,enum=ocis.messages.settings.v0.Permission_Constraint" json:"constraint,omitempty"` // @gotags: yaml:"constraint" + Operation Permission_Operation `protobuf:"varint,1,opt,name=operation,proto3,enum=ocis.messages.settings.v0.Permission_Operation" json:"operation,omitempty" yaml:"operation"` // @gotags: yaml:"operation" + Constraint Permission_Constraint `protobuf:"varint,2,opt,name=constraint,proto3,enum=ocis.messages.settings.v0.Permission_Constraint" json:"constraint,omitempty" yaml:"constraint"` // @gotags: yaml:"constraint" } func (x *Permission) Reset() { @@ -1187,12 +1187,12 @@ type Value struct { unknownFields protoimpl.UnknownFields // id is the id of the Value. It is generated on saving it. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: yaml:"id" - BundleId string `protobuf:"bytes,2,opt,name=bundle_id,json=bundleId,proto3" json:"bundle_id,omitempty"` // @gotags: yaml:"bundle_id" + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` // @gotags: yaml:"id" + BundleId string `protobuf:"bytes,2,opt,name=bundle_id,json=bundleId,proto3" json:"bundle_id,omitempty" yaml:"bundle_id"` // @gotags: yaml:"bundle_id" // setting_id is the id of the setting from within its bundle. - SettingId string `protobuf:"bytes,3,opt,name=setting_id,json=settingId,proto3" json:"setting_id,omitempty"` // @gotags: yaml:"setting_id" - AccountUuid string `protobuf:"bytes,4,opt,name=account_uuid,json=accountUuid,proto3" json:"account_uuid,omitempty"` // @gotags: yaml:"account_uuid" - Resource *Resource `protobuf:"bytes,5,opt,name=resource,proto3" json:"resource,omitempty"` // @gotags: yaml:"resource" + SettingId string `protobuf:"bytes,3,opt,name=setting_id,json=settingId,proto3" json:"setting_id,omitempty" yaml:"setting_id"` // @gotags: yaml:"setting_id" + AccountUuid string `protobuf:"bytes,4,opt,name=account_uuid,json=accountUuid,proto3" json:"account_uuid,omitempty" yaml:"account_uuid"` // @gotags: yaml:"account_uuid" + Resource *Resource `protobuf:"bytes,5,opt,name=resource,proto3" json:"resource,omitempty" yaml:"resource"` // @gotags: yaml:"resource" // Types that are assignable to Value: // // *Value_BoolValue @@ -1309,19 +1309,19 @@ type isValue_Value interface { } type Value_BoolValue struct { - BoolValue bool `protobuf:"varint,6,opt,name=bool_value,json=boolValue,proto3,oneof"` // @gotags: yaml:"bool_value" + BoolValue bool `protobuf:"varint,6,opt,name=bool_value,json=boolValue,proto3,oneof" yaml:"bool_value"` // @gotags: yaml:"bool_value" } type Value_IntValue struct { - IntValue int64 `protobuf:"varint,7,opt,name=int_value,json=intValue,proto3,oneof"` // @gotags: yaml:"int_value" + IntValue int64 `protobuf:"varint,7,opt,name=int_value,json=intValue,proto3,oneof" yaml:"int_value"` // @gotags: yaml:"int_value" } type Value_StringValue struct { - StringValue string `protobuf:"bytes,8,opt,name=string_value,json=stringValue,proto3,oneof"` // @gotags: yaml:"string_value" + StringValue string `protobuf:"bytes,8,opt,name=string_value,json=stringValue,proto3,oneof" yaml:"string_value"` // @gotags: yaml:"string_value" } type Value_ListValue struct { - ListValue *ListValue `protobuf:"bytes,9,opt,name=list_value,json=listValue,proto3,oneof"` // @gotags: yaml:"list_value" + ListValue *ListValue `protobuf:"bytes,9,opt,name=list_value,json=listValue,proto3,oneof" yaml:"list_value"` // @gotags: yaml:"list_value" } func (*Value_BoolValue) isValue_Value() {} @@ -1337,7 +1337,7 @@ type ListValue struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Values []*ListOptionValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` // @gotags: yaml:"values" + Values []*ListOptionValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" yaml:"values"` // @gotags: yaml:"values" } func (x *ListValue) Reset() { @@ -1449,11 +1449,11 @@ type isListOptionValue_Option interface { } type ListOptionValue_StringValue struct { - StringValue string `protobuf:"bytes,1,opt,name=string_value,json=stringValue,proto3,oneof"` // @gotags: yaml:"string_value" + StringValue string `protobuf:"bytes,1,opt,name=string_value,json=stringValue,proto3,oneof" yaml:"string_value"` // @gotags: yaml:"string_value" } type ListOptionValue_IntValue struct { - IntValue int64 `protobuf:"varint,2,opt,name=int_value,json=intValue,proto3,oneof"` // @gotags: yaml:"int_value" + IntValue int64 `protobuf:"varint,2,opt,name=int_value,json=intValue,proto3,oneof" yaml:"int_value"` // @gotags: yaml:"int_value" } func (*ListOptionValue_StringValue) isListOptionValue_Option() {} diff --git a/protogen/gen/ocis/services/search/v0/search.swagger.json b/protogen/gen/ocis/services/search/v0/search.swagger.json index 6fce96faa1f..d84ac28f330 100644 --- a/protogen/gen/ocis/services/search/v0/search.swagger.json +++ b/protogen/gen/ocis/services/search/v0/search.swagger.json @@ -270,6 +270,26 @@ }, "audio": { "$ref": "#/definitions/v0Audio" + }, + "location": { + "$ref": "#/definitions/v0GeoCoordinates" + } + } + }, + "v0GeoCoordinates": { + "type": "object", + "properties": { + "altitude": { + "type": "number", + "format": "double" + }, + "latitude": { + "type": "number", + "format": "double" + }, + "longitude": { + "type": "number", + "format": "double" } } }, diff --git a/protogen/gen/ocis/services/thumbnails/v0/thumbnails.pb.go b/protogen/gen/ocis/services/thumbnails/v0/thumbnails.pb.go index 1b0aa92c6d3..5d03a85fad6 100644 --- a/protogen/gen/ocis/services/thumbnails/v0/thumbnails.pb.go +++ b/protogen/gen/ocis/services/thumbnails/v0/thumbnails.pb.go @@ -36,7 +36,7 @@ type GetThumbnailRequest struct { Width int32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` // The height of the thumbnail Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` - // Indicates which processor should be used + // Indicates which image processor to use Processor string `protobuf:"bytes,5,opt,name=processor,proto3" json:"processor,omitempty"` // Types that are assignable to Source: // diff --git a/protogen/proto/ocis/messages/search/v0/search.proto b/protogen/proto/ocis/messages/search/v0/search.proto index 034d7f85e4b..84b2b9c2504 100644 --- a/protogen/proto/ocis/messages/search/v0/search.proto +++ b/protogen/proto/ocis/messages/search/v0/search.proto @@ -36,6 +36,12 @@ message Audio { optional int32 year = 16; } +message GeoCoordinates { + optional double altitude = 1; + optional double latitude = 2; + optional double longitude = 3; +} + message Entity { Reference ref = 1; ResourceID id = 2; @@ -52,6 +58,7 @@ message Entity { repeated string tags = 13; string highlights = 14; Audio audio = 15; + GeoCoordinates location = 16; } message Match { diff --git a/services/graph/pkg/service/v0/driveitems.go b/services/graph/pkg/service/v0/driveitems.go index 78243d9802a..38e960204ea 100644 --- a/services/graph/pkg/service/v0/driveitems.go +++ b/services/graph/pkg/service/v0/driveitems.go @@ -870,7 +870,10 @@ func cs3ResourceToDriveItem(logger *log.Logger, res *storageprovider.ResourceInf driveItem.Folder = &libregraph.Folder{} } - driveItem.Audio = cs3ResourceToDriveItemAudioFacet(logger, res) + if res.ArbitraryMetadata != nil { + driveItem.Audio = cs3ResourceToDriveItemAudioFacet(logger, res) + driveItem.Location = cs3ResourceToDriveItemLocationFacet(logger, res) + } return driveItem, nil } @@ -893,6 +896,20 @@ func cs3ResourceToDriveItemAudioFacet(logger *log.Logger, res *storageprovider.R return nil } +func cs3ResourceToDriveItemLocationFacet(logger *log.Logger, res *storageprovider.ResourceInfo) *libregraph.GeoCoordinates { + k := res.ArbitraryMetadata.Metadata + if k == nil { + return nil + } + + var location = &libregraph.GeoCoordinates{} + if ok := unmarshalStringMap(logger, location, k, "libre.graph.location."); ok { + return location + } + + return nil +} + func getFieldName(structField reflect.StructField) string { tag := structField.Tag.Get("json") if tag == "" { @@ -922,6 +939,10 @@ func unmarshalStringMap(logger *log.Logger, out any, flatMap map[string]string, tmp, err = strconv.ParseInt(value, 10, 32) case reflect.Int64: tmp, err = strconv.ParseInt(value, 10, 64) + case reflect.Float32: + tmp, err = strconv.ParseFloat(value, 32) + case reflect.Float64: + tmp, err = strconv.ParseFloat(value, 64) case reflect.Bool: tmp, err = strconv.ParseBool(value) default: diff --git a/services/graph/pkg/service/v0/driveitems_test.go b/services/graph/pkg/service/v0/driveitems_test.go index 0eab263318f..e3eb2cb2562 100644 --- a/services/graph/pkg/service/v0/driveitems_test.go +++ b/services/graph/pkg/service/v0/driveitems_test.go @@ -759,6 +759,7 @@ var _ = Describe("Driveitems", func() { res := assertItemsList(1) Expect(res.Value[0].Audio).To(BeNil()) + Expect(res.Value[0].Location).To(BeNil()) }) It("returns the audio facet if metadata is available", func() { @@ -816,6 +817,36 @@ var _ = Describe("Driveitems", func() { Expect(audio.TrackCount).To(Equal(libregraph.PtrInt32(9))) Expect(audio.Year).To(Equal(libregraph.PtrInt32(1994))) }) + + It("returns the location facet if metadata is available", func() { + gatewayClient.On("ListContainer", mock.Anything, mock.Anything).Return(&provider.ListContainerResponse{ + Status: status.NewOK(ctx), + Infos: []*provider.ResourceInfo{ + { + Type: provider.ResourceType_RESOURCE_TYPE_FILE, + Id: &provider.ResourceId{StorageId: "storageid", SpaceId: "spaceid", OpaqueId: "opaqueid"}, + Etag: "etag", + Mtime: utils.TimeToTS(mtime), + MimeType: "image/jpeg", + ArbitraryMetadata: &provider.ArbitraryMetadata{ + Metadata: map[string]string{ + "libre.graph.location.altitude": "1047.7", + "libre.graph.location.latitude": "49.48675890884328", + "libre.graph.location.longitude": "11.103870357204285", + }, + }, + }, + }, + }, nil) + + res := assertItemsList(1) + location := res.Value[0].Location + + Expect(location).ToNot(BeNil()) + Expect(location.Altitude).To(Equal(libregraph.PtrFloat64(1047.7))) + Expect(location.Latitude).To(Equal(libregraph.PtrFloat64(49.48675890884328))) + Expect(location.Longitude).To(Equal(libregraph.PtrFloat64(11.103870357204285))) + }) }) }) }) diff --git a/services/search/pkg/content/content.go b/services/search/pkg/content/content.go index fccd8062896..ba61befaf40 100644 --- a/services/search/pkg/content/content.go +++ b/services/search/pkg/content/content.go @@ -21,7 +21,8 @@ type Document struct { Mtime string MimeType string Tags []string - Audio *libregraph.Audio `json:"audio,omitempty"` + Audio *libregraph.Audio `json:"audio,omitempty"` + Location *libregraph.GeoCoordinates `json:"location,omitempty"` } func CleanString(content, langCode string) string { diff --git a/services/search/pkg/content/tika.go b/services/search/pkg/content/tika.go index b169de8fd88..d4d0b53f447 100644 --- a/services/search/pkg/content/tika.go +++ b/services/search/pkg/content/tika.go @@ -89,72 +89,121 @@ func (t Tika) Extract(ctx context.Context, ri *provider.ResourceInfo) (Document, doc.Content = strings.TrimSpace(fmt.Sprintf("%s %s", doc.Content, content)) } + doc.Location = t.getLocation(meta) + if contentType, err := getFirstValue(meta, "Content-Type"); err == nil && strings.HasPrefix(contentType, "audio/") { - audio := libregraph.Audio{} + doc.Audio = t.getAudio(meta) + } + } + + if langCode, _ := t.tika.LanguageString(ctx, doc.Content); langCode != "" && t.CleanStopWords { + doc.Content = CleanString(doc.Content, langCode) + } + + return doc, nil +} + +func (t Tika) getLocation(meta map[string][]string) *libregraph.GeoCoordinates { + var location *libregraph.GeoCoordinates + initLocation := func() { + if location == nil { + location = libregraph.NewGeoCoordinates() + } + } + + // TODO: location.Altitute: transform the following data to … feet above sea level. + // "GPS:GPS Altitude": []string{"227.4 metres"}, + // "GPS:GPS Altitude Ref": []string{"Sea level"}, - if v, err := getFirstValue(meta, "xmpDM:album"); err == nil { - audio.SetAlbum(v) - } + if v, err := getFirstValue(meta, "geo:lat"); err == nil { + if i, err := strconv.ParseFloat(v, 64); err == nil { + initLocation() + location.SetLatitude(i) + } + } + + if v, err := getFirstValue(meta, "geo:long"); err == nil { + if i, err := strconv.ParseFloat(v, 64); err == nil { + initLocation() + location.SetLongitude(i) + } + } - if v, err := getFirstValue(meta, "xmpDM:albumArtist"); err == nil { - audio.SetAlbumArtist(v) - } + return location +} - if v, err := getFirstValue(meta, "xmpDM:artist"); err == nil { - audio.SetArtist(v) - } +func (t Tika) getAudio(meta map[string][]string) *libregraph.Audio { + var audio *libregraph.Audio + initAudio := func() { + if audio == nil { + audio = libregraph.NewAudio() + } + } - // TODO: audio.Bitrate: not provided by tika - // TODO: audio.Composers: not provided by tika - // TODO: audio.Copyright: not provided by tika for audio files? + if v, err := getFirstValue(meta, "xmpDM:album"); err == nil { + initAudio() + audio.SetAlbum(v) + } - if v, err := getFirstValue(meta, "xmpDM:discNumber"); err == nil { - if i, err := strconv.ParseInt(v, 10, 32); err == nil { - audio.SetDisc(int32(i)) - } + if v, err := getFirstValue(meta, "xmpDM:albumArtist"); err == nil { + initAudio() + audio.SetAlbumArtist(v) + } - } + if v, err := getFirstValue(meta, "xmpDM:artist"); err == nil { + initAudio() + audio.SetArtist(v) + } - // TODO: audio.DiscCount: not provided by tika + // TODO: audio.Bitrate: not provided by tika + // TODO: audio.Composers: not provided by tika + // TODO: audio.Copyright: not provided by tika for audio files? - if v, err := getFirstValue(meta, "xmpDM:duration"); err == nil { - if i, err := strconv.ParseInt(v, 10, 64); err == nil { - audio.SetDuration(i * 1000) - } - } + if v, err := getFirstValue(meta, "xmpDM:discNumber"); err == nil { + if i, err := strconv.ParseInt(v, 10, 32); err == nil { + initAudio() + audio.SetDisc(int32(i)) + } - if v, err := getFirstValue(meta, "xmpDM:genre"); err == nil { - audio.SetGenre(v) - } + } - // TODO: audio.HasDrm: not provided by tika - // TODO: audio.IsVariableBitrate: not provided by tika + // TODO: audio.DiscCount: not provided by tika - if v, err := getFirstValue(meta, "dc:title"); err == nil { - audio.SetTitle(v) - } + if v, err := getFirstValue(meta, "xmpDM:duration"); err == nil { + if i, err := strconv.ParseInt(v, 10, 64); err == nil { + initAudio() + audio.SetDuration(i * 1000) + } + } - if v, err := getFirstValue(meta, "xmpDM:trackNumber"); err == nil { - if i, err := strconv.ParseInt(v, 10, 32); err == nil { - audio.SetTrack(int32(i)) - } - } + if v, err := getFirstValue(meta, "xmpDM:genre"); err == nil { + initAudio() + audio.SetGenre(v) + } - // TODO: audio.TrackCount: not provided by tika + // TODO: audio.HasDrm: not provided by tika + // TODO: audio.IsVariableBitrate: not provided by tika - if v, err := getFirstValue(meta, "xmpDM:releaseDate"); err == nil { - if i, err := strconv.ParseInt(v, 10, 32); err == nil { - audio.SetYear(int32(i)) - } - } + if v, err := getFirstValue(meta, "dc:title"); err == nil { + initAudio() + audio.SetTitle(v) + } - doc.Audio = &audio + if v, err := getFirstValue(meta, "xmpDM:trackNumber"); err == nil { + if i, err := strconv.ParseInt(v, 10, 32); err == nil { + initAudio() + audio.SetTrack(int32(i)) } } - if langCode, _ := t.tika.LanguageString(ctx, doc.Content); langCode != "" && t.CleanStopWords { - doc.Content = CleanString(doc.Content, langCode) + // TODO: audio.TrackCount: not provided by tika + + if v, err := getFirstValue(meta, "xmpDM:releaseDate"); err == nil { + if i, err := strconv.ParseInt(v, 10, 32); err == nil { + initAudio() + audio.SetYear(int32(i)) + } } - return doc, nil + return audio } diff --git a/services/search/pkg/content/tika_test.go b/services/search/pkg/content/tika_test.go index 497247cac8d..8a1c7699453 100644 --- a/services/search/pkg/content/tika_test.go +++ b/services/search/pkg/content/tika_test.go @@ -140,6 +140,28 @@ var _ = Describe("Tika", func() { }) + It("adds location content", func() { + fullResponse = `[ + { + "geo:lat": "49.48675890884328", + "geo:long": "11.103870357204285" + } + ]` + doc, err := tika.Extract(context.TODO(), &provider.ResourceInfo{ + Type: provider.ResourceType_RESOURCE_TYPE_FILE, + Size: 1, + }) + Expect(err).ToNot(HaveOccurred()) + + location := doc.Location + Expect(location).ToNot(BeNil()) + + // TODO: Altitude is not supported right now + Expect(location.Altitude).To(BeNil()) + Expect(location.Latitude).To(Equal(libregraph.PtrFloat64(49.48675890884328))) + Expect(location.Longitude).To(Equal(libregraph.PtrFloat64(11.103870357204285))) + }) + It("removes stop words", func() { body = "body to test stop words!!! against almost everyone" language = "en" diff --git a/services/search/pkg/engine/bleve.go b/services/search/pkg/engine/bleve.go index 8e1b6efedbb..a0b3f26f30c 100644 --- a/services/search/pkg/engine/bleve.go +++ b/services/search/pkg/engine/bleve.go @@ -211,6 +211,7 @@ func (b *Bleve) Search(ctx context.Context, sir *searchService.SearchIndexReques Tags: getFieldSliceValue[string](hit.Fields, "Tags"), Highlights: getFragmentValue(hit.Fragments, "Content", 0), Audio: getAudioValue[searchMessage.Audio](hit.Fields), + Location: getLocationValue[searchMessage.GeoCoordinates](hit.Fields), }, } @@ -329,6 +330,7 @@ func (b *Bleve) getResource(id string) (*Resource, error) { Content: getFieldValue[string](fields, "Content"), Tags: getFieldSliceValue[string](fields, "Tags"), Audio: getAudioValue[libregraph.Audio](fields), + Location: getLocationValue[libregraph.GeoCoordinates](fields), }, }, nil } @@ -382,6 +384,15 @@ func getAudioValue[T any](fields map[string]interface{}) *T { return nil } +func getLocationValue[T any](fields map[string]interface{}) *T { + var location = newPointerOfType[T]() + if ok := unmarshalInterfaceMap(location, fields, "location."); ok { + return location + } + + return nil +} + func (b *Bleve) updateEntity(id string, mutateFunc func(r *Resource)) (*Resource, error) { it, err := b.getResource(id) if err != nil { diff --git a/services/search/pkg/engine/bleve_test.go b/services/search/pkg/engine/bleve_test.go index 8b9668faf47..1023ee8da0c 100644 --- a/services/search/pkg/engine/bleve_test.go +++ b/services/search/pkg/engine/bleve_test.go @@ -548,5 +548,38 @@ var _ = Describe("Bleve", func() { }) }) + Context("with location metadata", func() { + BeforeEach(func() { + resource := engine.Resource{ + ID: "1$2!7", + ParentID: rootResource.ID, + RootID: rootResource.ID, + Path: "./team.jpg", + Type: uint64(sprovider.ResourceType_RESOURCE_TYPE_FILE), + Document: content.Document{ + Name: "team.jpg", + MimeType: "image/jpeg", + Location: &libregraph.GeoCoordinates{ + Altitude: libregraph.PtrFloat64(1047.7), + Latitude: libregraph.PtrFloat64(49.48675890884328), + Longitude: libregraph.PtrFloat64(11.103870357204285), + }, + }, + } + err := eng.Upsert(resource.ID, resource) + Expect(err).ToNot(HaveOccurred()) + }) + + It("returns audio metadata for search", func() { + matches := assertDocCount(rootResource.ID, `*team*`, 1) + location := matches[0].Entity.Location + + Expect(location).ToNot(BeNil()) + + Expect(location.Altitude).To(Equal(libregraph.PtrFloat64(1047.7))) + Expect(location.Latitude).To(Equal(libregraph.PtrFloat64(49.48675890884328))) + Expect(location.Longitude).To(Equal(libregraph.PtrFloat64(11.103870357204285))) + }) + }) }) }) diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go index 199b3df31e3..7561e880fe0 100644 --- a/services/search/pkg/search/service.go +++ b/services/search/pkg/search/service.go @@ -494,6 +494,7 @@ func (s *Service) UpsertItem(ref *provider.Reference, uID *user.UserId) { // determine if metadata needs to be stored in storage as well metadata := map[string]string{} addAudioMetadata(metadata, doc.Audio) + addLocationMetadata(metadata, doc.Location) if len(metadata) == 0 { return } @@ -525,6 +526,13 @@ func addAudioMetadata(metadata map[string]string, audio *libregraph.Audio) { marshalToStringMap(audio, metadata, "libre.graph.audio.") } +func addLocationMetadata(metadata map[string]string, location *libregraph.GeoCoordinates) { + if location == nil { + return + } + marshalToStringMap(location, metadata, "libre.graph.location.") +} + func marshalToStringMap[T libregraph.MappedNullable](source T, target map[string]string, prefix string) { // ToMap never returns a non-nil error ... m, _ := source.ToMap() @@ -549,6 +557,10 @@ func valueToString(value interface{}) string { return strconv.FormatInt(int64(*v), 10) case *int64: return strconv.FormatInt(*v, 10) + case *float32: + return strconv.FormatFloat(float64(*v), 'f', -1, 32) + case *float64: + return strconv.FormatFloat(*v, 'f', -1, 64) case *bool: return strconv.FormatBool(*v) default: