Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best way of searching entity. #95

Closed
JARMourato opened this issue Feb 27, 2016 · 10 comments
Closed

Best way of searching entity. #95

JARMourato opened this issue Feb 27, 2016 · 10 comments
Assignees

Comments

@JARMourato
Copy link

Hi, first off, I would like to say that I love the concept. I've been using it for a few days and I would like to ask if there is an optimized way that you'd recommend for searching for a specific entity with a specific property. I've been time profiling the app I'm using and for comparing 350 entities that already exist in the Graph, its taking roughly 10 seconds.

@daniel-jonathan
Copy link
Member

Thank you! We have big ideas for Graph :)

Hmmm... 10 seconds seems really long. Can you share more about your setup and how you are currently searching?

To search properties fastest, you can pull up the Entities with a given property and value, then filter further. Or you can choose the Type to search by and then filter. For example:

let a: Array<Entity> = graph.searchForEntity(types: ["Type"]).filter { (entity: Entity) -> Bool in
    return "Daniel" == entity["name"] as? String
}

let b: Array<Entity> = graph.searchForEntity(properties: [("name", "Daniel")]).filter { (entity: Entity) -> Bool in
    return "Type" == entity.type
}

The search could be combined and more complex. Mainly, it depends on your model and what you are trying to get out of the search.

@JARMourato
Copy link
Author

hey, thanks for the answer. The model I'm using is rather extensive, but for the purpose of the search only one property matters. Imagine an entity of type "Entry" which has a property named "unique". Imagine I have entered 350 unique entries. If I try to re-enter those same 350 entries, I have to check each of the entries I'm trying to add against the whole data base. I was looking for a way to retrieve the entity of type "entry" with the property "unique" = "something" with a single call (without using filter).

Or at least the optimized way of doing that. Thanks for the help

@daniel-jonathan
Copy link
Member

Graph doesn't support user made indices with unique keys yet. It does though, generate a unique key that can be used externally, or I can show you how to use Algorithm, which is a data structure library to achieve what you are looking for in log(n) time.

A unique key feature is something I would be working on very soon.

@JARMourato
Copy link
Author

Okay :) How would algorithm apply in this case ? I would run it over the graph ?

@daniel-jonathan
Copy link
Member

In Algorithm, there is a data structure called SortedDictionary. It has a key value pair, where the keys are kept sorted and the value is of any type you set. Using that data structure, below is a class called Index that monitors changes in Graph and always manages the collection of Entities that are being uniquely keyed. Keys in a SortedDictionary are kept unique by default.

import Graph
import Algorithm

class Index<Key : Comparable where Key : Hashable>: GraphDelegate {
    private var graph: Graph!
    private(set) var dictionary: SortedDictionary<Key, Entity>!

    init() {
        prepareGraph()
        prepareDictionary()
    }

    func find(key: Key) -> Entity? {
        return dictionary.findValueForKey(key)
    }

    private func prepareGraph() {
        graph = Graph()
        graph.delegate = self
        graph.watchForEntity(properties: ["unique"])
    }

    private func prepareDictionary() {
        dictionary = SortedDictionary<Key, Entity>()
        for entity in graph.searchForEntity(properties: [("unique", nil)]) {
            if let key: Key = entity["unique"] as? Key {
                dictionary.insert(key, value: entity)
            }
        }
    }

    @objc func graphDidInsertEntityProperty(graph: Graph, entity: Entity, property: String, value: AnyObject) {
        if let v: Key = value as? Key {
            dictionary.insert(v, value: entity)
        }
    }

    @objc func graphDidUpdateEntityProperty(graph: Graph, entity: Entity, property: String, value: AnyObject) {
        // If you need to handle key updates.
    }

    @objc func graphDidDeleteEntityProperty(graph: Graph, entity: Entity, property: String, value: AnyObject) {
        if let v: Key = value as? Key {
            dictionary.removeValueForKeys(v)
        }
    }
}

To use it, you can do:

let graph: Graph = Graph()

let keys: Array<String> = ["Jon", "Frank", "Sarah", "Susan"]
let index: Index<String> = Index<String>()

for key in keys {
        var entity: Entity? = index.find(key)

    if nil == entity {
        entity = Entity(type: "Entry")
        entity!["unique"] = key
    }
}

graph.save()

Using this approach you are only ever doing one search and one save. The comparison is done in log(n) because underlying the SortedDictionary is a RedBlackTree. If you insert new "Entry" type Entities, they will automatically be added to the Index as the changes in Graph are being watched through the watch API.

@JARMourato
Copy link
Author

Awesome!Many Thanks, this is helps me a lot. I Have only a minor doubt, why do you search for key unique with "nil" value in prepare for dictionary ?

@daniel-jonathan
Copy link
Member

oh because when the value is set to nil, it searches all value types. You could do this as well,

graph.searchForEntity(properties: [("unique", "*")])
graph.searchForEntity(properties: [(key: "unique", value: "*")])

where you are saying it is a String and that you wildcard the search. The nil, allows it to be any type. So if you know your keys are String then the "*" is good, otherwise nil is better. The nil is also faster, as it has less comparisons to do. The "*" card still has to check String type and such.

@daniel-jonathan
Copy link
Member

I am going to close this issue for now, if you have any further questions, please reopen or create a new issue.

Let me know how it worked out.

All the best!

@JARMourato
Copy link
Author

Once I've implemented the solution I'll post my results :) thank for the help

@daniel-jonathan
Copy link
Member

There are some tweaks you can make to use the solution across the board. If you need any help, let us know :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants