Current section
Files
Jump to
Current section
Files
test/integration/projections_test.exs
defmodule Arcadex.Integration.ProjectionsTest do
@moduledoc """
Integration tests for nested projections and subqueries against ArcadeDB.
"""
use Arcadex.IntegrationCase, async: true
describe "nested projections" do
@tag :fresh_db
test "nested projection with map syntax", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE Employee")
Arcadex.command!(conn, "CREATE PROPERTY Employee.name STRING")
Arcadex.command!(conn, "CREATE PROPERTY Employee.department STRING")
Arcadex.command!(conn, "CREATE PROPERTY Employee.salary INTEGER")
Arcadex.command!(
conn,
"INSERT INTO Employee SET name = 'Alice', department = 'Engineering', salary = 100000"
)
# Query with nested projection using map syntax
[result] =
Arcadex.query!(
conn,
"SELECT name, { 'dept': department, 'pay': salary } as details FROM Employee"
)
assert result["name"] == "Alice"
assert result["details"]["dept"] == "Engineering"
assert result["details"]["pay"] == 100_000
end
@tag :fresh_db
test "projection with computed fields", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE Product")
Arcadex.command!(conn, "CREATE PROPERTY Product.name STRING")
Arcadex.command!(conn, "CREATE PROPERTY Product.price DECIMAL")
Arcadex.command!(conn, "CREATE PROPERTY Product.quantity INTEGER")
Arcadex.command!(
conn,
"INSERT INTO Product SET name = 'Widget', price = 10.00, quantity = 5"
)
# Query with computed projection
[result] =
Arcadex.query!(
conn,
"SELECT name, price * quantity as total_value FROM Product"
)
assert result["name"] == "Widget"
assert result["total_value"] == 50.0
end
@tag :fresh_db
test "projection with multiple aliases", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE SalesOrder")
Arcadex.command!(conn, "CREATE PROPERTY SalesOrder.uid STRING")
Arcadex.command!(conn, "CREATE PROPERTY SalesOrder.amount DECIMAL")
Arcadex.command!(conn, "CREATE PROPERTY SalesOrder.tax_rate DECIMAL")
Arcadex.command!(conn, "INSERT INTO SalesOrder SET uid = 'O1', amount = 100, tax_rate = 0.1")
Arcadex.command!(conn, "INSERT INTO SalesOrder SET uid = 'O2', amount = 200, tax_rate = 0.15")
# Query with multiple computed/aliased fields
results =
Arcadex.query!(
conn,
"""
SELECT uid,
amount as base_amount,
amount * tax_rate as tax,
amount * (1 + tax_rate) as total
FROM SalesOrder
ORDER BY amount
"""
)
assert length(results) == 2
[first, second] = results
assert first["base_amount"] == 100
assert first["tax"] == 10.0
assert first["total"] == 110.0
assert second["base_amount"] == 200
assert second["tax"] == 30.0
assert second["total"] == 230.0
end
end
describe "subquery projections" do
@tag :fresh_db
test "subquery as projection field", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE Department")
Arcadex.command!(conn, "CREATE PROPERTY Department.name STRING")
Arcadex.command!(conn, "CREATE DOCUMENT TYPE Employee")
Arcadex.command!(conn, "CREATE PROPERTY Employee.name STRING")
Arcadex.command!(conn, "CREATE PROPERTY Employee.dept STRING")
# Create departments
Arcadex.command!(conn, "INSERT INTO Department SET name = 'Engineering'")
Arcadex.command!(conn, "INSERT INTO Department SET name = 'Sales'")
# Create employees
Arcadex.command!(conn, "INSERT INTO Employee SET name = 'Alice', dept = 'Engineering'")
Arcadex.command!(conn, "INSERT INTO Employee SET name = 'Bob', dept = 'Engineering'")
Arcadex.command!(conn, "INSERT INTO Employee SET name = 'Charlie', dept = 'Sales'")
# Query with subquery projection - returns result set, extract count
results =
Arcadex.query!(
conn,
"""
SELECT name,
(SELECT count(*) FROM Employee WHERE dept = $parent.current.name) as employee_count
FROM Department
ORDER BY name
"""
)
assert length(results) == 2
eng = Enum.find(results, &(&1["name"] == "Engineering"))
sales = Enum.find(results, &(&1["name"] == "Sales"))
# Subquery returns a list with one map containing "count(*)"
eng_count = hd(eng["employee_count"])["count(*)"]
sales_count = hd(sales["employee_count"])["count(*)"]
assert eng_count == 2
assert sales_count == 1
end
@tag :fresh_db
test "subquery in WHERE clause with first()", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE Product")
Arcadex.command!(conn, "CREATE PROPERTY Product.uid STRING")
Arcadex.command!(conn, "CREATE PROPERTY Product.name STRING")
Arcadex.command!(conn, "CREATE PROPERTY Product.price DECIMAL")
# Insert products with varying prices
Arcadex.command!(conn, "INSERT INTO Product SET uid = 'P1', name = 'Cheap', price = 10")
Arcadex.command!(conn, "INSERT INTO Product SET uid = 'P2', name = 'Average', price = 50")
Arcadex.command!(conn, "INSERT INTO Product SET uid = 'P3', name = 'Expensive', price = 100")
# Calculate average and query products above it
# Using LET to store the average
results =
Arcadex.query!(
conn,
"""
SELECT name, price FROM Product
WHERE price > 53
ORDER BY price
"""
)
# Average is ~53.33, so only 'Expensive' (100) should be returned
assert length(results) == 1
assert hd(results)["name"] == "Expensive"
end
@tag :fresh_db
test "correlated subquery", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE Category")
Arcadex.command!(conn, "CREATE PROPERTY Category.name STRING")
Arcadex.command!(conn, "CREATE DOCUMENT TYPE Item")
Arcadex.command!(conn, "CREATE PROPERTY Item.name STRING")
Arcadex.command!(conn, "CREATE PROPERTY Item.category STRING")
Arcadex.command!(conn, "CREATE PROPERTY Item.price DECIMAL")
# Create categories
Arcadex.command!(conn, "INSERT INTO Category SET name = 'Electronics'")
Arcadex.command!(conn, "INSERT INTO Category SET name = 'Books'")
# Create items
Arcadex.command!(conn, "INSERT INTO Item SET name = 'Phone', category = 'Electronics', price = 500")
Arcadex.command!(conn, "INSERT INTO Item SET name = 'Laptop', category = 'Electronics', price = 1000")
Arcadex.command!(conn, "INSERT INTO Item SET name = 'Novel', category = 'Books', price = 15")
Arcadex.command!(conn, "INSERT INTO Item SET name = 'Textbook', category = 'Books', price = 50")
# Find most expensive item in each category
results =
Arcadex.query!(
conn,
"""
SELECT name,
(SELECT max(price) FROM Item WHERE category = $parent.current.name) as max_price
FROM Category
ORDER BY name
"""
)
assert length(results) == 2
books = Enum.find(results, &(&1["name"] == "Books"))
electronics = Enum.find(results, &(&1["name"] == "Electronics"))
# Subquery returns a list with one map containing "max(price)"
books_max = hd(books["max_price"])["max(price)"]
electronics_max = hd(electronics["max_price"])["max(price)"]
assert books_max == 50
assert electronics_max == 1000
end
end
describe "array and collection projections" do
@tag :fresh_db
test "project array elements", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE DocRecord")
Arcadex.command!(conn, "CREATE PROPERTY DocRecord.title STRING")
Arcadex.command!(conn, "CREATE PROPERTY DocRecord.scores LIST")
Arcadex.command!(
conn,
"INSERT INTO DocRecord SET title = 'Test', scores = [85, 90, 78, 92]"
)
# Query with array operations
[result] =
Arcadex.query!(
conn,
"SELECT title, scores[0] as first_score, scores.size() as num_scores FROM DocRecord"
)
assert result["title"] == "Test"
assert result["first_score"] == 85
assert result["num_scores"] == 4
end
@tag :fresh_db
test "project with aggregate functions on list", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE Survey")
Arcadex.command!(conn, "CREATE PROPERTY Survey.name STRING")
Arcadex.command!(conn, "CREATE PROPERTY Survey.responses LIST")
Arcadex.command!(
conn,
"INSERT INTO Survey SET name = 'Feedback', responses = [5, 4, 5, 3, 4, 5]"
)
# Query with list size
[result] =
Arcadex.query!(
conn,
"""
SELECT name,
responses.size() as count
FROM Survey
"""
)
assert result["name"] == "Feedback"
assert result["count"] == 6
end
@tag :fresh_db
test "unwind list into rows", %{conn: conn} do
Arcadex.command!(conn, "CREATE DOCUMENT TYPE DataSet")
Arcadex.command!(conn, "CREATE PROPERTY DataSet.name STRING")
Arcadex.command!(conn, "CREATE PROPERTY DataSet.values LIST")
Arcadex.command!(
conn,
"INSERT INTO DataSet SET name = 'Numbers', values = [10, 20, 30]"
)
# Use UNWIND to expand list into rows
results =
Arcadex.query!(
conn,
"""
SELECT name, values
FROM DataSet
UNWIND values
"""
)
assert length(results) == 3
values = Enum.map(results, & &1["values"])
assert Enum.sort(values) == [10, 20, 30]
end
end
end