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

Update api doc for java, nodejs, python and add pytest for in-mem mode #4095

Merged
merged 4 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion tools/java_api/src/main/java/com/kuzudb/KuzuDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ public class KuzuDatabase {
boolean destroyed = false;
boolean readOnly = false;

/**
* Creates a database object. The database will be created in memory with default settings.
*/
public KuzuDatabase() {
this("");
}

/**
* Creates a database object.
* @param databasePath: Database path. If the database does not already exist, it will be created.
Expand All @@ -25,7 +32,8 @@ public KuzuDatabase(String databasePath) {

/**
* Creates a database object.
* @param databasePath: Database path. If the database does not already exist, it will be created.
* @param databasePath: Database path. If the path is empty, or equal to `:memory:`, the database will be created in
* memory.
* @param bufferPoolSize: Max size of the buffer pool in bytes.
* @param enableCompression: Enable compression in storage.
* @param readOnly: Open the database in READ_ONLY mode.
Expand Down
10 changes: 10 additions & 0 deletions tools/java_api/src/test/java/com/kuzudb/test/DatabaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,14 @@ void DBCreationAndDestroyWithPathOnly() {
fail("DBCreationAndDestroyWithPathOnly failed");
}
}

@Test
void DBCreationAndDestroyWithNoParam(){
try {
KuzuDatabase database = new KuzuDatabase();
database.destroy();
} catch (Exception e) {
fail("DBCreationAndDestroyWithNoParam failed");
}
}
}
10 changes: 7 additions & 3 deletions tools/nodejs_api/src_js/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class Database {
* executed. To initialize the database immediately, call the `init()`
* function on the returned object.
*
* @param {String} databasePath path to the database file.
* @param {String} databasePath path to the database file. If the path is not specified, or empty, or equal to
`:memory:`, the database will be created in memory.
* @param {Number} bufferManagerSize size of the buffer manager in bytes.
* @param {Boolean} enableCompression whether to enable compression.
* @param {Boolean} readOnly if true, database will be opened in read-only mode.
Expand All @@ -19,13 +20,16 @@ class Database {
* address space limit some environment.
*/
constructor(
databasePath = "",
databasePath,
bufferManagerSize = 0,
enableCompression = true,
readOnly = false,
maxDBSize = 0
) {
if (typeof databasePath !== "string") {
if (!databasePath) {
databasePath = ":memory:";
}
else if (typeof databasePath !== "string") {
throw new Error("Database path must be a string.");
}
if (typeof bufferManagerSize !== "number" || bufferManagerSize < 0) {
Expand Down
42 changes: 42 additions & 0 deletions tools/nodejs_api/test/test_database.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,48 @@ describe("Database constructor", function () {
assert.equal(e.message, "Max DB size must be a positive integer.");
}
});

it("should create an in-memory database when no path is provided", async function () {
const testDb = new kuzu.Database();
const conn = new kuzu.Connection(testDb);
let res = await conn.query("CREATE NODE TABLE person(name STRING, age INT64, PRIMARY KEY(name));");
res.close();
res = await conn.query("CREATE (:person {name: 'Alice', age: 30});");
res.close();
res = await conn.query("CREATE (:person {name: 'Bob', age: 40});");
res.close();
res = await conn.query("MATCH (p:person) RETURN p.*;");
const result = await res.getAll();
assert.equal(result.length, 2);
assert.deepEqual(result, [
{ 'p.name': 'Alice', 'p.age': 30 },
{ 'p.name': 'Bob', 'p.age': 40 }
]);
res.close();
conn.close();
testDb.close();
});

it("should create an in-memory database when empty path is provided", async function () {
const testDb = new kuzu.Database("", 1 << 28 /* 256MB */);
const conn = new kuzu.Connection(testDb);
let res = await conn.query("CREATE NODE TABLE person(name STRING, age INT64, PRIMARY KEY(name));");
res.close();
res = await conn.query("CREATE (:person {name: 'Alice', age: 30});");
res.close();
res = await conn.query("CREATE (:person {name: 'Bob', age: 40});");
res.close();
res = await conn.query("MATCH (p:person) RETURN p.*;");
const result = await res.getAll();
assert.equal(result.length, 2);
assert.deepEqual(result, [
{ 'p.name': 'Alice', 'p.age': 30 },
{ 'p.name': 'Bob', 'p.age': 40 }
]);
res.close();
conn.close();
testDb.close();
});
});

describe("Database close", function () {
Expand Down
2 changes: 1 addition & 1 deletion tools/python_api/src_py/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ def create_function(
parsed_params_type = [x if type(x) is str else x.value for x in params_type]
if type(return_type) is not str:
return_type = return_type.value

self._connection.create_function(
name=name,
udf=udf,
Expand Down
7 changes: 5 additions & 2 deletions tools/python_api/src_py/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Database:

def __init__(
self,
database_path: str | Path = "",
database_path: str | Path | None = None,
*,
buffer_pool_size: int = 0,
max_num_threads: int = 0,
Expand All @@ -40,7 +40,8 @@ def __init__(
Parameters
----------
database_path : str, Path
The path to database files
The path to database files. If the path is not specified, or empty, or equal to `:memory:`, the database
will be created in memory.

buffer_pool_size : int
The maximum size of buffer pool in bytes. Defaults to ~80% of system memory.
Expand Down Expand Up @@ -73,6 +74,8 @@ def __init__(
environment and 1GB under 32-bit one.

"""
if database_path is None:
database_path = ":memory:"
if isinstance(database_path, Path):
database_path = str(database_path)

Expand Down
2 changes: 2 additions & 0 deletions tools/python_api/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,10 @@ def init_rdf(conn: kuzu.Connection) -> None:
if line := line.strip():
conn.execute(line)


_POOL_SIZE_: int = 256 * 1024 * 1024


def init_db(path: Path) -> Path:
if Path(path).exists():
shutil.rmtree(path)
Expand Down
48 changes: 47 additions & 1 deletion tools/python_api/test/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ def open_database_on_subprocess(tmp_path: Path, build_dir: Path) -> None:
print(r"{tmp_path!s}")
"""
)
result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True)
result = subprocess.run(
[sys.executable, "-c", code], capture_output=True, text=True
)
if result.returncode != 0:
raise RuntimeError(result.stderr)

Expand Down Expand Up @@ -77,3 +79,47 @@ def test_database_context_manager(tmp_path: Path, build_dir: Path) -> None:
# TODO: Enable this test on Windows when the read-only mode is implemented.
if sys.platform != "win32":
open_database_on_subprocess(db_path, build_dir)


def test_in_mem_database_memory_db_path() -> None:
db = kuzu.Database(database_path=":memory:")
assert not db.is_closed
assert db._database is not None

# Open the database on a subprocess. It should raise an exception.
conn = kuzu.Connection(db)
conn.execute("CREATE NODE TABLE person(name STRING, age INT64, PRIMARY KEY(name));")
conn.execute("CREATE (:person {name: 'Alice', age: 30});")
conn.execute("CREATE (:person {name: 'Bob', age: 40});")
result = conn.execute("MATCH (p:person) RETURN p.*")
assert result.get_num_tuples() == 2


def test_in_mem_database_empty_db_path() -> None:
db = kuzu.Database()
assert not db.is_closed
assert db._database is not None

# Open the database on a subprocess. It should raise an exception.
conn = kuzu.Connection(db)
conn.execute("CREATE NODE TABLE person(name STRING, age INT64, PRIMARY KEY(name));")
conn.execute("CREATE (:person {name: 'Alice', age: 30});")
conn.execute("CREATE (:person {name: 'Bob', age: 40});")
result = conn.execute("MATCH (p:person) RETURN p.*")
assert result.get_num_tuples() == 2


def test_in_mem_database_no_db_path() -> None:
with kuzu.Database(database_path="") as db:
assert not db.is_closed
assert db._database is not None

# Open the database on a subprocess. It should raise an exception.
conn = kuzu.Connection(db)
conn.execute(
"CREATE NODE TABLE person(name STRING, age INT64, PRIMARY KEY(name));"
)
conn.execute("CREATE (:person {name: 'Alice', age: 30});")
conn.execute("CREATE (:person {name: 'Bob', age: 40});")
with conn.execute("MATCH (p:person) RETURN p.*") as result:
assert result.get_num_tuples() == 2
3 changes: 2 additions & 1 deletion tools/python_api/test/test_datatype.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import numpy as np
import pandas as pd
import pytz

from type_aliases import ConnDB


Expand Down Expand Up @@ -100,6 +99,7 @@ def test_double(conn_db_readonly: ConnDB) -> None:
assert not result.has_next()
result.close()


def test_decimal(conn_db_readonly: ConnDB) -> None:
conn, _ = conn_db_readonly
res = conn.execute("UNWIND [1, 2, 3] AS A UNWIND [5.7, 8.3, 2.9] AS B WITH cast(CAST(A AS DECIMAL) * CAST(B AS DECIMAL) AS DECIMAL(18, 1)) AS PROD RETURN COLLECT(PROD) AS RES")
Expand Down Expand Up @@ -127,6 +127,7 @@ def test_decimal(conn_db_readonly: ConnDB) -> None:
Decimal('8.7'),
])


def test_string(conn_db_readonly: ConnDB) -> None:
conn, db = conn_db_readonly
result = conn.execute("MATCH (a:person) WHERE a.ID = 0 RETURN a.fName;")
Expand Down
1 change: 1 addition & 0 deletions tools/python_api/test/test_df.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ def test_get_df_unicode(conn_db_readonly: ConnDB) -> None:
"Roma",
]


def test_get_df_decimal(conn_db_readonly: ConnDB) -> None:
conn, _ = conn_db_readonly
res = conn.execute("UNWIND [1, 2, 3] AS A UNWIND [5.7, 8.3, 2.9] AS B RETURN CAST(CAST(A AS DECIMAL) * CAST(B AS DECIMAL) AS DECIMAL(18, 1)) AS PROD").get_as_df()
Expand Down
1 change: 0 additions & 1 deletion tools/python_api/test/test_exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import kuzu
import pytest

from type_aliases import ConnDB


Expand Down
5 changes: 4 additions & 1 deletion tools/python_api/test/test_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pathlib import Path
import kuzu


def test_struct_param_access(conn_db_readwrite: ConnDB) -> None:
conn, _ = conn_db_readwrite
batch = \
Expand Down Expand Up @@ -45,6 +46,7 @@ def test_struct_param_access(conn_db_readwrite: ConnDB) -> None:
parameters={"batch": batch}
)


def test_array_binding(conn_db_readwrite: ConnDB) -> None:
conn, _ = conn_db_readwrite
conn.execute("CREATE NODE TABLE node(id STRING, embedding DOUBLE[3], PRIMARY KEY(id))")
Expand All @@ -65,7 +67,8 @@ def test_array_binding(conn_db_readwrite: ConnDB) -> None:
# """, {"emb": [4.3, 5.2, 6.7], "emb1": [2.2, 3.3, 5.5]}
# )
# assert str(err.value) == "Binder exception: Left and right type are both ANY, which is not currently supported."



def test_bool_param(conn_db_readonly: ConnDB) -> None:
conn, db = conn_db_readonly
result = conn.execute(
Expand Down
12 changes: 7 additions & 5 deletions tools/python_api/test/test_scan_pandas.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import datetime
import re
from pathlib import Path
from uuid import UUID

import numpy as np
import pandas as pd
import pytest
from uuid import UUID

try:
from zoneinfo import ZoneInfo
Expand Down Expand Up @@ -399,24 +399,26 @@ def test_copy_from_pandas_object(tmp_path: Path) -> None:
assert result.get_next() == ["Karissa", '40']
assert result.has_next() is False


def test_copy_from_pandas_date(tmp_path: Path) -> None:
db = kuzu.Database(tmp_path)
conn = kuzu.Connection(db)
df = pd.DataFrame({"id": [1, 2], "date": [pd.Timestamp('2024-01-03'), pd.Timestamp('2023-10-10')]})
conn.execute("CREATE NODE TABLE Person(id INT16, d TIMESTAMP, PRIMARY KEY (id));")
conn.execute("COPY Person FROM df;")
result = conn.execute("match (p:Person) return p.*")
assert result.get_next() == [1, datetime.datetime(2024,1,3)]
assert result.get_next() == [2, datetime.datetime(2023,10,10)]
assert result.get_next() == [1, datetime.datetime(2024, 1, 3)]
assert result.get_next() == [2, datetime.datetime(2023, 10, 10)]
assert result.has_next() is False


def test_scan_string_to_nested(tmp_path: Path) -> None:
db = kuzu.Database(tmp_path)
conn = kuzu.Connection(db)
df = pd.DataFrame({"id": ["1"], "lstcol": ["[1,2,3]"], "mapcol": ["{'a'=1,'b'=2}"], "structcol": ["{a:1,b:2}"], "lstlstcol": ["[[],[1,2,3],[4,5,6]]"]})
conn.execute("CREATE NODE TABLE tab(id INT64, lstcol INT64[], mapcol MAP(STRING, INT64), structcol STRUCT(a INT64, b INT64), lstlstcol INT64[][], PRIMARY KEY(id))")
conn.execute("COPY tab from df")
result = conn.execute("match (t:tab) return t.*")
assert result.get_next() == [1, [1,2,3], {"'a'": 1, "'b'": 2}, {"a": 1, "b": 2}, [[],[1,2,3],[4,5,6]]]
assert result.get_next() == [1, [1, 2, 3], {"'a'": 1, "'b'": 2}, {"a": 1, "b": 2}, [[], [1, 2, 3], [4, 5, 6]]]
assert not result.has_next()

3 changes: 2 additions & 1 deletion tools/python_api/test/test_scan_pandas_pyarrow.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import math
from decimal import Decimal
import random
import struct
from datetime import datetime, timedelta
from decimal import Decimal
from pathlib import Path

import kuzu
Expand Down Expand Up @@ -634,6 +634,7 @@ def test_pyarrow_map_offset(conn_db_readonly: ConnDB) -> None:

assert idx == 48


def test_pyarrow_decimal(conn_db_readwrite: ConnDB) -> None:
conn, db = conn_db_readwrite
datalength = 4
Expand Down
8 changes: 4 additions & 4 deletions tools/python_api/test/test_scan_pyarrow.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from datetime import datetime

import pyarrow as pa
import pytest
from type_aliases import ConnDB

def test_pyarrow_basic(conn_db_readonly : ConnDB) -> None:

def test_pyarrow_basic(conn_db_readonly: ConnDB) -> None:
conn, db = conn_db_readonly
tab = pa.Table.from_arrays(
[
Expand All @@ -19,7 +18,8 @@ def test_pyarrow_basic(conn_db_readonly : ConnDB) -> None:
assert (result.get_next() == [2, 'b', False])
assert (result.get_next() == [3, 'c', None])

def test_pyarrow_copy_from(conn_db_readwrite : ConnDB) -> None:

def test_pyarrow_copy_from(conn_db_readwrite: ConnDB) -> None:
conn, db = conn_db_readwrite
tab = pa.Table.from_arrays(
[
Expand Down
7 changes: 0 additions & 7 deletions tools/python_api/test/test_torch_geometric_remote_backend.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
from __future__ import annotations

import random

import kuzu
import torch
from test_helper import KUZU_ROOT
from type_aliases import ConnDB

TINY_SNB_KNOWS_GROUND_TRUTH = {
0: [2, 3, 5],
2: [0, 3, 5],
Expand Down
Loading
Loading