I'm using MongoDB Realm and the Atlas UI for a Swift app mapping US States with congressional districts (I've used Region to refer to states to avoid confusion with the swift keyword #State), so each region should have a list of districts. I can insert starting data on regions and districts into the Atlas UI using PyMongo and display those states and districts individually when pulling realm results in my swift app, but when I pull the region it does not show any associated districts in its list of districts.
For reference, here is how I defined my region and district model
class Region_Preset: Object {
#objc dynamic var id_number = 0
#objc dynamic var _id = ObjectId.generate()
#objc dynamic var _partition: String = ""
#objc dynamic var Abbreviation: String = ""
#objc dynamic var Name: String = ""
let Population: Int? = 0
let Districts = List<District_Preset>()
let Senators = List<PoliticianUnverified_Preset>()
let Representatives = List<PoliticianUnverified_Preset>()
let Voters = List<Voter_Preset>()
override class func primaryKey() -> String? {
return "_id"
}
convenience init(id_number: Int, partition: String, Abbreviation: String, Name: String) {
self.init()
self.id_number = id_number
self._partition = partition
self.Abbreviation = Abbreviation
self.Name = Name
}
}
class District_Preset: Object {
#objc dynamic var id_number = 0
#objc dynamic var district_id: String = ""
#objc dynamic var _id = ObjectId.generate()
#objc dynamic var _partition: String = ""
#objc dynamic var Number: String = ""
let Population: Int? = 0
let Zipcodes = List<Zipcode_Preset>()
let Voters = List<Voter_Preset>()
#objc dynamic var Representative: PoliticianUnverified_Preset?
override class func primaryKey() -> String? {
return "_id"
}
convenience init(id_number: Int, district_id: String, partition: String, Number: String, Representative: PoliticianUnverified_Preset) {
self.init()
self.id_number = id_number
self.district_id = district_id
self._partition = partition
self.Number = Number
self.Representative = Representative
}
}
And here are the Pymongo functions
def upload_regions():
for region in region_file:
new_region = { "id_number": region[0], "_partition": PARTITION_VALUE, "Abbreviation": region[1], "Name": region[2]}
db.Region_Preset.insert_one(new_region)
def upload_districts():
for district in district_file:
new_district = { "id_number": district[0], "district_id": district[1], "_partition": PARTITION_VALUE, "Number": str(district[2]) }
db.District_Preset.insert_one(new_district)
returned_district = db.District_Preset.find_one({"id_number": district[0]})
db.Region_Preset.update_one({"id_number": district[4]}, {"$push": {"Districts": returned_district}})
And like I said, the results of the Pymongo insertion appear in the Atlas UI:
As you can see, both that regions and districts appear inserted in Atlas UI and even (from the 2nd picture) that the districts appear to be adequately inserted into the region's array of districts.
When I pull this data and print it in my Swift code, however, as shown in this code
Realm.asyncOpen(configuration: userconfiguration) { result in
switch result {
case .failure(let error):
print("Failed to open realm: \(error.localizedDescription)")
// handle error
case .success(let newrealm):
print("Successfully opened realm: \(newrealm)")
// Use realm
if let user_id = app.currentUser?.id {
let regions = newrealm.objects(Region_Preset.self)
print(regions)
I get the following output from the print statement
Results<Region_Preset> <0x7f931b325330> (
[0] Region_Preset {
id_number = 1;
_id = 5fe0f0c6474d38a0a1c11166;
_partition = vote;
Abbreviation = NY;
Name = New York;
Districts = List<District_Preset> <0x60000095b520> (
);
Senators = List<PoliticianUnverified_Preset> <0x6000009440a0> (
);
Representatives = List<PoliticianUnverified_Preset> <0x6000009441e0> (
);
Voters = List<Voter_Preset> <0x600000944280> (
);
}
)
Which shows the existence of the region in Atlas UI (and the districts when I print that instead individually), but does not show any districts associated with the region.
Am I doing something wrong here in the python code, the Atlas UI, the swift code or somewhere else? I assume this functionality should definitely be supported by Pymongo/MongoDB Realm but I can just not get it to show - any help is greatly appreciated!
Related
Suppose that I have two models and corresponding tables in the Realm Database
public class Customer :Object {
dynamic var Id : String = ""
dynamic var NAME : String = ""
dynamic var Address : String = ""
override public class func primaryKey() -> String? {
return "Id"
}
}
public class Bills :Object {
dynamic var Id : String = ""
dynamic var Amount : String = ""
dynamic var CustomerId : String = ""
override public class func primaryKey() -> String? {
return "Id"
}
}
What I am doing: I am getting list of All customers easily by doing
this
realmObj.objects(Customer.self)
What I want: I want to do followings.
I want to get all the list of Customers but I also want that the list contain such customer on top who purchases items more often. I mean I want the list ordering on the basis of customers that purchases more time from our shop. For this I can get it from the customer Id in Bills tables.
But I do not know how to do it in Realm.
I know it can be done through subquery but I am not getting how to do it in Realm. Please tell me what will be query/Predicate here to get the desired results.
I have two answers but there are a couple of things to address first.
For both of your classes, if you want them to be managed by Realm you need to include #objc before each var you want managed
public class Customer :Object {
#objc dynamic var Id : String = ""
or optionally add #objcMembers to the class name
#objcMembers class Customer :Object {
dynamic var Id : String = ""
The other thing is that class properties (vars) should always be lower case, class names should start with upper case. Nothing should be all CAPS.
public class Customer :Object {
#objc dynamic var customer_id : String = ""
#objc dynamic var name : String = ""
First solution is using your current structure:
var topCustomers = [(String, Int)]() //stores the customer id and count in an array of tuples
let results = realm.objects(Bills.self) //get all the bills
let allCustomerIds = results.map{ $0.CustomerId } //get a list of all of the customer id's
let uniqueIds = Set(allCustomerIds) //create a set of unique customer ids
for custId in uniqueIds { //iterate over the array of unique customer id's
let count = results.filter("CustomerId == %#", custId).count // get a count of each customer id from results
topCustomers.append( (custId, count) ) //add the customer id and it's count to the array
}
topCustomers.sort { $0.1 > $1.1 } //sort the array by count
for x in topCustomers { //print out the array - the customer with the most bills will be at the top
print(x.0, x.1)
}
a second, more elegant approach uses a relationship between customers and their bills. This will provide a LOT more flexibility for generating reports, queries and overall organization.
Here's the updated classes:
class CustomerClass: Object {
#objc dynamic var customer_id = UUID().uuidString
#objc dynamic var name = ""
#objc dynamic var address = ""
let billList = List<BillClass>()
override public class func primaryKey() -> String? {
return "customer_id"
}
}
class BillClass: Object {
#objc dynamic var bill_id = UUID().uuidString
#objc dynamic var amount = ""
override public class func primaryKey() -> String? {
return "bill_id"
}
}
and then some very short code to accomplish the same thing as in the first example
let customers = realm.objects(CustomerClass.self) //get all customers
let results = customers.map { ($0.name, $0.billList.count) } //iterate over all, creating tuple with customer name & bill count
let sortedResults = results.sorted(by: { $0.1 > $1.1} ) //sort by bill count, descending
sortedResults.forEach { print($0) } //print the results, customer will most bills at top
Notes the use of UUID().uuidString
#objc dynamic var bill_id = UUID().uuidString
creates unique primary keys for your objects. Eliminates dealing with indexing, uniqueness, incrementing etc.
I have been trying to create model like firebase database structure. I can able to create normal string, bool and int value but not able to do array and dictionary.
Here is my firebase structure screenshot:
Here i am trying to add groupMembers and to in model like firebase structure.
Here is my Model i tried to create with array and dictionary:
import Foundation
import RealmSwift
class RealmMessages: Object {
#objc dynamic var messageText : String?
#objc dynamic var sender: String?
let chatCreatedDateTimee = List<timeStampValue>()
#objc dynamic var chatId: String?
#objc dynamic var from: String?
#objc dynamic var groupMemberss : [String: String]!
let groupMemebersCount = RealmOptional<Int>()
#objc dynamic var task: Bool = false
#objc dynamic var to: Array = [String]()
}
class timeStampValue: Object {
let timestamp = RealmOptional<Int>()
}
Here is my contoller code: Trying to add value into realm database.
var dic : [String : String] = [:]
var cont = ["one", "two", "three"]
var oneVal = ["909090": "SELF", "808080": "Other"]
override func viewDidLoad() {
super.viewDidLoad()
let realm = try! Realm()
print("realm location:::\(String(describing: Realm.Configuration.defaultConfiguration.fileURL))")
let myMessage = RealmMessages()
myMessage.messageText = "Diva"
myMessage.sender = "yIvq1mQxjfZpjs1ybRTTlDOmUKV2"
let timevalue = timeStampValue()
timevalue.timestamp.value = 123123131
myMessage.chatId = "+918000080000"
myMessage.from = "+918000080000"
myMessage.groupMemberss = oneVal
myMessage.to = cont
try! realm.write {
realm.add(myMessage)
}
}
How to get groupMemberss and to structure in realm database like firebase. And how to create array and dictionary in realm
There are a number of solutions but here's two.
Assuming the data has been read in and the data from the groupMembers snapshot is sitting in a dictionary var that looks like this
let groupMembersDict = [
"919": "participant",
"111": "observer",
"222": "participant"
]
To store that in Realm, you can work with primitives and store each key and value in a separate List (think: Array) or you can leverage a managed Realm object and store those in a List.
If you want to keep the data within an object; here's what it would look like.
class GroupData: Object {
#objc dynamic var num = ""
#objc dynamic var type = ""
convenience init(withNum: String, andType: String) {
self.init()
self.num = withNum
self.type = andType
}
}
Here's the main object showing both options; either option 1: store the key value pairs in two arrays, or option 2: use the above GroupData object to store the key value pairs together
class Messages: Object {
#objc dynamic var messageText = ""
//option 1: two lists of primative strings that can be accessed like an array.
// downside is managing two lists
let groupNum = List<String>()
let groupType = List<String>()
//option 2: a list of members using another Realm object
let groupNumType = List<GroupData>()
}
And some code to create two messages, one of each type
let msg0 = Messages()
msg0.messageText = "My message"
for member in groupMembersDict {
msg0.groupNum.append( member.key )
msg0.groupType.append( member.value )
}
let msg1 = Messages()
msg1.messageText = "This message"
for member in groupMembersDict {
let aGroup = GroupData(withNum: member.key, andType: member.value)
msg1.groupNumType.append(aGroup)
}
store them in realm
realm.add(msg0)
realm.add(msg1)
read them both in an display the message from option 2. Option 1 would be just iterating over the arrays to print the group data
let messageResults = realm.objects(Messages.self)
for msg in messageResults {
print(msg.messageText)
for group in msg.groupNumType {
print(group.num, group.type)
}
}
Keep in mind that all managed properties must be primitives: NSString, NSDate, NSData, NSNumber or List, Results, RLMLinkingObjects, or subclasses of RLMObject like the GroupData shown above.
I am getting JSON data from server by api call in swift application.
So, I want to store that into Realm data base and again need to fetch to show in tableview.
I have no idea about Realm database, After, checked few forums, I got basic idea for creating Object class.
So, I have installed Realm pod file and imported that library to my classes.
My JSON data is
[{
"type": "story",
"story":
{
"author-name": "",
"headline": "Quotes ",
"summary": "Best quotes of Muhammad Ali",
"hero-image": "https://image”
}
},
{
"type": “Trending”,
"story":
{
"author-name": "",
"headline": "Quotes ",
"summary": "Best quotes of Muhammad Ali",
"hero-image": "https://image”
}
},
{
"type": “Technology”,
"story":
{
"author-name": "",
"headline": "Quotes ",
"summary": "Best quotes of Muhammad Ali",
"hero-image": "https://image”
}
},
{
"type": “Top”,
"story":
{
"author-name": "",
"headline": "Quotes ",
"summary": "Best quotes of Muhammad Ali",
"hero-image": "https://image”
}
}
]
And I have each type keyword has different model class saved data from api data to show in Tableview
like
let storyObj = StoryModule()
let trending = StoryModule()
let technology = StoryModule()
let stotopryObj1 = StoryModule()
and I am saving each key value for every type
if abc.type == "story" {
let storyObj = abc.story
storyObj.authorname = storyObj?.authorname
storyObj.heroimage = storyObj?.heroimage
storyObj.headline = storyObj?.headline
storyObj.summary = storyObj?.summary
self.treningStoriesList.append(storyObj)
}
It is same for remaining Trending, Top and Technology objects.
and the Realm module is
import RealmSwift
class DemoInfo: Object {
#objc dynamic var category = ""
let items = List<DemoList>()
}
class DemoList : Object {
#objc dynamic var authorName = ""
#objc dynamic var imageUrl = ""
#objc dynamic var summary = ""
#objc dynamic var headLine = ""
}
And In MainViewController class,
let realmDB = try! Realm()
But, Here I got struck, How to save those storyObj,technology,top, etc module data and fetch.
Can anyone suggest me?
If you want to add a realm object in your db, you must define a primary key for each realm object classes. So, you need to change your JSON file, after you can create your realm objects like this;
DemoObject.swift
import RealmSwift
class DemoObject: Object {
#objc dynamic var id: String = ""
#objc dynamic var type: String = ""
#objc dynamic var subObject: SubObject?
override static func primaryKey() -> String? {
return "id"
}
}
SubObject.swift
import RealmSwift
class SubObject: Object {
#objc dynamic var id: String = ""
#objc dynamic var authorName: String = ""
#objc dynamic var imageUrl: String = ""
#objc dynamic var summary: String = ""
#objc dynamic var headLine: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
Then, you can use these codes to add your db.
let realm = try! Realm()
let demo = DemoObject()
demo.id = "1"
let sub = SubObject()
sub.id = "1"
sub.authorName = "Author Name"
sub.headLine = "Head Line"
sub.summary = "image Url"
demo.subObject = sub
try! realm.write {
realm.add(demo, update: true)
}
Could someone point me in the right direction on how to solve this issue, please?
I am creating table cells with the values from the structure below. The cells are created and the data is displayed in the cells by the time they were created which works fine.
The issue is some of the cells have the same name and I have an individual id for each cell from the struc Data but I need the user to know which one of the duplicates was created first within the duplicates. Kind of like a sub-number.
For example: 1:apple -1 , 2:pear -1, 3:apple -2
1(position in all the cell) - Apple (name of the cell) - 1 (value based on how many cells are named apple)
The func idName() I created tells us how many occurrences of a name happens but how could I break this down so the data would display like above?
struct Data {
var id: Int
var name: String
var color: String
var time: TimeInterval
var sessionId: Int
var userId: Int
}
func idName () {
let idElement = elements //another variable is used to grab the Array
var counts: [String: Int] = [:]
var idValue: Int
for id in idElement {
counts[id.name] = (counts[id.name] ?? 0) + 1
for (key, value) in counts {
print("\(key) occurs \(value) time(s)")
}
}
}
"I need the user to know which one of the duplicates was created first."
How a bout adding a date to each item when it is created?
var date: Date = Date()
So you can sort them...
myObjects = myObjects.sorted(by: {
$0.date.compare($1.date) == .orderedDescending
})
Another way is to add a UUID, this will give you a unique Identifier to reference:
var uuid: UUID = UUID()
var someObjectID = myObject.uuid.uuidString
Update:
When an Element (your data struct) is created, you should be checking your array of elements prior to adding one of the same name, if one of the same name exists then you can increment a counter not stored as a property of the struct.
You can use map and Dictionary(_:uniquingKeysWith:).
to return an array of mapped elements (an array of your data structs).
let mappedElements = elements.map($0.name, 1)
then, count duplicates and create a dictionary containing the number of matching items.
let counts = Dictionary(mappedElements, uniquingKeysWith: +)
this will result in ["apple": 3, "pear": 2, "peach": 1] etc.
I Just added nameCounter property in Data which will indicate the occurrence of particular name. Like this -
struct Data1 {
var id: Int
var name: String
var nameCOunter: Int? = 1
init(id: Int, name: String) {
self.id = id
self.name = name
}
static func addTestData() ->[Data1] {
var arr = [Data1]()
let model = Data1(id: 1, name: "apple")
let model1 = Data1(id: 2, name: "peer")
let model2 = Data1(id: 3, name: "apple")
let model3 = Data1(id: 4, name: "orange")
let model4 = Data1(id: 5, name: "grape")
let model5 = Data1(id: 6, name: "peer")
let model6 = Data1(id: 7, name: "apple")
arr = [model,model1,model2,model3,model4,model5,model6]
return arr
}
}
func idName() {
let idElement = Data1.addTestData()
var countedElement = [Data1]()
var nameArr = [String]()
for var dataModel in idElement {
nameArr.append(dataModel.name)
let count = nameArr.filter{$0 == dataModel.name}.count
dataModel.nameCOunter = count
countedElement.append(dataModel)
}
print(countedElement)
}
I just started to saving data using Realm also using ObjectMapper. The problem I have is:
ObjectInList is inicially with id = 0 and symbol = "CA", after mapping it has different data like id = 0, symbol = "First Name, Last name".
Before let database begins I have stored data in profileDictionary variable, and after database.add(profileDictionary) the content of object List changed to initially data like
id = 0 and
symbol = "CA"
The corious thing is that variable testInt didn't change its value and is equal to 10.
Thanks for your advices!
class ObjectInList: Object, Mappable{
dynamic var id : Int = 0
dynamic var symbol : String = "CA"
convenience required init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
symbol <- map["symbol"]
}
}
class ProfileDictionaries: Object, Mappable{
dynamic var testInt : Int = 20
var states = List<ObjectInList>()
convenience required init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
// ObjectMapper does not support List<Object>() collections.
testInt = 10
var array: [ObjectInList] = []
array <- map["states"]
states.appendContentsOf(array)
}
}
SwiftEventBus.onMainThread(self, name: Constants.ServerResponse.PROFILE_DICTIONARIES_SUCCESS){
result in
let profileDictionary = result.object as? ProfileDictionaries ?? ProfileDictionaries()
self.removeDatabase()
do{
let database = try Realm()
try! database.write {
database.add(profileDictionary)
let localProfileDictionaries = database.objects(ProfileDictionaries)
}
catch let error as NSError{
print(error)
self.removeDatabase()
}
}
This post has helped me - Swift Realm: After writing transaction reference set to nil
we should use PO instead of P in LLDB debugger - the data is stored correctly!