# Overview

Inset (<https://github.com/slatepowered/inset>) is a **work in progress** advanced Java 8 datastore framework. It provides straightforward, flexible and fast cached access to databases through, for example, declaring objects.

It currently has support for MongoDB as a datasource and reflection (from data classes) as a data codec provider.

### Installation - Gradle

The library is provided for Maven and Gradle through Jitpack, so begin by adding Jitpack as a repository:

```gradle
repositories {
    maven { url = "https://jitpack.io" }
}
```

Then, add Inset as a dependency using as a version either a version tag you can find on the releases page (<https://github.com/slatepowered/inset/releases>) or using a commmit ID.

```gradle
def INSET_VERSION = "<commit id/version>"

dependencies {
    // inset: core module, provides the reflective data objects
    // and all the base classes
    implementation "com.github.slatepowered.inset:inset-core:${INSET_VERSION}"
    // inset: datasource implementation of your choice, in this case mongodb
    implementation "com.github.slatepowered.inset:inset-mongodb:${INSET_VERSION}"
}
```

### Example

```java
// our player/user stats class, keyed by uuid, we
// want to save and load using a datastore
@ToString
static class Stats {
    @Key
    protected UUID uuid;
    protected Long balance = 0;
    protected Integer kills = 0;
    protected Integer deaths = 0;
}

// [*] this will later be used as a projection for bulk queries
//     where we only want to know and work with the balance
//     if you want projections on cached items to be fast you will want
//     to implement the interface in your value type (in this case Stats)
//     and it will just return the Stats type as it can be cast.
interface PartialStats {
    @Key
    UUID uuid();
    Long balance();
}

void example() {
    // create the common data manager instance
    // which manages codecs, scheduling, etc
    DataManager dataManager = DataManager.builder()
            .executorService(ForkJoinPool.commonPool())
            .codecRegistry(new CodecRegistry(ReflectiveCodecFactory.builder().build()))
            .build();

    // connect to the mongodb database and wrap
    // it using a MongoDataSource so it is usable
    MongoDataSource dataSource = MongoDataSource.builder(dataManager)
            .connect(MONGO_CONNECTION_STRING, "test-database")
            .build();

    // get the collection we want to use as a DataTable
    DataTable table = dataSource.table("test-collection");

    // create the datastore with the UUID key type and Stats value type
    Datastore<UUID, Stats> datastore = dataManager.datastore(UUID.class, Stats.class)
            .sourceTable(table)
            .dataCache(DataCache.doubleBackedConcurrent()) // ConcurrentHashMap & ConcurrentLinkedQueue-backed cache
            .build();

    /* Perform a query by key */
    final UUID key = new UUID(123456, 7891011);
    datastore.findOne(key) // -> FindOperation<UUID, Stats>
            .thenDefaultIfAbsent() // Create a default Stats instance if it was not found
            .thenFetchIfCached()   // Reload the data if it was found in the cache
            .thenUse(item /* -> DataItem<UUID, Stats> */ -> {
                // get the Stats object from the data item
                Stats stats = item.get();
                stats.kills++;

                item.saveAsync().whenComplete((unused, ex) -> {
                    if (ex != null) {
                        System.err.println("Failed to save stats for " + key);
                        ex.printStackTrace();
                        return;
                    }

                    System.out.println("Successfully saved incremented stats for " + key);
                });
            })
            .exceptionally(result /* -> FindOperation<UUID, Stats> */ -> {
                // usually you want to check the error type but im lazy
                result.errorAs(Throwable.class).printStackTrace();
            });

    /* Perform a query with field constraints */
    datastore.findOne(Query.builder()
            .greaterOrEq("kills", 5)
            .build()) /* -> FindOperation<UUID, Stats> */
            .then(result /* -> FindOperation<UUID, Stats> */ -> {
                if (!result.isPresent()) {
                    System.err.println("Could not find a profile with at least 5 kills");
                    return;
                }

                // do shit...

                // uncache the item
                result.item().dispose();
            });

    /* Perform a bulk query */
    datastore.findAll(Query.builder()
            .greaterOrEq("kills", 3)
            .build())
            .then(result /* FindAllOperation<UUID, Stats> */ -> {
                System.out.println("Best players!!! (3+ kills)");
                result.stream() /* -> Stream<PartialItem<UUID, Stats>> */
                        .map(PartialItem::fetch /* -> DataItem<UUID, Stats> */)
                        .map(DataItem::get /* -> Stats */)
                        .forEach(System.out::println);
            })
            .exceptionally(result /* -> FindAllOperation<UUID, Stats> */ -> {
                // again, usually you want to check the error type but im lazy
                result.errorAs(Throwable.class).printStackTrace();
            });
            
    /* Perform a bulk query with partial projections */
    datastore.findAll(Query.all(), 
            // enabling caches ensures you have the latest local changes
            // returned on the query but can hurt performance in large
            // (primarily in sorted) queries. if this is disabled (default),
            // it retrieves the order solely from the database.
            FindAllOperation.Options.builder().useCaches(true).build()
    ).then(result -> result
            // only work with and retrieve balance from the db,
            // this has no effect on cached items
            .projection(PartialStats.class)
            // sort by balance in descending order
            .sort(Sorting.builder().descend("balance").build())
            // stream the found items
            .stream()
            // create a PartialStats instance from the partial data
            .map(partialItem -> partialItem.project(PartialStats.class))
            .forEach(partialStats -> 
                    System.out.println("UUID " + partialStats.uuid() + " has $" + partialStats.balance()
    ).exceptionally(result /* -> FindAllOperation<UUID, Stats> */ -> {
        // again again, usually you want to check the error type but im lazy
        result.errorAs(Throwable.class).printStackTrace();
    });

    // await completion of all the queries
    // before exiting this function
    dataManager.await();
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://orbyfied.gitbook.io/inset/overview.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
