How to insert many records in Realm with Swift?

0

I am trying to perform a massive data insertion in Realm, but it is doing it very slowly.

this is my code:

            let realm = try! Realm()


            let group = GroupRealm()

            group.id = 1
            group.name = "test"

            try! realm.write { 
                realm.add(group)
            }

            var product = [String:AnyObject]()
            product["id"] = "" as AnyObject!
            product["code"] = ("") as AnyObject!
            product["detail"] = ("") as AnyObject!
            product["onHand"] = (0) as AnyObject!
            product["price"] = (0) as AnyObject!
            product["isSerialized"] = (0) as AnyObject!
            product["isLotNumber"] = (0) as AnyObject!

            var groupProduct = [String:AnyObject]()
            groupProduct["group"] = group as AnyObject!

            for r in 0..<300 {
                realm.beginWrite()
                for i in 0..<100 {
                    product["id"] = "\(i)-\(r)" as AnyObject!
                    realm.create(ProductRealm.self, value: product, update: true)

                    groupProduct["id"] = "\(i)-\(r)" as AnyObject!
                    groupProduct["product"] = product as AnyObject!
                    realm.create(GroupProductRealm.self, value: groupProduct, update: true)
                }
                try! realm.commitWrite()
            }

the objects that I am using are these:

class ProductRealm: Object {
    public dynamic var id: String = ""
    public dynamic var code: String = ""
    public dynamic var detail: String = ""
    public dynamic var onHand: Int = 0
    public dynamic var price: Double = 0
    public dynamic var isSerialized: Int = 0
    public dynamic var isLotNumber: Int = 0
    override static func primaryKey() -> String? {
        return "id"
    }
}

class GroupProductRealm: Object {
    public dynamic var id = ""
    public dynamic var group: GroupRealm!
    public dynamic var product: ProductRealm!
    override static func primaryKey() -> String? {
        return "id"
    }
}
class GroupRealm: Object {
    public dynamic var id = 1
    public dynamic var name = ""
    public dynamic var update = Date()
    override static func primaryKey() -> String? {
        return "id"
    }
}

The tests on my iphone 5s to insert 30000 records take more than 30 seconds, while on an old project that I have with sqlite it takes around 16 seconds.

I've read that realm is much faster in this than sqlite so I know it's my mistake, but I do not hit the D:

    
asked by Sergio Castro 09.02.2017 в 22:11
source

2 answers

0

Well after a lot of tests I found a way to reduce the time to only 1-2 seconds of data creation to insert and 7-9 seconds of insertion (it can be lowered more if less records are shown, for example if I show when 1000 inserts is reduced to 4-6 seconds).

The first thing I have to say, is that the objects were wrong, although in sqlite many-to-many relationships are made with an auxiliary table, here it is completely redundant, since one can insert the same record several times in a array, so the objects are like this:

   class ProductRealm: Object {
        public dynamic var id: String = ""
        public dynamic var code: String = ""
        public dynamic var detail: String = ""
        public dynamic var onHand: Int = 0
        public dynamic var price: Double = 0
        public dynamic var isSerialized: Int = 0
        public dynamic var isLotNumber: Int = 0
        override static func primaryKey() -> String? {
            return "id"
        }
    }

   class GroupRealm: Object {
        public dynamic var id = 1
        public dynamic var name = ""
        public dynamic var update = Date()
        let products = List<ProductRealm>()
        override static func primaryKey() -> String? {
            return "id"
        }
    }

This means that unlike sqlite it is only necessary to insert the data in a single "table" and the references in the other "table" which makes it much simpler and improves the performance a little, however the Insertion times were still very high.

After many tests it occurred to me to use the jsonObject to insert, since the idea is to bring these records from a server, and to my surprise this greatly reduced the insertion time compared to the dictionaries, so the code final would look like this:

//
//  ViewController.swift
//  TestRealm
//
//  Created by Sergio Castro on 10/02/17.
//  Copyright © 2017 BasTechSoluctions. All rights reserved.
//

import UIKit
import RealmSwift

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Mostrar la ubicación del proyecto para ubicar la base de datos
        print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.absoluteString)


        DispatchQueue(label: "background").async { //Abrir un nuevo hilo para insertar de forma asíncrona
            autoreleasepool {

                let date1 = Date() // Inicia el conteo del tiempo

                let realm = try! Realm() // Instanciar realm en el hilo nuevo

                let products = realm.objects(ProductRealm.self) //Crear una lista de todos los productos

                //Crear grupo
                let group = GroupRealm()
                group.id = 1
                group.name = "test"

                try! realm.write { //Insertar/Reemplazar grupo, sin productos
                    realm.create(GroupRealm.self, value: group, update: true)
                }

                ///Crear json
                //Primero creo un String, donde ingreso todos los datos en formato [{},{},...]
                var stringJson = "["
                for i in 0..<30000 {
                    if(i > 0){ stringJson.append(",") }
                    let dataOrigin = "{\"id\": \"\(i)\", \"code\": \"\", \"detail\": \"\", \"onHand\": 0, \"price\": 0, \"isSerialized\": 0, \"isLotNumber\": 0}"
                    stringJson.append("\(dataOrigin)")
                }
                stringJson.append("]")

                //Convertir el String en Datos y luego pasarlo a jsonObject (esto para simular el proceso de descarga de datos)
                let data = stringJson.data(using: .utf8)!
                let jsonP = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [Any]

                //Mostrar el tiempo empleado para la creación del json en consola, el tiempo está en segundos por defecto
                let date2 = Date()
                var time = date2.timeIntervalSince(date1)
                print("Json creation in seconds \(time)")

                //Recorrer el json en grupos de 100, tantas veces haga falta para recorrer por completo el json, en este caso 300 veces
                let limit = 100
                for c in 0..<(jsonP.count / limit){
                    //Insertar/Reemplazar los grupos de datos
                    realm.beginWrite()
                    for i in 0..<limit{
                        let row = jsonP[i + (c*limit)]
                        realm.create(ProductRealm.self, value: row, update: true)
                    }
                    try! realm.commitWrite()
                    //Mostrar en consola la cantidad de procesos realizados vs la cantidad de registros actuales
                    print("\((c+1)*limit) / \(products.count)")
                }

                group.products.append(objectsIn: products) //Agregar los productos al grupo, aquí se pueden poner filtros

                //Actualizar el grupo, para que el array products tenga una referencia a sus productos correspondientes
                try! realm.write {
                    realm.create(GroupRealm.self, value: group, update: true)
                }

                // Calcular el tiempo de inserción y mostrarlo en consola
                let date3 = Date()
                time = date3.timeIntervalSince(date2)
                print("Insert time in seconds \(time)")

                // Calcular el tiempo total del calculo
                time = date3.timeIntervalSince(date1)
                print("total time in seconds \(time)")

            }
        }
    }
}


class ProductRealm: Object {
    public dynamic var id: String = ""
    public dynamic var code: String = ""
    public dynamic var detail: String = ""
    public dynamic var onHand: Int = 0
    public dynamic var price: Double = 0
    public dynamic var isSerialized: Int = 0
    public dynamic var isLotNumber: Int = 0
    override static func primaryKey() -> String? {
        return "id"
    }
}

class GroupRealm: Object {
    public dynamic var id = 1
    public dynamic var name = ""
    public dynamic var update = Date()
    let products = List<ProductRealm>()
    override static func primaryKey() -> String? {
        return "id"
    }
}

Well, I'll leave it that way, but if you find a way to optimize it, I'd really appreciate it.

    
answered by 11.02.2017 / 00:34
source
0

What is mainly causing you to take longer to insert it is to open and close several write transactions within for .

This would be an inefficient or incorrect way:

let realm = try Realm()

for i in 0..<300 {

    try realm.write {

        // Inserts
    }

}

Instead, this one would be the most efficient and correct:

let realm = try Realm()

try realm.write {

    for i in 0..<300 {

        // Inserts

    }

}

For your particular case, I would do the following:

let realm = try! Realm()

let group = GroupRealm()

group.id = 1
group.name = "test"

realm.beginWrite() // -------------- Inicio de escritura

realm.add(group)

var product = [String:AnyObject]()
product["id"] = "" as AnyObject!
product["code"] = ("") as AnyObject!
product["detail"] = ("") as AnyObject!
product["onHand"] = (0) as AnyObject!
product["price"] = (0) as AnyObject!
product["isSerialized"] = (0) as AnyObject!
product["isLotNumber"] = (0) as AnyObject!

var groupProduct = [String:AnyObject]()
groupProduct["group"] = group as AnyObject!

for r in 0..<300 {

    for i in 0..<100 {
        product["id"] = "\(i)-\(r)" as AnyObject!
        realm.create(ProductRealm.self, value: product, update: true)

        groupProduct["id"] = "\(i)-\(r)" as AnyObject!
        groupProduct["product"] = product as AnyObject!
        realm.create(GroupProductRealm.self, value: groupProduct, update: true)
    }

}

try! realm.commitWrite() // -------------- Fin de escritura
    
answered by 11.02.2017 в 11:25