Skip to content

Fluent search

Today we're excited to release a simplified search experience via our SDKs, that we're calling "fluent search".

It is available today in:

  • Java SDK v1.1.0+
  • Python SDK v0.6.2+

Why?

If you are super search-savvy, you may already be familiar with Elastic and its power and flexibility. However, we understand not everyone has these skills — and for many of us having a basic and easy-to-use search mechanism can be desirable.

That's where fluent search comes in!

It's meant to be easy to learn, quick to do the basics, but also capable enough to address fairly powerful search use cases.

How does it work?

Fluent search is just a set of classes and methods over the same back-end APIs. So your queries will run as fast as ever, but defining them should now require less effort.

It'll probably help to look at a side-by-side comparison. Let's use one of our existing examples to illustrate. In this scenario, imagine you are searching for all assets that are marked as verified but are missing a description (suggesting they are in fact incomplete):

Before, without fluent search
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import static com.atlan.util.QueryFactory.*; // (1)

AtlanClient client = Atlan.getDefaultClient();
client.assets.all()
    .filter(where(KeywordFields.CERTIFICATE_STATUS).eq(CertificateStatus.VERIFIED)) // (2)
    .exclude(where(TextFields.DESCRIPTION).present())
    .exclude(where(TextFields.USER_DESCRIPTION).present())
    .attribute("ownerUsers") // (3)
    .attribute("ownerGroups")
    .stream()
    .forEach(a -> {
        log.info("Asset: {}", a);
    });
  1. You used to need to static-import a QueryFactory to get access to various helper methods like where.
  2. Adding a condition to a query was then fairly complex task of combining these helper methods with enumerations, where you needed to know how a field was indexed (keyword vs text vs numeric) to find it and add it.
  3. You still had to know the exact names, spelling and capitalization of attributes to ensure they were included in each result.
Now, with fluent search
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
AtlanClient client = Atlan.getDefaultClient();
client.assets.select()
    .where(Asset.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED)) // (1)
    .whereNot(Asset.DESCRIPTION.hasAnyValue())
    .whereNot(Asset.USER_DESCRIPTION.hasAnyValue())
    .includeOnResults(Asset.OWNER_USERS) // (2)
    .includeOnResults(Asset.OWNER_GROUPS)
    .stream()
    .forEach(a -> {
        log.info("Asset: {}", a);
    });
  1. Now you no longer need the QueryFactory — you can directly chain where and whereNot clauses into the query. Even better, the attributes now self-restrict how you can search them based on their type — you no longer need to know how the fields are indexed.
  2. You no longer need to rely on typo-prone strings to list the attributes to include on results, either!
Before, without fluent search
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.enums import CertificateStatus
from pyatlan.model.search import DSL, IndexSearchRequest, Term, Exists

client = AtlanClient()
verified_query = Term.with_certificate_status(CertificateStatus.VERIFIED)  # (1)
description_query = Exists.with_description()  # (2)
user_description_query = Exists.with_user_description()
compound_query = verified_query + ~description_query + ~user_description_query  # (3)
dsl = DSL(query=compound_query)  # (4)
find_assets = IndexSearchRequest(
    dsl=dsl,  # (5)
    attributes=[
        "ownerUsers",  # (6)
        "ownerGroups"
    ]
)
results = client.asset.search(find_assets)  # (7)
for asset in results:
    print(asset)
  1. Before, you had to know which kind of Elastic query to use (in this case, a Term query).
  2. You were also limited to searching only those attributes exposed by these classes.
  3. You then had to know how to combine your queries (when to use a +, prefix with a ~, and so on).
  4. You then had to bundle all of that combined information into a DSL...
  5. ... and then bundle that DSL into a search request.
  6. If you wanted to ensure particular attributes were included in each result, you had to rely on knowing the exact spelling and capitalization of the attributes.
  7. Only then could you actually run your search and iterate through the results.
Now, with fluent search
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import Asset
from pyatlan.model.enums import CertificateStatus
from pyatlan.model.fluent_search import FluentSearch

client = AtlanClient()
request = (
    FluentSearch()
    .where(Asset.CERTIFICATE_STATUS.eq(CertificateStatus.VERIFIED.value))  # (1)
    .where_not(Asset.DESCRIPTION.has_any_value())  # (2)
    .where_not(Asset.USER_DESCRIPTION.has_any_value())
    .include_on_results(Asset.OWNER_USERS)  # (3)
    .include_on_results(Asset.OWNER_GROUPS)
).to_request()  # (4)
for result in client.asset.search(request):  # (5)
    print(result)
  1. Now you can compose queries against any searchable attribute in Atlan. The operations you can carry out against each attribute are determined for you based on the kind of attribute you're searching.
  2. Different methods make it clear whether your condition will be used to include (where()) or exclude (where_not()) results, without needing to rely on arithmetic operations like + and ~.
  3. You can chain other criteria like which attributes to include in each result directly onto the search, no need to build DSLs and then requests. And no need to rely on typo-prone strings anymore, either!
  4. You can still translate the fluent query into a request.
  5. And you can then run your search and iterate through the results just like before.

Where to learn more

The various snippets, patterns and examples on the portal have all been updated to show off fluent search. To learn more specifically about search, check out:

We look forward to the use cases you'll unlock — do let us know any feedback! 🎉