SkillAgentSearch skills...

Linq.J

Lanbda Core → LINQ for Java, Object Query Language (LINQ) library based on the JVM

Install / Use

/learn @erupts/Linq.J
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<div align="center">

Linq.J

A Lightweight, Zero-Dependency Object Query Language for Java

Query in-memory data the way you query a database — with SQL-like fluent API inspired by C# LINQ

<p> <a href="https://www.erupt.xyz" target="_blank"><img src="https://img.shields.io/badge/Linq.J-brightgreen" alt="Erupt Framework" /></a> <a href="https://mvnrepository.com/search?q=linq.j"><img src="https://img.shields.io/maven-central/v/xyz.erupt/linq.j" alt="maven-central" /></a> <a href="https://www.oracle.com/technetwork/java/javase/downloads/index.html"><img src="https://img.shields.io/badge/JDK-8+-green.svg" alt="jdk 8+" /></a> <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="license MIT"></a> <a href="https://github.com/erupts/linq.j"><img src="https://img.shields.io/github/stars/erupts/linq.j?style=social" alt="GitHub stars" /></a> <a href='https://gitee.com/erupt/linq/stargazers'><img src='https://gitee.com/erupt/linq/badge/star.svg?theme=gray' alt='GitEE star' /></a> <a href='https://gitcode.com/erupts/Linq.J'><img src='https://gitcode.com/erupts/linq.j/star/badge.svg' alt='GitCode star' /></a> </p>

中文 / English

</div>

Highlights

  • Zero Runtime Dependencies — pure JDK, no third-party libraries
  • ~50 KB — minimal footprint, won't bloat your project
  • JDK 8+ — works on Java 8 and above
  • Type-Safe — lambda method references for column operations, compile-time checked
  • SQL-LikeSELECT, JOIN, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET, DISTINCT
  • Pluggable Engine — swap or extend the default EruptEngine with your own execution strategy

Why Linq.J?

Java developers often need to join, filter, sort, and aggregate data from in-memory collections — results from RPCs, heterogeneous data sources, or post-SQL processing. Without Linq.J, this means verbose for loops, if branches, and scattered logic. Linq.J replaces all of that with a single fluent chain that reads like SQL.

var result = Linq.from(employees)
    .innerJoin(departments, Dept::getId, Emp::getDeptId)
    .where(Emp::getSalary, salary -> salary > 5000)
    .groupBy(Dept::getName)
    .select(
        Columns.of(Dept::getName, "department"),
        Columns.avg(Emp::getSalary, "avgSalary"),
        Columns.count("headcount")
    )
    .orderByDesc(Emp::getSalary)
    .write(DeptStats.class);

Getting Started

1. Add the Dependency

<dependency>
    <groupId>xyz.erupt</groupId>
    <artifactId>linq.j</artifactId>
    <version>LATEST</version>
</dependency>

2. Ensure Fields Have Getters

Linq.J resolves field names from lambda method references via SerializedLambda. Your data classes must have getter methods. Using Lombok @Getter is recommended:

@Getter
public class User {
    private Long id;
    private String name;
    private Integer age;
}

3. Start Querying

List<User> adults = Linq.from(users)
    .where(User::getAge, age -> age >= 18)
    .select(User::getName, User::getAge)
    .orderBy(User::getAge)
    .write(User.class);

Supported Data Sources

Linq.J operates on any in-memory collection. Feed it data from anywhere:

| Source | Example | |--------|---------| | List / Array | Linq.from(list) or Linq.from("A", "B", "C") | | SQL Result | Load via JDBC / MyBatis / JPA, then query with Linq | | CSV / XML / JSON | Parse into objects, then query with Linq | | Redis / MongoDB | Fetch results, then query with Linq | | Stream / File | Collect into a list, then query with Linq | | RPC Response | Feign / Dubbo / gRPC results, then query with Linq |

API Reference

Select

// Select all fields
Linq.from(source).select(User.class);

// Select specific fields
Linq.from(source).select(User::getName, User::getDate, User::getTags);

// Select with alias
Linq.from(source).select(User::getTags, "tagAlias");

// Select with value transformation
Linq.from(source).select(Columns.ofx(User::getId, id -> id + "-suffix"));

// Aggregate functions
Linq.from(source).select(
    Columns.count("count"),
    Columns.sum(User::getId, "sum"),
    Columns.max(User::getId, "max"),
    Columns.min(User::getId, "min"),
    Columns.avg(User::getId, "avg"),
    Columns.countDistinct(User::getName, "uniqueNames")
);

Join

Four standard join types are supported, powered by a Hash Join engine for performance:

// Left Join
Linq.from(source).leftJoin(target, Target::getId, Source::getId);

// Right Join
Linq.from(source).rightJoin(target, Target::getId, Source::getId);

// Inner Join
Linq.from(source).innerJoin(target, Target::getId, Source::getId);

// Full Join
Linq.from(source).fullJoin(target, Target::getId, Source::getId);

// Join + multi-table select
Linq.from(source)
    .leftJoin(target, Target::getId, Source::getId)
    .select(Source.class)
    .select(Target::getName)
    .write(Result.class);

Where

// Equals
Linq.from(source).eq(User::getName, "Thanos");

// Between (inclusive)
Linq.from(source).between(User::getId, 1, 100);

// In
Linq.from(source).in(User::getId, 1, 2, 3);

// Like (contains)
Linq.from(source).like(User::getName, "admin");

// Is Null
Linq.from(source).isNull(User::getId);

// Greater than / Less than
Linq.from(source).gt(User::getAge, 18);

// Custom single-field condition
Linq.from(source).where(User::getId, id -> id >= 5);

// Custom multi-field condition
Linq.from(source).where(row -> {
    String name = row.get(User::getName);
    Integer age = (Integer) row.get(User::getAge);
    return "admin".equals(name) || age > 18;
});

Group By & Having

Linq.from(orders)
    .groupBy(Order::getCategory)
    .select(
        Columns.of(Order::getCategory, "name"),
        Columns.min(Order::getDate, "earliest"),
        Columns.avg(Order::getPrice, "avgPrice"),
        Columns.count("total"),
        Columns.countDistinct(Order::getBuyer, "uniqueBuyers")
    )
    .having(row -> Integer.parseInt(row.get("total").toString()) > 10)
    .orderBy(Order::getPrice)
    .write(CategoryStats.class);

Order By, Limit & Offset

Linq.from(source)
    .orderBy(User::getName)          // ascending
    .orderByDesc(User::getAge)       // descending
    .offset(10)                      // skip first 10
    .limit(20)                       // take 20 records
    .write(User.class);

Result Output

// Write to List<T>
List<User> list = Linq.from(source).write(User.class);

// Write to single object
User one = Linq.from(source).limit(1).writeOne(User.class);

// Write to List<Map<String, Object>>
List<Map<String, Object>> maps = Linq.from(source).writeMap();

// Write to single Map<String, Object>
Map<String, Object> map = Linq.from(source).writeMapOne();

Architecture

Linq.J follows a clean layered architecture with separation between query building and execution:

┌─────────────────────────────────────────────────────┐
│                  Fluent API Layer                    │
│   Select · Join · Where · GroupBy · OrderBy · Write  │
├─────────────────────────────────────────────────────┤
│                  Query Model Layer                   │
│        Linq (Facade) · Dql (State) · Column · Row    │
├─────────────────────────────────────────────────────┤
│                 Execution Engine Layer                │
│       Engine (Abstract) → EruptEngine (Default)      │
├─────────────────────────────────────────────────────┤
│                Lambda Resolution Layer               │
│     SFunction · LambdaSee · SerializedLambda         │
└─────────────────────────────────────────────────────┘

Pluggable Engine: Replace the default EruptEngine with a custom implementation:

Linq.setEngine(new MyCustomEngine());

Use Cases

| Scenario | Description | |----------|-------------| | RPC Result Association | Join results from Feign / Dubbo / gRPC calls in memory instead of multiple DB roundtrips | | Heterogeneous Data | Unify and query data from Redis, MongoDB, MySQL in one place | | Post-SQL Processing | Further filter, sort, and aggregate database query results in code | | In-Memory Pagination | Merge multiple result sets, then sort, aggregate, and paginate | | Object Mapping | Semantic, readable object transformation and projection | | Cross-Source Federation | Federated queries across different data sources at the application layer |

Before & After

<table> <tr> <td width="50%">

Traditional Java

Map<String, List<Order>> grouped = new HashMap<>();
for (Order o : orders) {
    if (o.getAmount() > 100) {
        grouped.computeIfAbsent(
            o.getCategory(),
            k -> new ArrayList<>()
        ).add(o);
    }
}
Map<String, Double> avgMap = new HashMap<>();
for (var entry : grouped.entrySet()) {
    double sum = 0;
    for (var o : entry.getValue()) {
        sum += o.getAmount();
    }
    avgMap.put(entry.getKey(),
        sum / entry.getValue().size());
}
// Still need sorting... more code...
</td> <td width="50%">

With Linq.J

var result = Linq.from(orders)
    .where(Order::getAmount, a -> a > 100)
    .groupBy(Order::getCategory)
    .select(
        Columns.of(Order::getCategory, "cat"),
        Columns.avg(Order::getAmount, "avg"),
        Columns.count("cnt")
    )
    .orderByDesc(Order::getAmount)
    .write(Stats.class);
</td> </tr> </table>

Roadmap

  • [x] HAVING clause support
  • [x] Group column formatting (group by date(created_at))
  • [ ] Set operations: UNION ALL, UNION, INTERSECT, EXCEPT, UNION BY NAME
  • [ ] Window functions (ROW_NUMBER, RANK, DENSE_RANK, ...)
  • [ ] Nested Loop Join strategy

Contributing

Contributions are welcome! Feel free to open issues and pull requests.

License

MIT

View on GitHub
GitHub Stars24
CategoryDevelopment
Updated1d ago
Forks3

Languages

Java

Security Score

95/100

Audited on Mar 30, 2026

No findings