How to save a CLLocation with Cloudkit JS - cloudkit

I'm using Cloudkit JS to save data to a public database. Its easy to do when the fields are all strings. I'm stuck now trying to figure out how to save data when the field type is a CLLocation. Somehow I need to structure the JavaScript to send both latitude and longitude values.
See the ??? in the code example below;
var new record = { recordType: "Thing",
fields: { name: { value: "My Name" },
description: { value: "My Description" },
location: { ??? }
}
};
Does anyone know how to take the lat and long coordinates and represent them in the code above?

Try passing it like this:
fields: {
location: { value: { latitude: lat, longitude: lng }, type: "LOCATION" }
}
lat and lng are Doubles, not Strings

Related

Algolia sorting by geolocation in Swift

I have a problem with Algolia geolocation sorting in iOS App.
I need to display all documents in a range of 100 km from the user location.
I have multiple documents in my Indice. The document looks like this:
"document": {
"price": 5000,
"unit": "cały projekt",
"_geoloc": {
"lat": 54.5,
"lng": 18.55
},
"title": "Test ogloszenia",
"range": 0,
"activeFrom": {
"_seconds": 1597042800,
"_nanoseconds": 0
}
}
In my Algolia Ranking and Sorting, I have default set GEO.
My Swift code for sorting locations looks like below:
func getAnnouncesLocation(location: CLLocationCoordinate2D, completion: #escaping ([Announcement]) -> ()) {
var announcementsArray = [Announcement]()
let query = Query(query: "")
query.aroundLatLng = LatLng(lat: location.latitude, lng: location.longitude)
query.aroundRadius = .explicit(100000) // 100 km
collectionIndex = searchClient.index(withName: "products_geolocation")
collectionIndex.search(query) { (content, error) in
guard let content = content else {
if let error = error {
print(error.localizedDescription)
}
return
}
print("HITS \(content)")
}
}
The code doesn't return any error but the content is empty.
Another sorting like by price works perfectly. The only problem is with geolocation.
If that can help to set sorting by the price I need to add a sort-by attribute like this: document.price in Dashboard.
I am saving data to Algolia from my server in Node.js and there I am creating _geoloc value.
The latitude and longitude are hardcoded for testing so there isn’t a problem with async.
Thank you for any kind of help.
Regards
Matt
I found a bug in my project.
While sending the document to Algolia I send like this:
const record = {
objectID: doc.id,
document: document
};
where document contained all properties and also _geoloc property. After some investigation, I separated _geoloc from the document I sent it like below:
const record = {
objectID: doc.id,
document: document,
_geoloc: coordinates
};
Now everything works fine.
Cheers

a missing sub document turns into a subdocument with undefined values, and schema validation fails

I have a schema.graphqls that looks like this:
type House {
id: ID!
rooms: Int!
address: String!
owner: Owner
}
type Owner: {
name: String!,
age: Int!
}
and a complementing mongoose schema:
export default class House {
static schema = {
rooms: Number
address: String,
owner: {
type : {
name: String,
age: Number
},
required: false
}
};
}
and I have an object in my mongodb looking like this (notice owner is missing intentionally):
ObjectId("xxx") {
rooms: 3,
address: "the street"
}
I'm trying to retrieve this document, the owner subdocument is missing (which is fine, its not mandatory).
The mongoose result fills this missing subdocument with undefined attributes,
ObjectId("xxx") {
rooms: 3,
address: "the street"
owner : {
name: undefined
age: undefined
}
which fails the schema validations (since indeed name and age are mandatory, if subdocument exists).
the actual error i'm getting is:
Resolve function for "House.owner" returned undefined
Could you possibly point me to whatever i'm doing wrong here?
thanks in advance
Following a direction from #Neil Lunn, I realised the problem is in the mongoose schema,
which led me to add required: false - which wasn't enough, but after adding also default: null voila.
problem solved. error gone.
final mongoose schema, to whom it may be of interest:
export default class House {
static schema = {
rooms: Number
address: String,
owner: {
type : {
name: String,
age: Number
},
required: false,
default: null
}
};
}

Load JSON into Firestore - data types

Native JSON only supports string, number, boolean. Firestore supports additional types - timestamp, geopoint, date/time. How should you format the JSON to load these data types?
Here's some information about supported datatypes: https://firebase.google.com/docs/firestore/manage-data/data-types
As well as an example:
var docData = {
stringExample: "Hello world!",
booleanExample: true,
// Note that numbers can be either integers or floating point
numberExample: 3.14159265,
// JSON representation as RFC 3339 String with timezone Z
// e.g. 1815-12-10T00:00:00.000Z
dateExample: new Date("December 10, 1815"),
arrayExample: [5, true, "hello"],
nullExample: null,
objectExample: {
a: 5,
b: {
nested: "foo"
}
},
// JSON object w/ latitude and longitude keys
geoPointExample: {
latitude: 37.773972
longitude: -122.431297
},
// FIRESTORE GEOPOINT
geoPointExample2: new firebase.firestore.GeoPoint(37.7739,-122.4312)
// Blobs are base64 encoded strings
blobExample: "RmlyZXN0b3JlIGlzIGF3ZXNvbWUh"
};
More info on protobuf to JSON is available here: https://developers.google.com/protocol-buffers/docs/proto3#json

Swift struct referencing another struct

I'm testing the following code in an Xcode Playground.
struct Coordinate: Codable {
var latitude: Double
var longitude: Double
}
struct Landmark: Codable {
var name: String
var foundingYear: Int
var location: Coordinate
}
var MyCoordinate: Coordinate = Coordinate.init(latitude: 30.1, longitude: 10.2)
var LandmarkA: Landmark = Landmark.init(name: "My Landmark", foundingYear: 2017, location: MyCoordinate)
var LandmarkB: Landmark = Landmark.init(name: "My Landmark 2", foundingYear: 2016, location: MyCoordinate)
LandmarkA.location.latitude = 12.1
print (LandmarkA.location.latitude) // 12.1
print (LandmarkB.location.latitude) // 30.1
Now if I convert this code to classes instead of structs.
class Coordinate {
var latitude: Double
var longitude: Double
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
}
class Landmark {
var name: String
var foundingYear: Int
var location: Coordinate
init(name: String, foundingYear: Int, location: Coordinate) {
self.name = name
self.foundingYear = foundingYear
self.location = location
}
}
var MyCoordinate: Coordinate = Coordinate.init(latitude: 30.1, longitude: 10.2)
var LandmarkA: Landmark = Landmark.init(name: "My Landmark", foundingYear: 2017, location: MyCoordinate)
var LandmarkB: Landmark = Landmark.init(name: "My Landmark 2", foundingYear: 2016, location: MyCoordinate)
LandmarkA.location.latitude = 12.1
print (LandmarkA.location.latitude) // 12.1
print (LandmarkB.location.latitude) // 12.1
Is it possible to get structs to behave the same way as classes in terms of what location is equal to? If I change the coordinate either by doing LandmarkA.location.latitude or MyCoordinate.latitude it should set both LandmarkA and LandmarkB. This behavior works with classes but I can't use Codable in classes and there is less code needed to setup structs.
The last two print statements in the first example I would like to both be 12.1 and the same since it's both referencing the same object.
This is the only thing I can't figure out with structs. structs have everything else I need, Codeable, functions within the struct, minimal setup code, etc.
I would really prefer to use structs but if this isn't possible I will probably settle for using classes.
Any thoughts of how to achieve this?

Using Array Attribute Type in Sails

I'm looking to use the sails attribute type 'array' in my app, but I can't find documentation for this anywhere.
I'd like to do the following:
module.exports = {
attributes : {
images: {
type: ['string']
},
location: {
type: ['float','float']
}
}
}
image is an array that will hold a list of image urls and location will hold 2 floats. Will this work in sail's? Else how can I get this to work.
Thanks
PS: I'm working solely with MongoDB
As of Sails 1.0 type array is no longer supported.
The type "array" is no longer supported. To use this type in your model, change
type to one of the supported types and set the columnType property to a column
type supported by the model's adapter, e.g. { type: 'json', columnType: 'array' }
SOLUTION ONE
Set up property to store an images array and a location array...
module.exports = {
attributes : {
images: {
type: 'json',
columnType: 'array'
}
location: {
type: 'json',
columnType: 'array'
}
}
}
SOLUTION TWO
A more elegant solution is to set up a single object to store both filename and location data
module.exports = {
attributes : {
images: {
type: 'json'
}
}
}
Then in your controller you would store object properties as arrays...
let imageData = {
filename: ["image1.jpg", "image2.jpg", "image3.jpg"],
location: [154.123123, 155.3434234, 35.12312312]
};
Images.create({images:imageData});
Some issues when storing data to the json object is that a string like "image1.jpg,image2.jpg,image3.jpg" will store in MongoDb no worries... doh. Ensure that when POSTing you may need to split the data .split(',').
sailsjs provide a function to solve your question,you can try this
module.exports = {
attributes : {
images: {
type: 'string'
},
location: {
type: 'json'
}
}
}
As far as I know, you can only specify it like this:
module.exports = {
attributes : {
images: {
type: 'array'
},
location: {
type: 'array'
}
}
}
See Sails ORM Attributes
For sails 1.0, for array maybe you can try this way i'm using just for sharing.
Also you can replace before update and process native query() and delete the attributes for updating by waterline. Hope this help you.
variants:
{
type: 'json',
columnType: 'array',
custom: (value) =>
{
/*
[
code : unique, string
name : string, maxLength[30]
cost : number, isFinite
price : number, isFinite
default : boolean
]
*/
return _.isArray(value)
&& _.every(value, (variant1) =>
{
return _.countBy(value, (variant2) =>
{
return variant2.code == variant1.code ? 'eq' : 'uneq';
}).eq <= 1
&& _.isString(variant1.name) && variant1.name.length < 30
&& _.isNumber(variant1.cost) && _.isFinite(variant1.cost)
&& _.isNumber(variant1.price) && _.isFinite(variant1.price)
&& _.isBoolean(variant1.default);
});
},
},
You can use type as ref for arrays and objects.
module.exports = {
attributes: {
images: {
type: 'ref'
},
location: {
type: 'ref'
}
}
}