ParseSwift ParseObject QueryConstraint - swift

I have two collections in a mongo DB.
Here is how a document looks in the first collection (MainCollection):
_id
:"mzWqPEDYRU"
TITLE
:"ZAZ: I want."
ownerID
:"lGutCBY52g"
accessKey
:"0kAd4TOmoK0"
_created_at
:2020-03-13T11:42:11.169+00:00
_updated_at
:2020-03-13T17:08:15.090+00:00
downloadCount
:2
And here is how it looks in the second collection (SecondCollection):
_id
:"07BOGA8bHG"
_p_unit
:"MainCollection$mzWqPEDYRU"
SENTENCE
:"I love nature peace and freedom."
Order
:5
ownerID
:"lGutCBY52g"
AUDIO
:"07067b5589d1edd1d907e96c1daf6da1_VOICE.bin"
_created_at
:2020-03-13T11:42:17.483+00:00
_updated_at
:2020-03-13T11:42:19.336+00:00
There is a parent children relationship between the first and the second collection. In the last document we can see the _p_unit field where the "mzWqPEDYRU" part points to the id of the parent in the first collection.
I have one problem from start with the following code:
func theFunction() {
do {MainCollection.query().find() {
result in
switch result {
case .success(let items):
print("items.count = \(items.count)")
for item in items {
/// ....
}
case .failure(let error):
print("Error in \(#function): \(error)")
}
}
}
}
The way this above code is written works fine and I get the number of elements in MainCollection as one would expect. But then comes a less expected behaviour, in this same code if I replace MainCollection by SecondCollection, instead of getting the number of elements in SecondCollection as I would think. I get an error like:
ParseError(code: ParseSwift.ParseError.Code.unknownError,
message: "Error decoding parse-server response:
Optional(<NSHTTPURLResponse: 0x2837211a0> { URL:}
{ Status Code: 200, Headers {} }) with error:
The data couldn’t be read because it isn’t in the correct format.
Format: Optional(\"{\\\"results\\\": .......
Can anybody point out what is causing this?

It is something like:
var SecondCollection.query(unit == documentOne).find()

The .query() method works in a key/value scheme so it should pass the key as a string and the value as the referenced type, so passing "unit" between double quotes is correct:
do {SecondCollection.query("unit" == cell).find() {
The error you're getting is because cell is a Parse.Object and it is expecting a value in that place (a property in this case).
Please try the following and see if it works for you:
do {SecondCollection.query("unit" == cell.id).find() {

Related

Is it possible to filter model's subfield in Strapi's GraphQL?

With strapi GraphQL it is possible to limit the number of returned entries. However, is it also possible to limit the number of returned subentries? For example, if I have "galleries" content type with a field called "images", can I limit the number of Images returned?
This is what I tried and works:
query mainQuery {
galleries(limit: 1) {
name
image {
url
id
}
}
}
And this is what I would actually like to do but does not work. There is no error or anything but the query returns all results, not just 1:
query mainQuery {
galleries {
name
image(limit: 1) {
url
id
}
}
}
I would like to fetch all galleries but only one image in each of them. Can anybody help me solve this?

FireStore - how to get around array "does-not-contain" queries

After some research, it's seems clear that I cannot use FireStore to query items a given array does NOT contain. Does anyone have a workaround for this use case?...
After a user signs up, the app fetches a bunch of cards that each have a corresponding "card" document in FireStore. After a user interacts with a card, the card document adds the user's uid to a field array (ex: usersWhoHaveSeenThisCard: [userUID]) and the "user" document adds the card's uid to a field array (ex: cardsThisUserHasSeen: [cardUID]). The "user" documents live in a "user" collection and the "card" documents live in a "card" collection.
Currently, I'd like to fetch all cards that a user has NOT interacted with. However, this is problematic, as I only know the cards that a user has interacted with, so a .whereField(usersWhoHaveSeenThisCard, arrayContains: currentUserUID) will not work, as I'd need an "arrayDoesNotContain" statement, which does not exist.
Finally, a user cannot own a card, so I cannot create a true / false boolian field in the card document (ex: userHasSeenThisCard: false) and search on that criteria.
The only solution I can think of, would be to create a new field array on the card document that includes every user who has NOT seen a card (ex: usersWhoHaveNotSeenThisCard: [userUID]), but that means that every user who signs up would have to write their uid to 1000+ card documents, which would eat up my data.
I might just be out of luck, but am hoping someone more knowledgeable with NOSQL / FireStore could provide some insight.
// If any code sample would help, please let me know and I'll update - I think this is largely conceptual as of now
As you've discovered from query limitations, there is no easy workaround for this using Cloud Firestore alone. You will need to somehow store a list of documents seen, load that into memory in the client app, then manually subtract those documents from the query results of all potential documents.
You might want to consider augmenting your app with another database that can do this sort of operation more cleanly (such as a SQL database that can perform joins and subqueries), and maintain them in parallel.
Either that, or require all the documents to be seen in a predictable order, such as by timestamp. Then all you have to store is the timestamp of the last document seen, and use that to filter the results.
There is an accepted and good answer, however, it doesn't provide a direct solution to the question so here goes... (this may or may not be helpful but it does work)
I don't know exactly what your Firestore structure is so here's my assumption:
cards
card_id_0
usersWhoHaveSeenThisCard
0: uid_0
1: uid_1
2: uid_2
card_id_1
usersWhoHaveSeenThisCard
0: uid_2
1: uid_3
card_id_2
usersWhoHaveSeenThisCard
0: uid_1
1: uid_3
Suppose we want to know which cards uid_2 has not seen - which in this case is card_id_2
func findCardsUserHasNotSeen(uidToCheck: String, completion: #escaping ( ([String]) -> Void ) ) {
let ref = self.db.collection("cards")
ref.getDocuments(completion: { snapshot, err in
if let err = err {
print(err.localizedDescription)
return
}
guard let docs = snapshot?.documents else {
print("no docs")
return
}
var documentsIdsThatDoNotContainThisUser = [String]()
for doc in docs {
let uidArray = doc.get("usersWhoHaveSeenThisCard") as! [String]
let x = uidArray.contains(uidToCheck)
if x == false {
documentsIdsThatDoNotContainThisUser.append(doc.documentID)
}
}
completion(documentsIdsThatDoNotContainThisUser)
})
}
Then, the use case like this
func checkUserAction() {
let uid = "uid_2" //the user id to check
self.findCardsUserHasNotSeen(uidToCheck: uid, completion: { result in
if result.count == 0 {
print("user: \(uid) has seen all cards")
return
}
for docId in result {
print("user: \(uid) has not seen: \(docId)")
}
})
}
and the output
user: uid_2 has not seen: card_id_2
This code goes through the documents, gets the array of uid's stored within each documents usersWhoHaveSeenThisCard node and determines if the uid is in the array. If not, it adds that documentID to the documentsIdsThatDoNotContainThisUser array. Once all docs have been checked, the array of documentID's that do not contain the user id is returned.
Knowing how fast Firestore is, I ran the code against a large dataset and the results were returned very quickly so it should not cause any kind of lag for most use cases.

Create a custom ID with Mgo

I'm currently starting with GoLang and MongoDB.I'm writing a small web application, a blog to be more specific (which is like the first webapp I write when I try new languages). Everything works fine with MGO even if I had some troubles at first. But now I'd like to access each blog entry (articles will be referred as entries to stick with my models) separately. I could use the ObjectID in the url. But that's damn ugly. For example :
mydomain.com/entries/543fd8940e82533995000002/
That's not user friendly. I did a lot of research on the internet to find a suitable solution, because using any other database engine I could just use the id (and that would be fine).
Could someone help me with the creation of a custom (public) id which would auto-increment when I insert a new entry and that I could use in the url ?
Here is the code of my model for now :
package models
import (
"time"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
)
type (
Entries []Entry
Entry struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Title string `bson:"title"`
Short string `bson:"short"`
Content string `bson:"content"`
Posted time.Time `bson:"posted"`
}
)
// Insert an entry to the database
func InsertEntry(database *mgo.Database, entry *Entry) error {
entry.ID = bson.NewObjectId()
return database.C("entries").Insert(entry)
}
// Find an entry by id
func GetEntryByID(database *mgo.Database, id string) (entry Entry, err error) {
bid := bson.ObjectIdHex(id)
err = database.C("entries").FindId(bid).One(&entry)
return
}
// Retrieves all the entries
func AllEntries(database *mgo.Database) (entries Entries, err error) {
err = database.C("entries").Find(nil).All(&entries)
return
}
// Retrieve all the entries sorted by date.
func AllEntriesByDate(database *mgo.Database) (entries Entries, err error) {
err = database.C("entries").Find(nil).Sort("-posted").All(&entries)
return
}
// Counts all the entries.
func CountAllEntries(database *mgo.Database) (count int, err error) {
count, err = database.C("entries").Find(nil).Count()
return
}
As you know the _id is a mandatory field, that is automatically fill by the driver when you do not set it. This is the behavior that you have in your current application/code. You can find information about this type and its generation here: http://docs.mongodb.org/manual/reference/object-id/
However, you can create your own _id and set the value to anything that makes sense for your business.
This is why I am do not understand the following statement:
I did a lot of research on the internet to find a suitable solution, because using any other database engine I could just use the id (and that would be fine).
You can use any value you want as soon as it is unique for your collection.
About the auto-increment, MongoDB does not provide any auto increment field, so you have to implement it yourself, and call the increment from your application.
For example you create a new collection that contains your "sequences/counters": (showing shell commands not go)
{
_id : "entry",
sequence : 0
}
Then when you want an new id for your document you have first to update, with a findand modify the document you have created with a simple $inc operation
var ret = db.counters.findAndModify(
{
query: { _id: "entry" },
update: { $inc: { seq: 1 } },
new: true
}
);
You can then use the returned value into you new document as an _id.
This pattern is documented here:
http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/

How to change array elements

How to write correct this part of query:
parseFloat(data.versions.0.content.axdducvoxb) ?**
I try to change my fields type from string to float. I have a lot of documents with main element and history of same elements:
axdducvoxb
versions.0.content.axdducvoxb
versions.1.content.axdducvoxb
versions.2.content.axdducvoxb
.......
Code:
db.documents.find().forEach(function(data) {
db.documents.update({_id:data._id},
{$set: {axdducvoxb:parseFloat(data.axdducvoxb)}});
})
When I use it everything is OK:
db.documents.find({"type": "chair"}).forEach(function(data) {
db.documents.update({_id:data._id},
{$set:{'versions.0.content.axdducvoxb':
parseFloat(data.axdducvoxb)}});
})
When I use it I replace versions.0.content.axdducvoxb element to data.axdducvoxb element
But when I make correct replace I see the Error:
SyntaxError: Unexpected number
Code:
db.documents.find({"type": "chair"}).forEach(function(data) {
db.documents.update({_id:data._id},
{$set:{'versions.0.content.axdducvoxb':
parseFloat(data.versions.0.content.axdducvoxb)}});
})
data.versions[0].content.axdducvoxb does not work too

Firestore batch set is only inserting the last item

I'm trying to batch create a bunch of documents from the array items, where each item is a map of key-value pairs. I've got multiple values in my array, but for some reason the only item that ever gets inserted into the db is the last item in the array.
What am I doing wrong here? Or can I only use setData once in the history of the commit?
let wordsRef = db.collection("items").document()
for item in items {
batch.setData(item, forDocument: wordsRef)
}
batch.commit() { err in
// error reporting
}
Thanks heaps!
I had the same problem a while ago. Just simply move the wordsRef inside the for loop, like I have done below. Hopefully this helps...
Code:
for item in items {
let wordsRef = db.collection("items").document()
batch.setData(item, forDocument: wordsRef)
}
batch.commit() { err in
// error reporting
}