I'm trying to do a simple view, that takes my data and groups all of my records by a key "sortDate". I feel like i've followed all the examples, read all the documents multiple times and it just doesn't work as expected.
Here is the view code i'm creating:
guard let database = database else { return }
database.viewNamed("byDate").setMapBlock({ (doc, emit) in
if let date = doc["sortDate"] as? String {
emit(date, doc)
}
}, version: "8")
let query = database.viewNamed("byDate").createQuery()
query.groupLevel = 1
query.descending = true
do {
let result = try query.run()
print(result)
while let row = result.nextRow() {
print(row)
print(row.value) //EMPTY
}
} catch {
print("Failed to retrieve all documents for \(database.name) database")
}
My row.value is NULL even though there are multiple records in the database, my allDocs query is returning just fine.
Specifying a grouplevel when the key is not an array causes CBL to aggregate the results using the reduce function. You haven't specified one, so that's why you get no results.
From the docs:
The value property of each row will be the result of running the view's reduce function over all the rows that were aggregated; or if the view has no reduce function, there's no value. (See the View documentation for information on reduce functions.)
Related
So I'm working on my first workout tracking app and this is my first time using Firebase/Firestore, so I'm just trying to figure out if there is a simple query that I can use for this...
Here is what my Firestore Database structure looks like:
/Users/mi9P3TrLwkQ3oDIut/Days/WZ3Q6LDuu1kja/Workouts/BpLGFREoJNzNQW/Exercises/5vRWuHlcJHc/WeightReps/cKrB0Dpf0myEDQV0
Basically I need to return a value for numberOfRowsInSection, but the value that I need to access is the number of workouts that are associated with each day of the week, and I'm not too sure how to go about using the section parameter to iterate over each day document in my Days collection in order to access the Workouts subcollections and get the count of the documents there for each day collection. Does that make sense?
I hope that the question makes sense. Any help would be greatly appreciated!
Not entirely sure if I am getting your question right but if you want to retrieve several documents with all their attributes this is how you can do it:
var counter = 0
func getData() {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser!.uid
for data in self.dataSourceArray {
db.collection("users").document(userID).collection("yourCollectionName").document(data.name).collection("yourCollectionName").getDocuments() { ( querySnapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
for document in querySnapshot!.documents {
self.counter += 1
}
}
}
}
}
Is this what youre looking for ?
I want to show a list of artists in my app which the user will be able to search through. I'm not sure however how to save this in Firestore?
First I created a collection "searchLists" with a document for each DJ but that means a lot of document reads so that's out of the question.
Now I created a document called "artists" which has a field "artistsDictionary" which contains all the artists.
| searchLists (collection)
* artists (document)
- artistsArray (array)
0: (map)
name: "Artist 0" (string)
1: (map)
name: "Artist 1" (string)
2: (map)
name: "Artist 2" (string)
And I retrieve and parse the array as followed:
let docRef = db.collection("searchLists").document("artists")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
guard let documentData = document.data() else { return }
let artistsDictionaryArray = documentData["artistsArray"] as? [[String: Any]] ?? []
let parsedArtists = artistsDictionaryArray.compactMap {
return SimpleArtist(dictionary: $0)
}
self.artistsArray = parsedArtists
} else {
print("Document does not exist")
}
}
(SimpleArtist is a struct containing a "name" field.)
And I mean, it works, but I'm still new to Firestore and this seems kinda off. Is it? Or is this how I should/could do it?
First I created a collection "searchLists" with a document for each DJ but that means a lot of document reads so that's out of the question.
This is the right approach, so you should go ahead with it.
Why do I say that?
According to the official documentation regarding modeling data in a Cloud Firestore database:
Cloud Firestore is optimized for storing large collections of small documents.
Storing data in an array is not a bad option but this is most likely used, let's say to store favorite djs. I say that because the documents have limits in Firestore. So there are some limits when it comes to how much data you can put into a document. According to the official documentation regarding usage and limits:
Maximum size for a document: 1 MiB (1,048,576 bytes)
As you can see, you are limited to 1 MiB total of data in a single document. When we are talking about storing text, you can store pretty much but as your array getts bigger, be careful about this limitation.
First off, Alexs' answer is 100% correct.
I want to add some additional data points that may help you in the long run.
The first item is arrays. Arrays are very challenging in NoSQL databases - while they provide a logical sequence data via the index, 0, 1, 2 they don't behave like an array in code - so for example; Suppose you wanted to insert an item at an index. Well - you can't (*you can but it's not just a simple 'insert' call). Also, you can't target array elements in queries which limits their usefulness. The smallest unit of change in a Firestore array field is the entire field - smaller changes to individual elements of a field can't be made. The fix is to not use arrays and to let FireStore create the documentID's for you data 'objects' on the fly e.g. the 'keys' to the node
The second issue - (which may not be an issue currently) is how the data is being handled. Suppose you release your app and a user has 2 million artists in their collection - with your code as is, all of that data is downloaded at one time which will probably not be the best UI experience but additionally, it could overwhelm the memory of the device. So working in 'chunks' of data it a lot easier on the device, and the user.
So I put together some sample code to help with that.
First a class to store your Artist data in. Just keeps track of the documentID and the artist name.
class ArtistClass {
var docId = ""
var name = ""
init(aDocId: String, aName: String) {
self.docId = aDocId
self.name = aName
}
}
and a class array to keep the artists in. This would be a potential dataSource for a tableView
var artistArray = [ArtistClass]()
This is to write an artist as a document instead of in an array. The documentID is a FireStore generated 'key' that's created for each artist.
func writeArtists() {
let artistsRef = self.db.collection("artists")
let floyd = [
"name": "Pink Floyd"
]
let zep = [
"name": "Led Zeppelin"
]
let who = [
"name": "The Who"
]
artistsRef.addDocument(data: floyd)
artistsRef.addDocument(data: zep)
artistsRef.addDocument(data: who)
}
and then function to read in all artists.
func readArtists() {
let artistsRef = self.db.collection("artists")
artistsRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let docId = document.documentID
let name = document.get("name") as! String
let artist = ArtistClass(aDocId: docId, aName: name)
self.artistArray.append(artist)
}
for a in self.artistArray { //prints the artists to console
print(a.docId, a.name)
}
}
}
}
So your data in Firestore looks like this
artists (collection)
8lok0a0ksodPSSKS
name: "Let Zeppelin"
WKkookokopkdokas
name: "The Who"
uh99jkjekkkokoks
name: "Pink Floyd"
so then the cool part. Suppose you have a tableView that shows 10 artists at a time with a down button to see the next 10. Make this change
let artistsRef = self.db.collection("artists").order(by: "name").limit(to: 10)
Oh - and you'll notice the function of sorting now goes the server instead of the device - so if there's a million artists, it's sorted on the server before being delivered to the device which will be significantly faster.
You can also then more easily perform queries for specific artist data and you won't need to be as concerned about storage as each artist is their own document instead of all artists in one.
Hope that helps!
I am using SqLite library for swift. I can return one row with this method,
func getById(id:Int64) -> Row?{
do{
let query=Users.filter(Id==id)
print(query.asSQL())
var data = try db!.pluck(query)
return data
}catch{
print("Error: \(error)")
return nil
}
}
This function is returning "Row" it's ok. But i am using tableView object so i need one dataSource.
How can i return table and set datasource for tableView, i have not found an example of this.
Best regards.
You can use this code:
I don't know all of your classes, but this is the main method to get query for SQLite3(swift), go through all results(if multiple), append them and return the whole data at the end.
//You say you want to return array of rows
func getById(id:Int64) -> [Row]? {
//Create empty array
var data = [Row]()
//build the query. I use UsersTable.entityName.filter(), but I don't know your structure
let query=Users.filter(Id==id)
do {
//for cycle to go to all of the results
for rowInfo in try db!.pluck(query) {
//Create variable. I don't know what you want. You can create it as Row() and after that add the information
let userData = Row()
//you need to use rowInfo, it store the SQL data. inside the key is from the table Structure. You used Users.filter, so I guess the struct with all column names is Users, so Users.name is the 'name' column
userData.name = rowInfo[Users.name]
//Apend the data
data.append(userData)
}
} catch {
return nil
}
//Return the array of Rows
return data
}
I wrote the following function that reads through the list of media items in my iTunes directory and returns the music files. I need to return the "song titles" but when I run it the items returned are in an unknown format. I am pretty sure I need to run them through a property filter or use some conversion to get the actual names correctly. At the end I want to output the contents in an array of Strings. I only run the loop four times in the screen shot attached. Can anyone point me to a missing conversion? It looks like the output is in hex format but not clear on that.
class func readMusicFiles() -> NSMutableArray {
//var songDecoded:[NSMutableArray]
let result = NSMutableArray()
let allSongsQuery:MPMediaQuery = MPMediaQuery.songsQuery();
let tempArray:NSArray = allSongsQuery.items!;
for item:AnyObject in tempArray {
if (item is MPMediaItem) {
let temp = item as! MPMediaItem;
if (temp.mediaType != MPMediaType.Music) {
continue;
}
result.addObject(item);
}
}
print(result)
return result
}
}
The output looks like this
The "hex" is not a "format"; it's merely an indication of the memory address of the object. Ignore it.
You've got your media items (songs in this case). Now, instead of saying print(result), ask for their titles:
for song in result {
print(song.title)
}
Or, to make a new array:
let titles = result.map {$0.title}
(Also, do not declare your function to return an NSMutableArray. That's a Cocoa thing. Try to stick to Swift arrays. For example, if you are going to end up with an array of titles, those are strings, so return a [String].)
I new to Swift programming. I am using CoreData in my app as a database option.
I am using NSManagedObject (deleteObject) to delete lastObject, firstObject and entire rows in the table.
I want to delete a specific row in the table by using a value which is stored in a variable. I am not using table view (I don't have any index path), this delete operation is the background process after I get response from the my application server.
Any links or tutorials or suggestions will be helpful.
Thanks in advance!!!
Code for deleting lastObject
if var results1 = context.executeFetchRequest(requestParams, error: nil) as? [Params] {
println("\n Params Results count : \(results1.count)")
for param in results1{
for var n = 0 ;n < results1.count; ++n
{
let lastPerson = (results1 as NSArray).lastObject as Params
context.deleteObject(lastPerson)
var savingError: NSError?
context.save(&savingError)
}
}
}
Params table consists of columns 'pname' and 'pvalues'.
I want to delete a specific param pname ='server_timeout'.
Consider if 'Params' table consist of 10 rows and 'pname'='server_timeout' might be present at index 5, 6.
How can I find the 'pname' (i.e 'server_timeout') in this list of records and delete it?
How about creating a predicate that filters out the list to those with pname value of server_timeout in the first place.
context.performBlock {
requestParams.predicate = NSPredicate(format: "pname == %#", "server_timeout")
if let results = context.executeFetchRequest(requestParams, error: nil) as? [Params] {
for param in results {
context.deleteObject(param)
}
var error: NSError?
if !context.save(&error) {
println(error)
}
}
}
As you can see, I have placed the whole search-and-delete inside a performBlock closure. I don't know how you have setup your Core Data, but you might want to consider threading to maximize perceived performance.