สร้างคอลัมน์ใน fetchrequest ตามนิพจน์

สรุป

ฉันต้องการที่จะสร้างค่าใหม่ตามนิพจน์ใน fetchRequest จากเอนทิตี Core Data

การมีเอนทิตีที่มีคุณสมบัตินี้ (เวอร์ชันสั้น)

  Name              Attributes
[Course] -> {licenseStart, name, id}

ฉันต้องการสร้าง fetchResquest บนเอนทิตีนั้นและดึงพจนานุกรมที่มีคุณลักษณะทั้งหมดบวกอีกหนึ่งฐานของนิพจน์นี้:

licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? "Actually" : "Cooming Soon"

ดังนั้นผลลัพธ์จะเป็น:

[ "licenseStart":1,"id":3, "name":"A", "status":0, ...
  "licenseStart":2,"id":4, "name":"B", "status":0, ...
  "licenseStart":3,"id":6, "name":"B", "status":0, ...
  "licenseStart":4,"id":2, "name":"C", "status":0, ...
  "licenseStart":5,"id":1, "name":"D", "status":1, ...
  "licenseStart":6,"id":7, "name":"E", "status":1, ...
]

โพสต์

ฉันต้องการถามคำถามเกี่ยวกับปัญหาที่ฉันกำลังเผชิญอยู่ตั้งแต่สองสามวันแล้ว .. ทุกวิธีที่ฉันได้ลองลงเอยด้วยวิธีแก้ปัญหาที่ซับซ้อนมากขึ้น

ให้ฉันอธิบายสถานการณ์ให้คุณฟัง:

ฉันมี CoreData Model ที่มีเอนทิตีที่มีคุณลักษณะที่แตกต่างกัน หนึ่งในนั้นคือ การเริ่มต้นใบอนุญาต นี่คือหมายเลขประทับเวลายูนิกซ์ที่ใช้ในการพิจารณาว่าหลักสูตรจะเริ่มเมื่อใด

ดังนั้น เมื่อฉันต้องการเติมข้อมูลเพื่อสร้าง UITableView ด้วยหลักสูตร ฉันกำลังดึงข้อมูลทั้งหมดนี้จากฐานข้อมูลโดยไม่มีปัญหาเลย ฉันใช้แอตทริบิวต์ licenseStart นี้เพื่อคำนวณส่วนต่างๆ ตามสูตร นี่คือโค้ดสำหรับ UITableView

ตัวควบคุม UITableView

let entity = NSEntityDescription.entityForName("Course", inManagedObjectContext: managedObjectContext!)
        let req = NSFetchRequest()
        let predicate = sectionProtocol?.FRCPredicate
        let sort = [NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: true), NSSortDescriptor(key: "courseId", ascending: true)]
        req.entity = entity
        req.sortDescriptors = sort
        req.predicate = predicate
        /* NSFetchedResultsController initialization
        a 'nil' 'sectionNameKeyPath' generates a single section */
        var aFetchedResultsController = NSFetchedResultsController(fetchRequest: req, managedObjectContext: managedObjectContext!, sectionNameKeyPath:"dateStatus", cacheName: nil)

หลังจากนี้ผลลัพธ์ของฉันจะเป็น (ตัวอย่างเล็ก ๆ ) เช่นนี้

[ "licenseStart":1,"id":3, "name":"A",...
  "licenseStart":2,"id":4, "name":"B",...
  "licenseStart":3,"id":6, "name":"B",...
  "licenseStart":4,"id":2, "name":"C",...
  "licenseStart":5,"id":1, "name":"D",...
  "licenseStart":6,"id":7, "name":"E",
]

สิ่งที่ฉันจะมีใน UITableView จะเป็น:

[Actually] 
  [A]
  [B]
  [B]
  [C]
[Cooming Soon]
  [D]
  [E]

ดังนั้น สิ่งที่ฉันทำตอนนี้คือการใช้ licenseStart เพื่อสร้างสองส่วน อย่างไร ง่าย ๆ เพียงใช้แอตทริบิวต์ของโมเดลของฉันที่เรียกว่า dateStatus และสร้างผลลัพธ์ที่เป็นไปได้สองรายการโดยใช้ ใบอนุญาตเริ่มต้นดังนี้:

โมเดลคลาส

var dateStatus : Int {
        get {
            return licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? "Actually" : "Cooming Soon"
        }
 }

อีกครั้งใน UITableView ของฉัน ฉันใช้วิธีการมอบหมาย dataSource เพื่อเติมแถวและส่วนต่างๆ

ปัญหา

จนถึงขณะนี้ทุกอย่างทำงานได้สมบูรณ์แบบ แต่ตอนนี้ ฉันต้องการเพิ่มตัวเลือกการเรียงลำดับที่ให้ผู้ใช้เรียงลำดับผลลัพธ์จากน้อยไปหามากหรือจากมากไปน้อย ปัญหาคือเมื่อฉันเรียงลำดับ กล่าวคือ เรียงลำดับจากน้อยไปมากโดยใช้ตัวเลือกการเรียงลำดับอื่น:

let entity = NSEntityDescription.entityForName("Course", inManagedObjectContext: managedObjectContext!)
        let req = NSFetchRequest()
        let predicate = sectionProtocol?.FRCPredicate
        let sort = [NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: false), NSSortDescriptor(key: "courseId", ascending: false)]
        req.entity = entity
        req.sortDescriptors = sort
        req.predicate = predicate
        /* NSFetchedResultsController initialization
        a 'nil' 'sectionNameKeyPath' generates a single section */
        var aFetchedResultsController = NSFetchedResultsController(fetchRequest: req, managedObjectContext: managedObjectContext!, sectionNameKeyPath:"dateStatus", cacheName: nil)

เนื่องจากวิธีที่ฉันกำลังสร้างส่วนต่าง ๆ ตาม LicenseStart ผลลัพธ์ของการดึงข้อมูลนี้จะเหมือนกับเมื่อก่อนทุกประการ:

[ "licenseStart":1,"id":3, "name":"A",...
  "licenseStart":2,"id":4, "name":"B",...
  "licenseStart":3,"id":6, "name":"B",...
  "licenseStart":4,"id":2, "name":"C",...
  "licenseStart":5,"id":1, "name":"D",...
  "licenseStart":6,"id":7, "name":"E",
]

สิ่งที่ฉันจะมีใน UITableView จะเป็น:

[Actually] 
  [A]
  [B]
  [B]
  [C]
[Coming Soon]
  [D]
  [E]

ทำไม เนื่องจาก LicenseStart นั้นแตกต่างกันทั้งหมด และใช้องค์ประกอบแรกนี้ใน NSSortDescriptors เพื่อดึงข้อมูล .. นี่ไม่ใช่พฤติกรรมที่ฉันต้องการ สิ่งที่ฉันต้องการคือการให้ส่วนต่าง ๆ อยู่ในลำดับเดียวกันกับเมื่อก่อน แต่องค์ประกอบภายในถูกจัดเรียงต่างกัน

i.e:

[Actually] 
  [C]
  [B]
  [B]
  [A]
[Coming Soon]
  [E]
  [D]

วิธีแก้ปัญหาที่เป็นไปได้

สิ่งที่ฉันคิดว่าคือการสร้างคอลัมน์ใหม่ในระหว่างการดึงข้อมูล หากฉันสามารถประเมินนิพจน์นี้ licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? 0 : 1 ในขณะที่ดึงข้อมูลได้ ฉันจะได้คอลัมน์ใหม่ที่มี 0 และ 1 เช่นนี้ในผลลัพธ์:

[ "licenseStart":1,"id":3, "name":"A", "status":0, ...
  "licenseStart":2,"id":4, "name":"B", "status":0, ...
  "licenseStart":3,"id":6, "name":"B", "status":0, ...
  "licenseStart":4,"id":2, "name":"C", "status":0, ...
  "licenseStart":5,"id":1, "name":"D", "status":1, ...
  "licenseStart":6,"id":7, "name":"E", "status":1, ...
]

และตอนนี้ฉันสามารถใช้สถานะนี้เพื่อสร้างส่วนต่างๆ ใน ​​NSSortDescriptors [NSSortDescriptor(key: "status", ascending: true),NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: true), NSSortDescriptor(key: "courseId", ascending: true)] สำหรับจากน้อยไปหามากและ [NSSortDescriptor(key: "status", ascending: true),NSSortDescriptor(key: "licenseStart", ascending: false), NSSortDescriptor(key: "name", ascending: false), NSSortDescriptor(key: "courseId", ascending: false)] สำหรับจากมากไปน้อย

นี่จะทำให้ฉันมีพฤติกรรมที่ฉันต้องการ .. แต่ฉันไม่สามารถหาวิธีที่ดีและสะอาดในการทำเช่นนี้ได้ ฉันพยายามทำสิ่งนี้โดยใช้ NSExpressions แต่ฉันไม่สามารถใช้ น้อยกว่า กับการดึงข้อมูล CoreData ได้

นอกจากนี้ วิธีแก้ปัญหาที่เป็นไปได้คือการสร้างคอลัมน์ใหม่นี้ในแบบจำลองข้อมูลของฉันและสร้างค่านี้เมื่อบันทึกข้อมูล แต่นี่ไม่ใช่แนวทางที่ถูกต้องเนื่องจากค่า LicenseStart นี้จะต้องเปรียบเทียบกับการประทับเวลาปัจจุบันทุกครั้งที่ผู้ใช้เติมข้อมูลในตารางนี้

ฉันกำลังถามว่ามีใครมีความคิดสั้น ๆ หรือมีวิธีแก้ปัญหาที่ชาญฉลาดสำหรับปัญหานี้หรือไม่

ใน MySQL หรือ SQLite นั้นง่ายต่อการสร้างคอลัมน์ใหม่ตามค่าหรือนิพจน์บางอย่าง แต่ใน coredata ฉันเป็นมือใหม่ ดังนั้นฉันจึงไม่เห็นวิธีที่ชัดเจนในการทำเช่นนี้

ขอบคุณมาก.


person neteot    schedule 16.03.2017    source แหล่งที่มา
comment
นั่นเป็นคำถามที่มากเกินไป คุณช่วยกรุณาใส่บทสรุปไว้ที่ใดที่หนึ่ง (อาจอยู่ด้านบนสุด) ที่แสดงให้เห็นผลลัพธ์สุดท้ายที่คุณต้องการบรรลุ และสิ่งที่คุณได้รับจริง ๆ สิ่งนี้ดูเหมือนเป็นการไหลเวียนของจิตสำนึก ซึ่งมีประโยชน์ในการทำให้ความคิดของคุณแย่ลง แต่ยากที่จะเห็นว่าปัญหาที่แท้จริงคืออะไร   -  person Ashley Mills    schedule 16.03.2017
comment
เพิ่มบทสรุปแล้ว ขอบคุณสำหรับคำติชม @AshleyMills :)   -  person neteot    schedule 16.03.2017


คำตอบ (2)


ฉันมีสถานการณ์ที่ฉันต้องการแบ่งองค์ประกอบบางส่วนออกเพื่อให้อยู่ด้านบนสุดของรายการโดยไม่ขึ้นอยู่กับคุณลักษณะ core-data ฉันดึงข้อมูลด้วย fetchedResultsController แล้วดำเนินการเพียงครั้งเดียวเพื่อใส่องค์ประกอบลงในอาร์เรย์สองตัว (O(n)) โดยรักษาการเรียงลำดับตามการเรียงลำดับดั้งเดิม เมื่อองค์ประกอบต่างๆ เปลี่ยนไป ฉันต้องหามันในอาร์เรย์เพื่อเปลี่ยนมัน แต่มันก็ไม่ได้ยากอย่างที่คิด

วิธีแก้ปัญหาที่ง่ายกว่าคือใช้ fetchedResultsController สองตัว

person Jon Rose    schedule 16.03.2017

พยายามอย่าคิดว่า CoreData เป็นฐานข้อมูล sql เชิงสัมพันธ์ปกติซึ่งคุณสามารถสร้างคอลัมน์ในการสืบค้นของคุณได้

ดูเหมือนว่าสิ่งที่คุณต้องการคือเพียงเพิ่มแอตทริบิวต์ status อื่นให้กับเอนทิตีของคุณ ทุกครั้งที่คุณอัปเดต licenseStart เพียงตั้งค่า status

หากคุณใช้โคเดเจนอยู่เช่นกัน

extension Course {
    func updateLicenseStart(date: Date) {
        licenseStart = date
        status = licenseStart.doubleValue / 1000 < Date().timeIntervalSince1970 ? 0 : 1
    } 
}

หรือเปลี่ยนชื่อแอตทริบิวต์ที่ซ่อนอยู่...

extension Course {
    var licenseStart {
        set {
            _licenseStart = newValue 
            status = licenseStart.doubleValue / 1000 < Date().timeIntervalSince1970 ? 0 : 1
        }
        get { return _licenseStart }
    } 
}

หากคุณยังคงรักษาคลาสโมเดลของคุณเอง คุณไม่สามารถเพิ่มผู้สังเกตการณ์คุณสมบัติให้กับ @NSManaged var ได้ แต่คุณสามารถลบ @NSManaged และแทนที่ set และ get...

public var licenseStart: Date {
    set {
        willChangeValueForKey("licenseStart")
        setPrimitiveValue(newValue, forKey: "licenseStart")
        status = licenseStart.doubleValue / 1000 < Date().timeIntervalSince1970 ? 0 : 1
        didChangeValueForKey("licenseStart")
    }
    get {
        willAccessValueForKey("licenseStart")
        let date = primitiveValueForKey("licenseStart") as? Date
        didAccessValueForKey("licenseStart")
        return date
    }
}
person Ashley Mills    schedule 16.03.2017
comment
ใช่ ฉันคิดถึงวิธีแก้ปัญหานั้นตั้งแต่ครั้งแรก แต่ประเด็นก็คือเมื่อคุณดึงข้อมูลจากเซิร์ฟเวอร์และบันทึกลงใน Core Data สถานะจะขึ้นอยู่กับช่วงเวลานั้น แต่สิ่งที่ฉันต้องการคือการมีสถานะตามการประทับเวลาปัจจุบันทุกครั้งที่ผู้ใช้เติม tableview และ ฉันส่ง fetchResultsController.fetchedObjects ไปยังวิธีการมอบสิทธิ์ tableview - person neteot; 16.03.2017
comment
เอ่อ ฉันคิดว่าฉันเข้าใจนะ เรากำลังพูดถึงกี่แถว? ต้องแม่นยำแค่ไหน? คุณสามารถอัปเดตเป็นชุดในการเริ่มต้นแอป / เบื้องหน้า / ก่อนที่ตารางจะถูกเติมและตั้งค่าสถานะในแต่ละแถวได้หรือไม่ - person Ashley Mills; 16.03.2017
comment
ไม่ใช่ปัญหาเรื่องปริมาณใช่ไหม แต่แอปมีฟังก์ชันการทำงานแบบออฟไลน์ที่สมบูรณ์ซึ่งทำให้ผู้ใช้สามารถทำหลักสูตรแบบออฟไลน์ได้ (ดาวน์โหลดเนื้อหาทั้งหมดของหลักสูตร) ​​จากนั้นเมื่อการเชื่อมต่อกลับมาทำงานอีกครั้ง ระบบจะเปิดฟังก์ชันอัปเดต ดังนั้นบางทีอาจมีส่วนที่ไม่สอดคล้องกันเป็นเวลาหลายวัน (อาจจะพูดว่า เร็วๆ นี้ เมื่อเป็น ปัจจุบัน) :S ฉันสามารถสร้าง NSExpression ด้วย propertiesToFetch และสร้างค่าใหม่ได้ (แต่ฉันไม่สามารถใช้ฟังก์ชันที่ซับซ้อนเหมือนข้างบน licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? 0 : 1 ได้ - person neteot; 16.03.2017