Simple Data Analysis Core - v2.1.14
    Preparing search index...

    Class SimpleTable

    IMPORTANT: When extending this class, always use this.sdb.newTable() to create new tables instead of new SimpleTable(...) directly. This ensures subclasses that override tableClass on their parent SimpleDB get their own table type back.

    Represents a table within a SimpleDB database, capable of handling tabular, geospatial, and vector data. SimpleTable instances are typically created via a SimpleDB instance.

    // Create a SimpleDB instance (in-memory by default)
    const sdb = new SimpleDB();

    // Create a table, load a CSV file, and log its first few rows
    const employees = await sdb
    .newTable("employees")
    .loadData("./employees.csv")
    .log();

    // Close the database connection and free up resources
    await sdb.close();
    // Handling geospatial data
    // Create a SimpleDB instance
    const sdb = new SimpleDB();

    // Create a table and load geospatial data from a GeoJSON file
    const boundaries = await sdb
    .newTable("boundaries")
    .loadGeoData("./boundaries.geojson")
    .log();

    // Close the database connection
    await sdb.close();

    Hierarchy

    • default
      • SimpleTable
    Index
    • Computes the cumulative sum of values in a column. For this method to work properly, ensure your data is sorted first.

      Parameters

      • column: string

        The name of the column storing the values to be accumulated.

      • newColumn: string

        The name of the new column in which the computed cumulative values will be stored.

      • options: { by?: string | string[] } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to partition by. Accumulation is performed independently within each group.

      Returns this

      The table, so methods can be chained.

      // Compute the cumulative sum of 'sales' in a new 'cumulativeSales' column
      // Ensure the table is sorted by a relevant column (e.g., date) before calling this method.
      await table.accumulate("sales", "cumulativeSales").log();
      // Compute the cumulative sum of 'orders' by 'customer_id'
      // Ensure the table is sorted by 'customer_id' and then by a relevant order column (e.g., order_date).
      await table.accumulate("orders", "cumulativeOrders", { by: "customer_id" }).log();
      // Compute the cumulative sum of 'revenue' by 'region' and 'product_category'
      await table.accumulate("revenue", "cumulativeRevenue", { by: ["region", "product_category"] }).log();
    • Adds one or more summary rows to the table. Each row is calculated from the original rows before any summary rows are added. This is useful for preparing totals and other statistics before exporting tabular data.

      Passing "all" selects every numeric column. Columns that are neither summarized nor used for labels contain NULL in the added rows. A stat string is also used as its row label; pass an object to customize that label. If options.stats is omitted, every supported stat is added.

      Parameters

      • columns: string | string[]

        The numeric column name, an array of numeric column names, or "all" to summarize every numeric column.

      • labelColumn: string

        The existing string column in which stat row labels will be written.

      • options: {
            position?: "top" | "bottom";
            stats?:
                | "countDistinct"
                | "countNull"
                | "min"
                | "max"
                | "mean"
                | "median"
                | "sum"
                | "skew"
                | "stdDev"
                | "variance"
                | {
                    label?: string;
                    stat: | "countDistinct"
                    | "countNull"
                    | "min"
                    | "max"
                    | "mean"
                    | "median"
                    | "sum"
                    | "skew"
                    | "stdDev"
                    | "variance";
                }
                | (
                    | "countDistinct"
                    | "countNull"
                    | "min"
                    | "max"
                    | "mean"
                    | "median"
                    | "sum"
                    | "skew"
                    | "stdDev"
                    | "variance"
                    | {
                        label?: string;
                        stat: | "countDistinct"
                        | "countNull"
                        | "min"
                        | "max"
                        | "mean"
                        | "median"
                        | "sum"
                        | "skew"
                        | "stdDev"
                        | "variance";
                    }
                )[];
        } = {}

        An optional object with configuration options:

        • Optionalposition?: "top" | "bottom"

          Whether to add the summary rows at the "top" or "bottom" of the table. Defaults to "bottom".

        • Optionalstats?:
              | "countDistinct"
              | "countNull"
              | "min"
              | "max"
              | "mean"
              | "median"
              | "sum"
              | "skew"
              | "stdDev"
              | "variance"
              | {
                  label?: string;
                  stat: | "countDistinct"
                  | "countNull"
                  | "min"
                  | "max"
                  | "mean"
                  | "median"
                  | "sum"
                  | "skew"
                  | "stdDev"
                  | "variance";
              }
              | (
                  | "countDistinct"
                  | "countNull"
                  | "min"
                  | "max"
                  | "mean"
                  | "median"
                  | "sum"
                  | "skew"
                  | "stdDev"
                  | "variance"
                  | {
                      label?: string;
                      stat: | "countDistinct"
                      | "countNull"
                      | "min"
                      | "max"
                      | "mean"
                      | "median"
                      | "sum"
                      | "skew"
                      | "stdDev"
                      | "variance";
                  }
              )[]

          A stat, stat configuration, or array of either. Supported stats are "countDistinct", "countNull", "min", "max", "mean", "median", "sum", "skew", "stdDev", and "variance". An object's label defaults to its stat. If omitted, all supported stats are added.

      Returns this

      The table, so methods can be chained.

      // Add a total row for every numeric column, labelled "sum" in "region".
      await table.addSummaryRows("all", "region", { stats: "sum" }).log();
      // Add two summary rows with default labels.
      await table.addSummaryRows(["sales", "expenses"], "region", {
      stats: ["sum", "mean"],
      position: "top",
      }).log();
      // Customize the labels written to the label column.
      await table.addSummaryRows("all", "region", {
      stats: [
      { stat: "sum", label: "Total" },
      { stat: "mean", label: "Average" },
      ],
      }).log();
    • Adds numeric start and end columns for bins of the specified interval size. Each bin includes its start and excludes its end: start <= value < end. A value exactly on an end boundary belongs to the next bin. Null source values produce null in both output columns.

      Parameters

      • column: string

        The numeric column containing values from which bins will be computed.

      • interval: number

        The finite, positive interval size for binning the values.

      • startColumn: string

        The required name of the new numeric column containing inclusive bin starts.

      • endColumn: string

        The required name of the new numeric column containing exclusive bin ends. Must differ from startColumn.

      • options: { startValue?: number } = {}

        An optional object with configuration options:

        • OptionalstartValue?: number

          The finite starting value for binning, no greater than the minimum source value. Defaults to the minimum value in the specified column.

      Returns this

      The table, so methods can be chained.

      // If the minimum is 5, bins have boundaries 5 and 15, 15 and 25, etc.
      await table.bins("column1", 10, "binStart", "binEnd").log();
      // Bins start at 0: a value of 10 has binStart 10 and binEnd 20.
      await table
      .bins("column1", 10, "binStart", "binEnd", { startValue: 0 })
      .log();
    • Computes proportions vertically over a column's values, relative to the sum of all values in that column or group.

      Parameters

      • column: string

        The column containing values for which proportions will be computed. The proportions are calculated based on the sum of values in the specified column.

      • newColumn: string

        The name of the new column where the proportions will be stored.

      • options: { base?: number; by?: string | string[]; decimals?: number } = {}

        An optional object with configuration options:

        • Optionalbase?: number

          A finite positive value that the proportions in the column or each group sum to before rounding. Defaults to 1.

        • Optionalby?: string | string[]

          The column name or an array of column names to partition by. Proportions are calculated independently within each group.

        • Optionaldecimals?: number

          The number of decimal places to round the computed proportions. Defaults to undefined (no rounding).

      Returns this

      The table, so methods can be chained.

      // Add a new column 'perc' with each 'column1' value divided by the sum of all 'column1' values
      await table.columnProportions("column1", "perc").log();
      // Compute proportions for 'column1' by 'column2', rounded to two decimal places
      await table.columnProportions("column1", "perc", { by: "column2", decimals: 2 }).log();
      // Compute percentages that sum to 100 before rounding
      await table.columnProportions("sales", "sales_percentage", { base: 100, decimals: 1 }).log();
      // Compute proportions for 'sales' by 'region' and 'product_type'
      await table.columnProportions("sales", "sales_proportion", { by: ["region", "product_type"] }).log();
    • Calculates correlations between columns. If no x and y columns are specified, the method computes the correlations for all numeric column combinations. Note that correlation is symmetrical: the correlation of x with y is the same as y with x.

      Parameters

      • options: {
            by?: string | string[];
            decimals?: number;
            outputTable?: string | boolean;
            x?: string;
            y?: string;
        } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to group by. Correlations are calculated independently within each group.

        • Optionaldecimals?: number

          The number of decimal places to round the correlation values. Defaults to undefined (no rounding).

        • OptionaloutputTable?: string | boolean

          If true, the results will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be overwritten. Defaults to false.

        • Optionalx?: string

          The name of the column for the x-values. If omitted, correlations will be computed for all numeric columns.

        • Optionaly?: string

          The name of the column for the y-values. It can be provided only when options.x is also set. If both are omitted, correlations will be computed for all numeric column pairs.

      Returns this

      A table instance containing the correlation results (either the current table or a new table), so methods can be chained.

      // Compute correlations between all numeric columns, overwriting the current table
      await table.correlations().log();
      // Compute correlations between 'column1' and all other numeric columns
      await table.correlations({ x: "column1" }).log();
      // Compute the correlation between 'column1' and 'column2'
      await table.correlations({ x: "column1", y: "column2" }).log();
      // Compute correlations within 'categoryColumn' and store results in a new table
      const correlationTable = await table.correlations({ by: "categoryColumn", outputTable: true }).log();
      // Compute correlations, rounded to 2 decimal places
      await table.correlations({ decimals: 2 }).log();
    • Indexes a numeric column by dividing each value by a reference value and multiplying the result by a base value.

      The reference can be calculated from the indexed column with a statistic, read from exactly one row selected by another column's value, or read from the unique row where another column reaches its minimum or maximum. With options.by, references are calculated or selected independently within each group. Null values in the indexed column remain null when their group has a valid reference. The operation throws when a group has no unique selected row or its reference value is null or zero.

      Exact temporal references are compared at their full DuckDB precision. JavaScript Date objects only have millisecond precision and always represent an instant. Construct them with an explicit timezone, such as new Date("2001-01-01T00:00:00Z"); date-time strings without Z or an offset use the user's local timezone.

      Parameters

      • column: string

        The numeric column containing the values to index.

      • newColumn: string

        The name of the new column where indexed values will be stored.

      • reference:
            | {
                at?: undefined;
                column?: undefined;
                equals?: undefined;
                stat: "min"
                | "max"
                | "mean"
                | "median";
            }
            | {
                at?: undefined;
                column: string;
                equals: string
                | number
                | bigint
                | boolean
                | Date;
                stat?: undefined;
            }
            | {
                at: "min"
                | "max";
                column: string;
                equals?: undefined;
                stat?: undefined;
            }

        A statistic calculated from column, a column and exact non-null equals value selecting a row, or a column and at set to min or max selecting its unique extreme row. The selected row's column value becomes the reference.

        • {
              at?: undefined;
              column?: undefined;
              equals?: undefined;
              stat: "min" | "max" | "mean" | "median";
          }
          • Optionalat?: undefined

            Selects the unique row where reference.column reaches its minimum or maximum.

          • Optionalcolumn?: undefined

            The column used to select an exact reference row or its unique minimum or maximum row.

          • Optionalequals?: undefined

            The non-null value used to select an exact reference row. Its JavaScript type must be compatible with the reference column's DuckDB type; string, numeric, boolean, and temporal values are not coerced across type families. Date values should be constructed with an explicit timezone.

          • stat: "min" | "max" | "mean" | "median"

            The statistic used to calculate the reference directly from the indexed column.

        • {
              at?: undefined;
              column: string;
              equals: string | number | bigint | boolean | Date;
              stat?: undefined;
          }
          • Optionalat?: undefined

            Selects the unique row where reference.column reaches its minimum or maximum.

          • column: string

            The column used to select an exact reference row or its unique minimum or maximum row.

          • equals: string | number | bigint | boolean | Date

            The non-null value used to select an exact reference row. Its JavaScript type must be compatible with the reference column's DuckDB type; string, numeric, boolean, and temporal values are not coerced across type families. Date values should be constructed with an explicit timezone.

          • Optionalstat?: undefined

            The statistic used to calculate the reference directly from the indexed column.

        • { at: "min" | "max"; column: string; equals?: undefined; stat?: undefined }
          • at: "min" | "max"

            Selects the unique row where reference.column reaches its minimum or maximum.

          • column: string

            The column used to select an exact reference row or its unique minimum or maximum row.

          • Optionalequals?: undefined

            The non-null value used to select an exact reference row. Its JavaScript type must be compatible with the reference column's DuckDB type; string, numeric, boolean, and temporal values are not coerced across type families. Date values should be constructed with an explicit timezone.

          • Optionalstat?: undefined

            The statistic used to calculate the reference directly from the indexed column.

      • options: { base?: number; by?: string | string[]; decimals?: number } = {}

        An optional object with configuration options.

        • Optionalbase?: number

          The finite positive value assigned to the reference. Defaults to 100.

        • Optionalby?: string | string[]

          A column name or an array of column names to partition by. The reference is calculated independently within each group.

        • Optionaldecimals?: number

          A finite non-negative integer specifying the number of decimal places to retain. By default, values are not rounded.

      Returns this

      The table, so methods can be chained.

      // Index each country's average home price to its January 2001 value.
      await table.indexValues(
      "homePrice",
      "homePriceIndexed",
      {
      column: "date",
      equals: new Date("2001-01-01T00:00:00Z"),
      },
      {
      by: "country",
      base: 100,
      decimals: 1,
      },
      ).log();
      // Index each value against the mean of its group.
      await table.indexValues("homePrice", "homePriceIndexed", { stat: "mean" }, {
      by: "country",
      }).log();
      // Index each country's average home price to its earliest value.
      // This throws if multiple rows share the earliest date in a country.
      await table.indexValues("homePrice", "homePriceIndexed", {
      column: "date",
      at: "min",
      }, { by: "country" }).log();
    • Performs linear regression analysis. The results include the slope, the y-intercept, and the R-squared value. If no x and y columns are specified, the method computes linear regression analysis for all numeric column permutations. Note that linear regression analysis is asymmetrical: the linear regression of x over y is not the same as y over x.

      Parameters

      • options: {
            by?: string | string[];
            decimals?: number;
            outputTable?: string | boolean;
            x?: string;
            y?: string;
        } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to group by. Linear regressions are calculated independently within each group.

        • Optionaldecimals?: number

          The number of decimal places to round the regression values (slope, intercept, r-squared). Defaults to undefined (no rounding).

        • OptionaloutputTable?: string | boolean

          If true, the results will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be overwritten. Defaults to false.

        • Optionalx?: string

          The name of the column for the independent variable (x-values). If omitted, linear regressions will be computed for all numeric columns as x.

        • Optionaly?: string

          The name of the column for the dependent variable (y-values). It can be provided only when options.x is also set. If both are omitted, linear regressions will be computed for all numeric column permutations.

      Returns this

      A table instance containing the linear regression results (either the current table or a new table), so methods can be chained.

      // Compute all linear regressions between all numeric columns, overwriting the current table
      await table.linearRegressions().log();
      // Compute linear regressions with 'column1' as the independent variable and all other numeric columns as dependent variables
      await table.linearRegressions({ x: "column1" }).log();
      // Compute the linear regression of 'sales' (y) over 'advertising' (x)
      await table.linearRegressions({ x: "advertising", y: "sales" }).log();
      // Compute linear regressions by 'region' and store results in a new table
      const regressionTable = await table.linearRegressions({ by: "region", outputTable: true }).log();
      // Compute linear regressions, rounded to 3 decimal places
      await table.linearRegressions({ decimals: 3 }).log();
    • Normalizes the values in a column using min-max normalization.

      Parameters

      • column: string

        The name of the column in which values will be normalized.

      • newColumn: string

        The name of the new column where normalized values will be stored.

      • options: { by?: string | string[]; decimals?: number; range?: [number, number] } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to partition by. Normalization is performed independently within each group.

        • Optionaldecimals?: number

          The number of decimal places to round the normalized values. Defaults to undefined (no rounding).

        • Optionalrange?: [number, number]

          The inclusive range to scale normalized values to, as [minimum, maximum]. Both values must be finite and the minimum must be less than the maximum. Defaults to [0, 1].

      Returns this

      The table, so methods can be chained.

      // Normalize the values in 'column1' and store them in a new 'normalizedColumn1' column
      await table.normalize("column1", "normalizedColumn1").log();
      // Normalize 'value' by 'group'
      await table.normalize("value", "normalizedValue", { by: "group" }).log();
      // Normalize 'data' values, rounded to 2 decimal places
      await table.normalize("data", "normalizedData", { decimals: 2 }).log();
      // Normalize 'score' values to a range from 0 to 10
      await table.normalize("score", "scaledScore", { range: [0, 10] }).log();
    • Identifies outliers in a specified column using the Interquartile Range (IQR) method.

      Parameters

      • column: string

        The name of the column in which outliers will be identified.

      • newColumn: string

        The name of the new column where the boolean results (TRUE for outlier, FALSE otherwise) will be stored.

      • options: { by?: string | string[] } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to partition by. Outliers are detected independently within each group.

      Returns this

      The table, so methods can be chained.

      // Look for outliers in the 'age' column and store results in a new 'isOutlier' column
      await table.outliersIQR("age", "isOutlier").log();
      // Look for outliers in 'salary' by 'gender'
      await table.outliersIQR("salary", "salaryOutlier", { by: "gender" }).log();
    • Assigns quantiles to rows in a new column based on specified column values.

      Parameters

      • column: string

        The column containing values from which quantiles will be assigned.

      • count: number

        The number of quantiles to divide the data into (e.g., 4 for quartiles, 10 for deciles).

      • newColumn: string

        The name of the new column where the assigned quantiles will be stored.

      • options: { by?: string | string[] } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to partition by. Quantiles are assigned independently within each group.

      Returns this

      The table, so methods can be chained.

      // Assigns a quantile from 1 to 10 for each row in a new 'quantiles' column, based on 'column1' values.
      await table.quantiles("column1", 10, "quantiles").log();
      // Assign quantiles by 'column2', based on 'column1' values.
      await table.quantiles("column1", 10, "quantiles", { by: "column2" }).log();
      // Assigns quartiles (4 quantiles) to 'sales' data, storing results in 'salesQuartile'
      await table.quantiles("sales", 4, "salesQuartile").log();
    • Assigns ranks to rows in a new column based on the values of a specified column.

      Parameters

      • column: string

        The column containing the values to be used for ranking.

      • newColumn: string

        The name of the new column where the ranks will be stored.

      • options: { by?: string | string[]; dense?: boolean; order?: "asc" | "desc" } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to rank by. Ranks are assigned independently within each group.

        • Optionaldense?: boolean

          A boolean indicating whether to use dense ranking (no gaps). If true, ranks will be consecutive integers (e.g., 1, 2, 2, 3). If false (default), ranks might have gaps (e.g., 1, 2, 2, 4).

        • Optionalorder?: "asc" | "desc"

          The order of values for ranking: "asc" for ascending (default) or "desc" for descending.

      Returns this

      The table, so methods can be chained.

      // Compute ranks in a new 'rank' column based on 'score' values (ascending)
      await table.ranks("score", "rank").log();
      // Compute ranks in a new 'descRank' column based on 'score' values (descending)
      await table.ranks("score", "descRank", { order: "desc" }).log();
      // Compute ranks by 'department', based on 'salary' values, without gaps
      await table.ranks("salary", "salaryRank", { by: "department", dense: true }).log();
      // Compute ranks by both 'department' and 'city'
      await table.ranks("sales", "salesRank", { by: ["department", "city"] }).log();
    • Computes rolling aggregations (e.g., rolling average, min, max) over a specified column. For rows without enough preceding or following rows to form a complete window, NULL will be returned. For this method to work properly, ensure your data is sorted by the relevant column(s) first.

      Parameters

      • column: string

        The name of the column storing the values to be aggregated.

      • newColumn: string

        The name of the new column in which the computed rolling values will be stored.

      • stat: "min" | "max" | "mean" | "median" | "sum"

        The aggregation function to apply: "min", "max", "mean", "median", or "sum".

      • preceding: number

        The number of preceding rows to include in the rolling window.

      • following: number

        The number of following rows to include in the rolling window.

      • options: { by?: string | string[]; decimals?: number } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to partition by. Rolling statistics are computed independently within each group.

        • Optionaldecimals?: number

          The number of decimal places to round the aggregated values. Defaults to undefined (no rounding).

      Returns this

      The table, so methods can be chained.

      // Compute a 7-day rolling average of 'sales' with 3 preceding and 3 following rows
      // (total window size of 7: 3 preceding + current + 3 following)
      await table.rolling("sales", "rollingAvgSales", "mean", 3, 3).log();
      // Compute a rolling sum of 'transactions' by 'customer_id'
      await table.rolling("transactions", "rollingSumTransactions", "sum", 5, 0, { by: "customer_id" }).log();
      // Compute a rolling maximum of 'temperature' rounded to 1 decimal place
      await table.rolling("temperature", "rollingMaxTemp", "max", 2, 2, { decimals: 1 }).log();
    • Computes proportions horizontally across specified columns for each row.

      For example, given a table showing counts of men, women, and non-binary individuals per year:

      Year Men Women NonBinary
      2021 564 685 145
      2022 354 278 56
      2023 856 321 221

      This method computes the proportion of men, women, and non-binary individuals on each row, adding new columns for these proportions.

      Parameters

      • columns: string[]

        An array of column names for which proportions will be computed on each row.

      • options: { base?: number; decimals?: number; suffix?: string } = {}

        An optional object with configuration options:

        • Optionalbase?: number

          A finite positive value that the proportions in each row sum to before rounding. Defaults to 1.

        • Optionaldecimals?: number

          The number of decimal places to round the computed proportions. Defaults to undefined (no rounding).

        • Optionalsuffix?: string

          A string suffix to append to the names of the new columns storing the computed proportions. Defaults to "Perc".

      Returns this

      The table, so methods can be chained.

      // Compute horizontal proportions for 'Men', 'Women', and 'NonBinary' columns, rounded to 2 decimal places
      await table.rowProportions(["Men", "Women", "NonBinary"], { decimals: 2 }).log();

      The table will then look like this:

      Year Men Women NonBinary MenPerc WomenPerc NonBinaryPerc
      2021 564 685 145 0.4 0.49 0.10
      2022 354 278 56 0.51 0.4 0.08
      2023 856 321 221 0.61 0.23 0.16

      By default, the new columns will be named with a suffix of "Perc". You can customize this suffix using the suffix option.

      // Compute horizontal proportions with a custom suffix "Prop"
      await table.rowProportions(["Men", "Women", "NonBinary"], { suffix: "Prop", decimals: 2 }).log();

      The table will then look like this:

      Year Men Women NonBinary MenProp WomenProp NonBinaryProp
      2021 564 685 145 0.4 0.49 0.10
      2022 354 278 56 0.51 0.4 0.08
      2023 856 321 221 0.61 0.23 0.16
      // Compute percentages that sum to 100 on each row before rounding
      await table.rowProportions(["Men", "Women", "NonBinary"], { base: 100, decimals: 1 }).log();
    • Selects a ranked numeric value within each row and adds its source column name, its value, or both as new columns.

      Values are ranked from highest to lowest by default. Null values are ignored. By default, a tie at the requested rank throws an error. Set options.ties to "first" to select the first tied column in the supplied order, or to "all" to produce one row for each tied column. The "all" option can therefore increase the table's row count. If null values leave a row without the requested rank, the new columns contain null.

      Parameters

      • columns: string[]

        The numeric columns to rank within each row.

      • options: (
            | { nameColumn: string; valueColumn?: string }
            | { nameColumn?: string; valueColumn: string }
        ) & {
            order?: "asc" | "desc";
            rank?: number;
            ties?: "all" | "strict" | "first";
        }

        The output columns and ranking configuration. At least one of nameColumn or valueColumn is required.

        • { nameColumn: string; valueColumn?: string }
          • nameColumn: string

            The name of a new column containing the selected source column's name.

            { nameColumn: "winner" }
            
          • OptionalvalueColumn?: string

            The name of a new column containing the selected source column's value.

            { valueColumn: "winningVotes" }
            
        • { nameColumn?: string; valueColumn: string }
          • OptionalnameColumn?: string

            The name of a new column containing the selected source column's name.

            { nameColumn: "winner" }
            
          • valueColumn: string

            The name of a new column containing the selected source column's value.

            { valueColumn: "winningVotes" }
            
        • Optionalorder?: "asc" | "desc"

          The ranking order: "desc" ranks the highest value first and "asc" ranks the lowest value first. Defaults to "desc".

          { order: "asc" }
          
        • Optionalrank?: number

          The one-based rank to select. Must not exceed the number of supplied columns. Defaults to 1.

          { rank: 2 }
          
        • Optionalties?: "all" | "strict" | "first"

          How to handle a tie at the requested rank: "strict" throws, "first" selects the first supplied column, and "all" produces one row per tied column. Defaults to "strict".

          { ties: "all" }
          

      Returns this

      The table, so methods can be chained.

      // Add the name and value of the highest-scoring party on each row.
      await table.rowRanks(["CAQ", "PLQ", "PQ"], {
      nameColumn: "winner",
      valueColumn: "winningVotes",
      }).log();
      // Add only the second-lowest value on each row.
      await table.rowRanks(["CAQ", "PLQ", "PQ"], {
      valueColumn: "secondLowestVotes",
      rank: 2,
      order: "asc",
      }).log();
    • Measures numeric profile similarity using each row's Mahalanobis distance from a supplied reference point and stores it in a new DOUBLE column. Smaller distances indicate more similar profiles. Optionally adds a dataset-relative similarity score, where larger values mean more similar. Sample covariance (n - 1) is estimated from the dataset, independently of the reference point.

      Accounts for differences in feature scales and correlations between features, making it useful for comparing profiles with measurements in different units.

      Pass one numeric LIST or ARRAY column, or an array of numeric scalar columns. Reference arrays follow the same dimension order. For scalar columns, a reference object supplies its own finite numeric values by column name; extra fields are ignored. Reference values are captured when this method is called. Inputs are converted privately to DOUBLE, which can lose precision for large integers and exact decimals; source columns and types remain unchanged.

      Requires more rows than dimensions and finite, non-null, consistent features with invertible, numerically stable covariance. Invalid inputs leave the source unchanged.

      Parameters

      • columns: string | string[]

        A numeric vector column, or numeric scalar columns in feature order.

      • referencePoint: number[] | { [key: string]: unknown }

        An array of finite numbers in feature order, or an object with an own finite numeric value for each scalar feature column; may be outside the dataset.

      • newColumn: string

        The name of the new DOUBLE distance column.

      • options: { similarityColumn?: string | boolean } = {}

        Optional output settings.

        • OptionalsimilarityColumn?: string | boolean

          A custom name, or true for a new DOUBLE column named "similarity"; false or omitted adds no score. The dataset-relative score is 1 - distance / maxDistance. Exact matches score 1 and the farthest rows score 0; if all distances are zero, every score is 1.

      Returns this

      The table, so methods can be chained.

      // Measure distance from a reference height and weight.
      await table
      .similarityMahalanobis(["height", "weight"], [175, 70], "distance")
      .log();
      // Compare feature vectors and add a dataset-relative similarity score.
      await table
      .similarityMahalanobis("features", [175, 70], "distance", {
      similarityColumn: "similarity",
      })
      .log();
      // Compare profiles directly with a row and use the default score column.
      const reference = await table.getRow("name === 'Alex'");
      await table
      .similarityMahalanobis(["height", "weight"], reference, "distance", {
      similarityColumn: true,
      })
      .log();
    • Creates a summary table from selected columns, optionally grouped by other columns. This method allows you to aggregate data, calculate statistics (e.g., count, mean, sum), and group results by categorical columns.

      Parameters

      • options: {
            by?: string | string[];
            columns?: string | string[];
            datesToMs?: boolean;
            decimals?: number;
            outputTable?: string | boolean;
            stats?:
                | "count"
                | "countDistinct"
                | "countNull"
                | "min"
                | "max"
                | "mean"
                | "median"
                | "sum"
                | "skew"
                | "stdDev"
                | "variance"
                | (
                    | "count"
                    | "countDistinct"
                    | "countNull"
                    | "min"
                    | "max"
                    | "mean"
                    | "median"
                    | "sum"
                    | "skew"
                    | "stdDev"
                    | "variance"
                )[]
                | {
                    [key: string]: | "count"
                    | "countDistinct"
                    | "countNull"
                    | "min"
                    | "max"
                    | "mean"
                    | "median"
                    | "sum"
                    | "skew"
                    | "stdDev"
                    | "variance";
                };
        } = {}

        An object with configuration options for summarization:

        • Optionalby?: string | string[]

          The column name or an array of column names to group by.

        • Optionalcolumns?: string | string[]

          The column name or an array of column names to summarize. If omitted, only the row count is returned.

        • OptionaldatesToMs?: boolean

          If true, timestamps, dates, and times will be converted to milliseconds before summarizing. This is useful when summarizing mixed data types (numbers and dates) as columns must be of the same type for aggregation.

        • Optionaldecimals?: number

          The number of decimal places to round the summarized columns. Defaults to undefined (no rounding).

        • OptionaloutputTable?: string | boolean

          If true, the results will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be overwritten. Defaults to false.

        • Optionalstats?:
              | "count"
              | "countDistinct"
              | "countNull"
              | "min"
              | "max"
              | "mean"
              | "median"
              | "sum"
              | "skew"
              | "stdDev"
              | "variance"
              | (
                  | "count"
                  | "countDistinct"
                  | "countNull"
                  | "min"
                  | "max"
                  | "mean"
                  | "median"
                  | "sum"
                  | "skew"
                  | "stdDev"
                  | "variance"
              )[]
              | {
                  [key: string]: | "count"
                  | "countDistinct"
                  | "countNull"
                  | "min"
                  | "max"
                  | "mean"
                  | "median"
                  | "sum"
                  | "skew"
                  | "stdDev"
                  | "variance";
              }

          The statistics to compute. Can be a single statistic (e.g., "mean"), an array (e.g., ["min", "max"]), or an object mapping output column names to statistics (e.g., { avgSalary: "mean" }). Supported statistics are "count", "countDistinct", "countNull", "min", "max", "mean", "median", "sum", "skew", "stdDev", and "variance".

      Returns this

      A table instance containing the summarized data (either the current table or a new table), so methods can be chained. When summarizing more than one column, a column column identifies which input column each row summarizes.

      // Summarize all columns with all available statistics, overwriting the current table
      const columns = await table.getColumns();
      await table.summarize({ columns }).log();
      // Summarize all columns and store the results in a new table with a generated name
      const columns = await table.getColumns();
      const summaryTable = await table.summarize({ columns, outputTable: true }).log();
      // Summarize all columns and store the results in a new table named 'mySummary'
      const columns = await table.getColumns();
      const mySummaryTable = await table.summarize({ columns, outputTable: "mySummary" }).log();
      // Summarize a single column ('sales') with all available statistics
      await table.summarize({ columns: "sales" }).log();
      // Summarize multiple columns ('sales' and 'profit') with all available statistics
      await table.summarize({ columns: ["sales", "profit"] }).log();
      // Summarize 'sales' by 'region' (single category)
      await table.summarize({ columns: "sales", by: "region" }).log();
      // Summarize 'sales' by 'region' and 'product_type'
      await table.summarize({ columns: "sales", by: ["region", "product_type"] }).log();
      // Summarize 'sales' by 'region' with a specific statistic (mean)
      await table.summarize({ columns: "sales", by: "region", stats: "mean" }).log();
      // Summarize 'sales' by 'region' with specific statistics (mean and sum)
      await table.summarize({ columns: "sales", by: "region", stats: ["mean", "sum"] }).log();
      // Summarize 'sales' by 'region' with custom named statistics
      await table.summarize({ columns: "sales", by: "region", stats: { averageSales: "mean", totalSales: "sum" } }).log();
      // Summarize 'price' and 'cost', rounding aggregated columns to 2 decimal places
      await table.summarize({ columns: ["price", "cost"], decimals: 2 }).log();
      // Summarize 'timestamp_column' by converting to milliseconds first
      await table.summarize({ columns: "timestamp_column", datesToMs: true, stats: "mean" }).log();
    • Computes the Z-score for values in a specified column.

      Parameters

      • column: string

        The name of the column for which Z-scores will be calculated.

      • newColumn: string

        The name of the new column where the computed Z-scores will be stored.

      • options: { by?: string | string[]; decimals?: number } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to partition by. Z-scores are calculated independently within each group.

        • Optionaldecimals?: number

          The number of decimal places to round the Z-score values. Defaults to undefined (no rounding).

      Returns this

      The table, so methods can be chained.

      // Calculate the Z-score for 'age' values and store results in a new 'ageZScore' column
      await table.zScore("age", "ageZScore").log();
      // Calculate Z-scores for 'salary' by 'department'
      await table.zScore("salary", "salaryZScore", { by: "department" }).log();
      // Calculate Z-scores for 'score', rounded to 2 decimal places
      await table.zScore("score", "scoreZScore", { decimals: 2 }).log();
    • Caches the results of computations in ./.sda-cache. You should add ./.sda-cache to your .gitignore file.

      Callback code and function/class inputs ignore comments, indentation, and line wrapping where safe.

      cache() automatically tracks whether earlier SDA operations changed the table. It also records every other already registered SimpleTable read through SimpleTable methods while compute runs and invalidates the cached step when any of their generations change. Tables created inside compute are part of the computation itself and are not dependencies.

      Inside compute, you may modify the table being cached and create or modify temporary tables, provided you remove them before the callback finishes. Other tables that already existed may be read but not modified, renamed, or removed. customQuery() bypasses enforcement of these rules.

      SimpleDB.customQuery() bypasses this tracking. Reading or changing a table with customQuery() can therefore return stale cached data. Include a value that identifies the custom query's dependencies in options.inputs (such as a table content hash), or use tracked SimpleTable methods.

      Cache entries are stored as DuckDB database files. Full-text search (FTS) indexes are persisted in the cache file and restored directly on a cache hit. Vector similarity search (VSS/HNSW) indexes are not persisted in the cache file; their definitions are stored as metadata and used to rebuild the indexes on every cache hit. If loading the entry or restoring its indexes fails, the computation runs again and replaces the cache entry.

      Parameters

      • compute: (table: this) => void | Promise<void>

        A function wrapping the computations to be cached. It receives the table on which cache() was called. This function will be executed on the first run or if the cached data is invalid/expired.

      • options: { inputs?: readonly unknown[]; ttl?: number } = {}

        An optional object with configuration options:

        • Optionalinputs?: readonly unknown[]

          An ordered array of additional values captured by compute that affect its result. Each position is compared structurally across runs, so adding, removing, moving, or changing an input invalidates the cache. Functions and class constructors are compared by source. SimpleTable dependencies read by compute are tracked automatically, and the table being cached is already tracked, so neither needs to be included here.

        • Optionalttl?: number

          Cache lifetime in seconds. Omit for no expiration, use 0 to refresh the matching cache entry immediately, or provide a positive value to refresh once the entry reaches that age. The cache is also invalidated when the compute function, the table, or an input changes.

      Returns Promise<SimpleTable>

      A promise that resolves to the table, so methods can be chained.

      // Computations are re-run if the callback changes or earlier operations modify the table
      const sdb = new SimpleDB();
      const items = await sdb.newTable("items").cache((table) => {
      table
      .loadData("items.csv")
      .summarize({
      columns: "price",
      by: "department",
      stats: ["min", "max", "mean"],
      });
      });
      await items.log();

      // It's important to call close() on the SimpleDB instance to clean up the cache.
      // This prevents the cache from growing indefinitely.
      await sdb.close();
      // Cache with a Time-To-Live (TTL) of 60 seconds
      // The computations will be re-run if the cached data is older than 1 minute, the callback changes, or the table changes.
      const sdb = new SimpleDB();
      const table = await sdb.newTable().cache((table) => {
      table
      .loadData("items.csv")
      .summarize({
      columns: "price",
      by: "department",
      stats: ["min", "max", "mean"],
      });
      }, { ttl: 60 });
      await table.log();

      await sdb.close();
      // Enable verbose logging for cache operations via SimpleDB instance
      const sdb = new SimpleDB({ cacheVerbose: true });
      const table = await sdb.newTable().cache((table) => {
      table
      .loadData("items.csv")
      .summarize({
      columns: "price",
      by: "department",
      stats: ["min", "max", "mean"],
      });
      });
      await table.log();

      await sdb.close();
      // Read-only table dependencies are tracked automatically. Other captured values go in inputs.
      const year = 2026;
      const summary = await sdb.newTable("summary").cache(async (table) => {
      table.loadArray(
      await fires.getData({ conditions: `year = ${year}` }),
      );
      }, { inputs: [year] });
      await summary.log();
    • Adds a new column to the table based on a specified data type (JavaScript or SQL types) and a SQL definition. With the default SimpleDB.expressionSyntax: "js", expressions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Parameters

      • newColumn: string

        The name of the new column to be added.

      • type:
            | "string"
            | "number"
            | "bigint"
            | "boolean"
            | "date"
            | "integer"
            | "float"
            | "time"
            | "datetime"
            | "datetimeTz"
            | "double"
            | "varchar"
            | "timestamp"
            | "timestamp with time zone"
            | "json"
            | "JSON"
            | `float[${number}]`
            | `FLOAT[${number}]`
            | `geometry('${string}')`
            | `GEOMETRY('${string}')`

        The data type for the new column. Can be a JavaScript type (e.g., "number", "string") or a SQL type (e.g., "integer", "varchar", "JSON", "FLOAT[3]", "GEOMETRY('EPSG:4326')").

      • definition: string

        A SQL expression defining how the values for the new column should be computed (e.g., "column1 + column2", "ST_Centroid(geom_column)").

      Returns this

      The table, so methods can be chained.

      // Add a new column 'total' as a float, calculated from 'column1' and 'column2'
      await table.addColumn("total", "float", "column1 + column2").log();
      // Add a new geometry column 'centroid' using the centroid of an existing 'country' geometry column
      await table.addColumn("centroid", "geometry('EPSG:4326')", `ST_Centroid("country")`).log();
      // Read a number from a JSON object into a new column using dot notation.
      await table.loadArray([{ details: { count: 3 } }], {
      columnTypes: { details: "JSON" },
      })
      .addColumn("count", "integer", "details.count")
      .log();
      // Create JSON and fixed-size vector columns with DuckDB expressions.
      await table.loadArray([{ count: 3 }])
      .addColumn("details", "JSON", "json_object('count', count)")
      .addColumn("embedding", "FLOAT[3]", "[0.25, 0.5, 0.75]")
      .log();
    • Adds a unique, non-null ID for each row in the current table. IDs start at 0 and identify rows within this table, including rows with identical contents. Without a prefix, the new column contains numbers. When a prefix is supplied, the new column contains strings with the exact prefix followed by the row number. An empty prefix therefore creates the strings "0", "1", and so on.

      IDs are not globally unique: separate tables can generate the same IDs.

      Parameters

      • newColumn: string

        The name of the new ID column.

      • options: { prefix?: string } = {}

        An optional object with configuration options.

        • Optionalprefix?: string

          Text to place before each row number. Supplying this option, including an empty string, creates string IDs.

      Returns this

      The table, so methods can be chained.

      // Add numeric IDs: 0, 1, 2, ...
      await table
      .addId("edgeId")
      .log();
      // Add string IDs: "flight-0", "flight-1", "flight-2", ...
      await flights
      .addId("edgeId", { prefix: "flight-" })
      .log();
    • Adds a new column to the table containing the row number, starting at 0 (like an index).

      Parameters

      • newColumn: string

        The name of the new column that will store the row number.

      • options: { by?: string | string[] } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          A column name or an array of column names to partition by. The row number restarts at 0 within each group.

      Returns this

      The table, so methods can be chained.

      // Add a new column named 'rowNumber' with the row number for each row
      await table.addRowNumber("rowNumber").log();
      // Add a new column named 'rowNumber' with the row number for each 'category'
      await table.addRowNumber("rowNumber", { by: "category" }).log();
    • Cleans column names by removing non-alphanumeric characters and formatting them to camel case.

      Returns this

      The table, so methods can be chained.

      // Clean all column names in the table
      // e.g., "First Name" becomes "firstName", "Product ID" becomes "productId"
      await table.cleanColumnNames().log();
    • Clones an existing column in this table, creating a new column with identical values.

      Parameters

      • column: string

        The name of the original column to clone.

      • newColumn: string

        The name of the new column to be created.

      Returns this

      The table, so methods can be chained.

      // Clone 'firstName' column as 'contactName'
      await table.cloneColumn("firstName", "contactName").log();
    • Clones a column in the table and offsets its values by a specified number of rows. This is useful for time-series analysis or comparing values across different time points.

      Important: The offset is applied based on the current row order in the table. For meaningful results, ensure your data is sorted appropriately (e.g., by date/time for time-series analysis) before calling this method.

      Parameters

      • column: string

        The name of the original column.

      • newColumn: string

        The name of the new column to be created with offset values.

      • options: { by?: string | string[]; offset?: number } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          A column name or an array of column names to partition by. The offset is applied independently within each group.

        • Optionaloffset?: number

          The number of rows to offset the values. A positive number shifts values downwards (later rows), a negative number shifts values upwards (earlier rows). Defaults to 1.

      Returns this

      The table, so methods can be chained.

      // Clone 'value' as 'previous_value', offsetting by 1 row (value of row N-1 goes to row N)
      await table.cloneColumnWithOffset("value", "previous_value").log();
      // Clone 'sales' as 'sales_2_days_ago', offsetting by 2 rows
      await table.cloneColumnWithOffset("sales", "sales_2_days_ago", { offset: 2 }).log();
      // Clone 'temperature' as 'prev_temp_by_city', offsetting by 1 row within each 'city' category
      await table.cloneColumnWithOffset("temperature", "prev_temp_by_city", {
      offset: 1,
      by: "city",
      }).log();
      // Clone 'stock_price' as 'prev_price_by_stock_and_exchange', offsetting by 1 row within each 'stock_symbol' and 'exchange' category
      await table.cloneColumnWithOffset("stock_price", "prev_price_by_stock_and_exchange", {
      offset: 1,
      by: ["stock_symbol", "exchange"],
      }).log();
    • Extracts one or more components from a temporal column into new columns. Pass a single part to create a column with that part's name, or pass an object mapping custom new-column names to parts. Existing columns are not overwritten.

      dayOfWeek uses Sunday as 0 through Saturday as 6. week follows ISO week numbering, and dayOfYear starts at 1. DuckDB DATE, TIME, TIMESTAMP, and TIMESTAMP WITH TIME ZONE columns are supported when the requested component applies to that type: date parts apply to dates and timestamps, while time parts apply to times and timestamps. NULL input values produce NULL extracted values. Parts extracted from TIMESTAMP WITH TIME ZONE values use UTC.

      Parameters

      • column: string

        The temporal column from which to extract components.

      • parts:
            | "day"
            | "month"
            | "year"
            | "quarter"
            | "week"
            | "dayOfWeek"
            | "dayOfYear"
            | "hour"
            | "minute"
            | "second"
            | {
                [newColumn: string]: | "day"
                | "month"
                | "year"
                | "quarter"
                | "week"
                | "dayOfWeek"
                | "dayOfYear"
                | "hour"
                | "minute"
                | "second";
            }

        A part to extract using its name as the new column, or an object mapping each custom new-column name to the part it should contain.

      Returns this

      The table, so methods can be chained.

      // Add a column named 'year' from the 'publishedAt' timestamp
      await table.extractDatePart("publishedAt", "year").log();
      // Extract multiple components with custom column names
      await table.extractDatePart("publishedAt", {
      publicationYear: "year",
      publicationMonth: "month",
      }).log();
    • Checks if a column with the specified name exists in the table.

      Parameters

      • column: string

        The name of the column to check.

      Returns Promise<boolean>

      A promise that resolves to true if the column exists, false otherwise.

      // Check if the table has a column named "age"
      const hasAgeColumn = await table.hasColumn("age");
      console.log(hasAgeColumn); // Output: true or false
    • Removes one or more columns from this table.

      Parameters

      • columns: string | string[]

        The name or an array of names of the columns to be removed.

      Returns this

      The table, so methods can be chained.

      // Remove 'column1' and 'column2' from the table
      await table.removeColumns(["column1", "column2"]).log();
      // Remove a single column named 'tempColumn'
      await table.removeColumns("tempColumn").log();
    • Renames one or more columns in the table. Throws if a source column does not exist, so a typo fails loudly instead of being silently ignored.

      Parameters

      • names: { [key: string]: string }

        An object mapping old column names to their new column names (e.g., { "oldName": "newName", "anotherOld": "anotherNew" }).

      • options: { strict?: boolean } = {}

        Configuration options.

        • Optionalstrict?: boolean

          Whether to verify the source columns exist before renaming. Defaults to true. Set to false to skip the check and its schema lookup when you know the columns exist and are renaming across many tables where the extra round-trip adds up.

      Returns this

      The table, so methods can be chained.

      // Rename "How old?" to "age" and "Man or woman?" to "sex"
      await table.renameColumns({ "How old?": "age", "Man or woman?": "sex" }).log();
      // Rename a single column
      await table.renameColumns({ "product_id": "productId" }).log();
      // Skip the existence check when renaming across many tables
      await table.renameColumns({ "product_id": "productId" }, { strict: false }).log();
    • Creates an instance of SimpleTable.

      Parameters

      • name: string

        The name of the table.

      • simpleDB: SimpleDB

        The SimpleDB instance that this table belongs to.

      • options: { charsToLog?: number; rowsToLog?: number; typesToLog?: boolean } = {}

        An optional object with configuration options:

        • OptionalcharsToLog?: number

          The maximum characters to display for text and stringified nested cells, including truncation markers (default: 75). Independent of column width. Use Infinity for unlimited text.

        • OptionalrowsToLog?: number

          The number of rows to log when displaying table data.

        • OptionaltypesToLog?: boolean

          A boolean indicating whether to include data types when logging a table.

      Returns SimpleTable

    • Writes the table's data to a file in various formats (CSV, JSON, Parquet, DuckDB, SQLite). Tables with geometry columns must use writeGeoData() instead. If the specified path does not exist, it will be created.

      Parameters

      • file: string

        The absolute path to the output file (e.g., "./output.csv", "./output.json").

      • options: { compression?: boolean; dataAsArrays?: boolean; formatDates?: boolean } = {}

        An optional object with configuration options:

        • Optionalcompression?: boolean

          A boolean indicating whether to compress the output file. If true, CSV and JSON files will be compressed with GZIP, while Parquet files will use ZSTD. Defaults to false.

        • OptionaldataAsArrays?: boolean

          For JSON files only. If true, JSON files are written as a single object with arrays for each column (e.g., { "col1": [v1, v2], "col2": [v3, v4] }) instead of an array of objects. This can reduce file size for web projects. You can use the arraysToData function from the journalism-format library to convert it back.

        • OptionalformatDates?: boolean

          For CSV and JSON files only. If true, date and timestamp columns will be formatted as ISO 8601 strings (e.g., "2025-01-01T01:00:00.000Z"). Defaults to false.

      Returns Promise<SimpleTable>

      A promise that resolves to the table, so methods can be chained.

      // Write data to a CSV file
      await table.writeData("./output.csv");
      // Write data to a JSON file with GZIP compression.
      // The output file will be named output.json.gz.
      await table.writeData("./output.json", { compression: true });
      // Write data to a Parquet file
      await table.writeData("./output.parquet");
      // Write data to a DuckDB database file
      await table.writeData("./my_database.db");
      // Write data to a SQLite database file
      await table.writeData("./my_database.sqlite");
      // Write JSON data with dates formatted as ISO strings
      await table.writeData("./output_dates.json", { formatDates: true });
    • Writes the table's geospatial data to a file in GeoJSON, GeoParquet, or Shapefile format. If the specified path does not exist, it will be created.

      Parameters

      • file: string

        The absolute path to the output file (e.g., "./output.geojson", "./output.geoparquet", "./shapefile-folder/output.shp", "./output.shp.zip"). A .shp.zip extension writes a ZIP archive using fast DEFLATE compression. Creating the archive temporarily requires enough disk space for both the uncompressed Shapefile and the ZIP, and ZIP archives are limited to 4 GB.

      • options: {
            compression?: boolean;
            formatDates?: boolean;
            metadata?: unknown;
            precision?: number;
            rewind?: boolean;
        } = {}

        An optional object with configuration options:

        • Optionalcompression?: boolean

          For GeoParquet, if true, uses ZSTD compression; otherwise, uses DuckDB's default SNAPPY compression. SNAPPY prioritizes faster compression, while ZSTD typically produces smaller files but takes longer to write. Read performance depends on the data and storage because smaller files can reduce I/O. This option is not supported for GeoJSON or Shapefiles. Defaults to false.

        • OptionalformatDates?: boolean

          For GeoJSON, if true, formats date and timestamp columns to ISO 8601 strings. Defaults to false.

        • Optionalmetadata?: unknown

          For GeoJSON, an object to be added as top-level metadata to the GeoJSON output.

        • Optionalprecision?: number

          For GeoJSON, the maximum number of figures after the decimal separator to write in coordinates. Defaults to undefined (full precision).

        • Optionalrewind?: boolean

          For GeoJSON, if true, rewinds the coordinates of polygons to follow the right-hand rule (RFC 7946). Defaults to false.

      Returns Promise<SimpleTable>

      A promise that resolves to the table, so methods can be chained.

      // Write geospatial data to a GeoJSON file
      await table.writeGeoData("./output.geojson");
      // Write geospatial data to a ZSTD-compressed GeoParquet file
      await table.writeGeoData("./output.geoparquet", { compression: true });
      // Write geospatial data to a Shapefile with all relevant files  in the same folder
      await table.writeGeoData("./shapefile-folder/output.shp");
      // Write a Shapefile and its related files to output.shp.zip
      await table.writeGeoData("./output.shp.zip");
      // Write GeoJSON with specific precision and metadata
      await table.writeGeoData("./output_high_precision.geojson", {
      precision: 6,
      metadata: { source: "SimpleDataAnalysis" },
      });
    • Adds the bounding box coordinates of geometries in a specified column as four new columns: minLon, minLat, maxLon, and maxLat.

      Parameters

      • options: { column?: string; decimals?: number } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries for which the bounding box will be computed. If omitted, the method will automatically attempt to find a geometry column.

        • Optionaldecimals?: number

          The number of decimal places to round the bounding box coordinates. Defaults to undefined (no rounding).

      Returns this

      The table, so methods can be chained.

      // Compute the bounding box for geometries in the default column
      await table.addBoundingBox().log();
      // The table now has minLon, minLat, maxLon, and maxLat columns.
      // Compute the bounding box for geometries in 'geom' column and round coordinates to 2 decimal places
      await table.addBoundingBox({ column: "geom", decimals: 2 }).log();
      // The table now has minLon, minLat, maxLon, and maxLat columns with values rounded to 2 decimal places.
    • Adds a column with boolean values indicating whether geometries are closed (e.g., polygons) or open (e.g., linestrings).

      Parameters

      • newColumn: string

        The name of the new column where the boolean results (TRUE for closed, FALSE for open) will be stored.

      • options: { column?: string } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Check if geometries are closed and store results in a new 'isClosed' column
      await table.addGeoClosedStatus("isClosed").log();
      // Check closed status of geometries in a specific column named 'boundaryGeom'
      await table.addGeoClosedStatus("boundaryClosed", { column: "boundaryGeom" }).log();
    • Moves EPSG:4326 point geometries to random locations within a maximum great-circle distance of their original positions.

      Points are sampled uniformly within the requested distance using the spherical direct geodesic (destination-point) formula and the same spherical Earth model as DuckDB's ST_Distance_Sphere(). This accounts for longitude scale changing with latitude. This method adds random jitter; it does not provide anonymization or differential privacy guarantees.

      This method supports only POINT geometries in EPSG:4326. Null and empty geometries are preserved.

      Parameters

      • maxDistance: number

        The maximum great-circle displacement in the selected unit. Must be a finite number greater than or equal to 0 and no greater than half Earth's circumference.

      • options: { column?: string; onlyDuplicates?: boolean; unit?: "m" | "km" } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the EPSG:4326 point geometry column. If omitted, the method will automatically attempt to find a geometry column.

        • OptionalonlyDuplicates?: boolean

          If true, moves only points whose complete original geometry occurs more than once in the selected geometry column. Every point in a duplicated group is moved. Defaults to false.

        • Optionalunit?: "m" | "km"

          The unit of maxDistance: "m" for metres or "km" for kilometres. Defaults to "m".

      Returns this

      The table, so methods can be chained.

      // Move every point by no more than 500 metres
      await table.addGeoNoise(500).log();
      // Separate duplicated points by up to 0.1 kilometres
      await table.addGeoNoise(0.1, {
      column: "geom",
      unit: "km",
      onlyDuplicates: true,
      }).log();
    • Adds a column with the geometry type (e.g., "POINT", "LINESTRING", "POLYGON") for each geometry.

      Parameters

      • newColumn: string

        The name of the new column where the geometry types will be stored.

      • options: { column?: string } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Add a new column 'geometryType' with the type of each geometry
      await table.addGeoType("geometryType").log();
      // Get the geometry type for geometries in a specific column named 'featureGeom'
      await table.addGeoType("featureType", { column: "featureGeom" }).log();
    • Adds a column with boolean values indicating the validity of geometries.

      Parameters

      • newColumn: string

        The name of the new column where the boolean results (TRUE for valid, FALSE for invalid) will be stored.

      • options: { column?: string } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries to be checked. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Check if geometries are valid and store results in a new 'isValid' column
      // The method will automatically detect the geometry column.
      await table.addGeoValidity("isValid").log();
      // Check validity of geometries in a specific column named 'myGeom'
      await table.addGeoValidity("isValidMyGeom", { column: "myGeom" }).log();
    • Adds a column with the number of vertices (points) in each geometry.

      Parameters

      • newColumn: string

        The name of the new column where the vertex counts will be stored.

      • options: { column?: string } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Add a new column 'vertexCount' with the number of vertices for each geometry
      // The method will automatically detect the geometry column.
      await table.addVertexCount("vertexCount").log();
      // Add vertex counts for geometries in a specific column named 'myGeom'
      await table.addVertexCount("myGeomVertices", { column: "myGeom" }).log();
    • Aggregates geometries in a specified column based on a chosen aggregation method.

      Parameters

      • method: "union" | "intersection"

        The aggregation method to apply: "union" (combines all geometries into a single multi-geometry) or "intersection" (computes the intersection of all geometries).

      • options: { by?: string | string[]; column?: string; outputTable?: string | boolean } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          The column name or an array of column names to group by. Geometries are aggregated independently within each group.

        • Optionalcolumn?: string

          The name of the column storing the geometries to be aggregated. If omitted, the method will automatically attempt to find a geometry column.

        • OptionaloutputTable?: string | boolean

          If true, the results will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be overwritten. Defaults to false.

      Returns this

      A table instance containing the aggregated geometries (either the current table or a new table), so methods can be chained.

      // Aggregate all geometries in the default column into a single union geometry
      await table.aggregateGeo("union").log();
      // Aggregate geometries by 'country' and compute their union
      await table.aggregateGeo("union", { by: "country" }).log();
      // Aggregate geometries in 'regions' column into their intersection, storing results in a new table
      const intersectionTable = await table.aggregateGeo("intersection", { column: "regions", outputTable: true }).log();
    • Computes the area of geometries in square meters ("m2") or optionally square kilometers ("km2"). The input geometry is assumed to be in EPSG:4326 (WGS84).

      Parameters

      • newColumn: string

        The name of the new column where the computed areas will be stored.

      • options: { column?: string; decimals?: number; unit?: "m2" | "km2" } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

        • Optionaldecimals?: number

          The number of decimal places to round the computed areas. Defaults to undefined (no rounding).

        • Optionalunit?: "m2" | "km2"

          The unit for the computed area: "m2" (square meters) or "km2" (square kilometers). Defaults to "m2".

      Returns this

      The table, so methods can be chained.

      // Compute the area of geometries in square meters and store in 'area_m2'
      await table.area("area_m2").log();
      // Compute the area of geometries in square kilometers and store in 'area_km2'
      await table.area("area_km2", { unit: "km2" }).log();
      // Compute areas in square kilometers rounded to two decimal places
      await table.area("area_km2", { unit: "km2", decimals: 2 }).log();
      // Compute the area of geometries in a specific column named 'myGeom'
      await table.area("myGeomArea", { column: "myGeom" }).log();
    • Computes a buffer (a polygon representing a specified distance around a geometry) for geometries in a specified column. The distance is in the Spatial Reference System (SRS) unit of the input geometries.

      Parameters

      • newColumn: string

        The name of the new column where the buffered geometries will be stored.

      • distance: number

        The distance for the buffer. This value is in the units of the geometry's SRS.

      • options: { column?: string } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Create a buffer of 1 unit around geometries in the default column, storing results in 'bufferedGeom'
      await table.buffer("bufferedGeom", 1).log();
      // Create a buffer of 10 units around geometries in a specific column named 'pointsGeom'
      await table.buffer("pointsBuffer", 10, { column: "pointsGeom" }).log();
    • Computes the centroid of geometries. The values are returned in the SRS unit of the input geometries.

      Parameters

      • newColumn: string

        The name of the new column where the computed centroid geometries will be stored.

      • options: { column?: string } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Compute the centroid of geometries in the default column, storing results in 'centerPoint'
      await table.centroid("centerPoint").log();
      // Compute the centroid of geometries in a specific column named 'areaGeom'
      await table.centroid("areaCentroid", { column: "areaGeom" }).log();
    • Returns TRUE if every point of a geometry in column is covered by a geometry in containerColumn, including their boundaries, and FALSE otherwise.

      Parameters

      • column: string

        The name of the column storing the geometries to be tested for containment.

      • containerColumn: string

        The name of the column storing the geometries to be tested as containers. Both columns must have the same projection.

      • newColumn: string

        The name of the new column where the boolean results (TRUE when covered, FALSE otherwise) will be stored.

      Returns this

      The table, so methods can be chained.

      // Check if geometries in 'pointGeom' are covered by 'polygonGeom', storing results in 'isCovered'
      await table.coveredBy("pointGeom", "polygonGeom", "isCovered").log();
    • Creates point geometries from latitude (y) and longitude (x) columns.

      Parameters

      • latColumn: string

        The name of the column storing the latitude (y-coordinate) values.

      • lonColumn: string

        The name of the column storing the longitude (x-coordinate) values.

      • newColumn: string

        The name of the new column where the point geometries will be stored.

      • options: { projection?: string } = {}

        An optional object with configuration options:

        • Optionalprojection?: string

          The projection of the coordinates. Defaults to EPSG:4326 (WGS84), passed as "EPSG:4326".

      Returns this

      The table, so methods can be chained.

      // Create point geometries in a new 'geom' column using latitude (y) and longitude (x) columns.
      // The resulting coordinates are ordered as [longitude, latitude], or [x, y].
      // The projection is assumed to be EPSG:4326 (WGS84).
      await table.createPoints("lat", "lon", "geom").log();
      // Create point geometries from coordinates in a projected coordinate system
      await table.createPoints("y", "x", "geom", { projection: "EPSG:3347" }).log();
    • Computes the geometric difference between two geometries, returning the portion of the first geometry that does not intersect the second.

      Parameters

      • column1: string

        The name of the column storing the geometries from which the second geometries will be subtracted.

      • column2: string

        The name of the column storing the geometries to subtract. Both columns must have the same projection.

      • newColumn: string

        The name of the new column where the geometric differences will be stored.

      Returns this

      The table, so methods can be chained.

      // Subtract 'geomB' from 'geomA', storing the result in 'geomA_minus_geomB'
      await table.difference("geomA", "geomB", "geomA_minus_geomB").log();
    • Computes the distance between geometries in two specified columns. By default, the distance is calculated in the Spatial Reference System (SRS) unit of the input geometries. You can optionally specify "spheroid" or "haversine" methods to get results in meters or kilometers. If using "spheroid" or "haversine", the input geometries must be in EPSG:4326 (WGS84).

      Parameters

      • column1: string

        The name of the first column storing geometries.

      • column2: string

        The name of the second column storing geometries.

      • newColumn: string

        The name of the new column where the computed distances will be stored.

      • options: {
            decimals?: number;
            method?: "srs" | "haversine" | "spheroid";
            unit?: "m" | "km";
        } = {}

        An optional object with configuration options:

        • Optionaldecimals?: number

          The number of decimal places to round the distance values. Defaults to undefined (no rounding).

        • Optionalmethod?: "srs" | "haversine" | "spheroid"

          The method to use for distance calculations: "srs" (default, uses SRS unit), "haversine" (meters, requires EPSG:4326 (WGS84)), or "spheroid" (meters, requires EPSG:4326 (WGS84), most accurate but slowest).

        • Optionalunit?: "m" | "km"

          If method is "spheroid" or "haversine", you can choose between "m" (meters, default) or "km" (kilometers).

      Returns this

      The table, so methods can be chained.

      // Compute distance between 'geomA' and 'geomB' in SRS units, store in 'distance_srs'
      await table.distance("geomA", "geomB", "distance_srs").log();
      // Compute Haversine distance in meters between 'point1' and 'point2', store in 'distance_m'
      // Input geometries must be in EPSG:4326 (WGS84).
      await table.distance("point1", "point2", "distance_m", { method: "haversine" }).log();
      // Compute Haversine distance in kilometers, rounded to 2 decimal places
      // Input geometries must be in EPSG:4326 (WGS84).
      await table.distance("point1", "point2", "distance_km", { method: "haversine", unit: "km", decimals: 2 }).log();
      // Compute Spheroid distance in kilometers
      // Input geometries must be in EPSG:4326 (WGS84).
      await table.distance("area1", "area2", "distance_spheroid_km", { method: "spheroid", unit: "km" }).log();
    • Extracts the latitude (y) and longitude (x) coordinates from point geometries. The input geometry is assumed to be in EPSG:4326 (WGS84).

      Parameters

      • column: string

        The name of the column storing the point geometries.

      • latColumn: string

        The name of the new column where the extracted latitude (y-coordinate) values will be stored.

      • lonColumn: string

        The name of the new column where the extracted longitude (x-coordinate) values will be stored.

      Returns this

      The table, so methods can be chained.

      // Extract latitude (y) and longitude (x) from 'geom' into new 'lat' and 'lon' columns.
      await table.extractLatLon("geom", "lat", "lon").log();
    • Fills holes in polygon geometries.

      Parameters

      • Optionalcolumn: string

        The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Fill holes in geometries in the default geometry column
      await table.fillHoles().log();
      // Fill holes in geometries in a specific column named 'polygonGeom'
      await table.fillHoles("polygonGeom").log();
    • Attempts to make invalid geometries valid without removing any vertices.

      Parameters

      • Optionalcolumn: string

        The name of the column storing the geometries to be fixed. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Fix invalid geometries in the default geometry column
      await table.fixGeo().log();
      // Fix invalid geometries in a specific column named 'myGeom'
      await table.fixGeo("myGeom").log();
    • Flips the coordinate order of geometries in a specified column (e.g., from [longitude (x), latitude (y)] to [latitude (y), longitude (x)] or vice-versa). Warning: This method should be used with caution as it directly manipulates coordinate order and can affect the accuracy of geospatial operations if not used correctly.

      Parameters

      • Optionalcolumn: string

        The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Flip coordinates in the default geometry column
      await table.flipCoordinates().log();
      // Flip coordinates in a specific column named 'myGeom'
      await table.flipCoordinates("myGeom").log();
    • Returns the bounding box of geometries in [minLon, minLat, maxLon, maxLat] order. By default, the method will try to find the column with the geometries. The input geometry is assumed to be in EPSG:4326 (WGS84).

      Parameters

      • Optionalcolumn: string

        The name of the column storing geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns Promise<[number, number, number, number]>

      A promise that resolves to an array [minLon, minLat, maxLon, maxLat] representing the bounding box.

      // Get the bounding box of geometries in the default column
      const bbox = await table.getBoundingBox();
      console.log(bbox); // e.g., [-75.0, 45.0, -73.0, 46.0]
      // Get the bounding box of geometries in a specific column named 'areaGeom'
      const areaBbox = await table.getBoundingBox("areaGeom");
      console.log(areaBbox);
    • Retrieves the projection of a specified geospatial column.

      Parameters

      • column: string

        The name of the geospatial column for which to retrieve the projection.

      Returns Promise<string>

      A promise that resolves to the projection of the specified column.

      // Get the projection of the 'geom' column
      const projection = await table.getProjection("geom");
    • Computes the intersection of two sets of geometries, creating new geometries where they overlap.

      Parameters

      • column1: string

        The name of the first column storing geometries.

      • column2: string

        The name of the second column storing geometries. Both columns must have the same projection.

      • newColumn: string

        The name of the new column where the computed intersection geometries will be stored.

      Returns this

      The table, so methods can be chained.

      // Compute the intersection of geometries in 'geomA' and 'geomB' columns, storing results in 'intersectGeom'
      await table.intersection("geomA", "geomB", "intersectGeom").log();
    • Returns TRUE if two geometries intersect (overlap in any way), and FALSE otherwise.

      Parameters

      • column1: string

        The name of the first column storing geometries.

      • column2: string

        The name of the second column storing geometries. Both columns must have the same projection.

      • newColumn: string

        The name of the new column where the boolean results (TRUE for intersection, FALSE otherwise) will be stored.

      Returns this

      The table, so methods can be chained.

      // Check if geometries in 'geomA' and 'geomB' intersect, storing results in 'doIntersect'
      await table.intersects("geomA", "geomB", "doIntersect").log();
    • Merges the data of this table (considered the left table) with another table (the right table) based on a spatial relationship. Note that the order of rows in the returned data is not guaranteed to be the same as in the original tables. This operation might create temporary files in a .tmp folder; consider adding .tmp to your .gitignore.

      Parameters

      • rightTable: SimpleTable

        The SimpleTable instance to be joined with this table.

      • method: "intersect" | "inside" | "withinDistance"

        The spatial join method to use: "intersect" (geometries overlap), "inside" (geometries of the left table are entirely within geometries of the right table), or "withinDistance" (geometries of the left table are within a specified distance of geometries in the right table).

      • options: {
            distance?: number;
            distanceMethod?: "srs" | "haversine" | "spheroid";
            excludeLeftGeometry?: boolean;
            excludeRightGeometry?: boolean;
            leftColumn?: string;
            outputTable?: string | boolean;
            rightColumn?: string;
            type?: "left" | "right" | "inner" | "full";
        } = {}

        An optional object with configuration options:

        • Optionaldistance?: number

          Required if method is "withinDistance". The target distance for the spatial join. The unit depends on distanceMethod.

        • OptionaldistanceMethod?: "srs" | "haversine" | "spheroid"

          The method for distance calculations: "srs" (default, uses the SRS unit), "haversine" (uses meters, requires EPSG:4326 (WGS84) input), or "spheroid" (uses meters, requires EPSG:4326 (WGS84) input, most accurate but slowest).

        • OptionalexcludeLeftGeometry?: boolean

          Whether to exclude the selected leftColumn geometry from the result. Defaults to false.

        • OptionalexcludeRightGeometry?: boolean

          Whether to exclude the selected rightColumn geometry from the result. Defaults to false.

        • OptionalleftColumn?: string

          The name of the column storing geometries in the left table (this table). If omitted, the method attempts to find one.

        • OptionaloutputTable?: string | boolean

          If true, the results will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be overwritten. Defaults to false.

        • OptionalrightColumn?: string

          The name of the column storing geometries in the right table. If omitted, the method attempts to find one.

        • Optionaltype?: "left" | "right" | "inner" | "full"

          The type of join operation to perform: "inner", "left" (default), "right", or "full". For some types (like "inside"), the table order is important.

      Returns this

      A table instance containing the spatially joined data (either the current table or a new table), so methods can be chained.

      // Merge data based on intersecting geometries, overwriting tableA
      await tableA.joinGeo(tableB, "intersect").log();
      // Merge data where geometries in tableA are inside geometries in tableB
      await tableA.joinGeo(tableB, "inside").log();
      // Join using both geometries without copying them into the result
      await tableA.joinGeo(tableB, "intersect", {
      excludeLeftGeometry: true,
      excludeRightGeometry: true,
      }).log();
      // Merge data where geometries in tableA are within 10 units (SRS) of geometries in tableB
      await tableA.joinGeo(tableB, "withinDistance", { distance: 10 }).log();
      // Merge data where geometries in tableA are within 10 kilometers (Haversine) of geometries in tableB
      // Input geometries must be in EPSG:4326 (WGS84).
      await tableA.joinGeo(tableB, "withinDistance", { distance: 10, distanceMethod: "haversine", unit: "km" }).log();
      // Merge data with specific geometry columns and an inner join type, storing results in a new table
      const tableC = await tableA.joinGeo(tableB, "intersect", {
      leftColumn: "geometriesA",
      rightColumn: "geometriesB",
      type: "inner",
      outputTable: true,
      }).log();
    • Computes the length of line geometries in meters ("m") or optionally kilometers ("km"). The input geometry is assumed to be in EPSG:4326 (WGS84).

      Parameters

      • newColumn: string

        The name of the new column where the computed lengths will be stored.

      • options: { column?: string; decimals?: number; unit?: "m" | "km" } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

        • Optionaldecimals?: number

          The number of decimal places to round the computed lengths. Defaults to undefined (no rounding).

        • Optionalunit?: "m" | "km"

          The unit for the computed length: "m" (meters) or "km" (kilometers). Defaults to "m".

      Returns this

      The table, so methods can be chained.

      // Compute the length of line geometries in meters and store in 'length_m'
      await table.length("length_m").log();
      // Compute the length of line geometries in kilometers and store in 'length_km'
      await table.length("length_km", { unit: "km" }).log();
      // Compute lengths in kilometers rounded to two decimal places
      await table.length("length_km", { unit: "km", decimals: 2 }).log();
      // Compute the length of geometries in a specific column named 'routeGeom'
      await table.length("routeLength", { column: "routeGeom" }).log();
    • Transforms closed linestring geometries into polygon geometries.

      Parameters

      • Optionalcolumn: string

        The name of the column storing the linestring geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Transform closed linestrings in the default geometry column into polygons
      await table.linesToPolygons().log();
      // Transform closed linestrings in a specific column named 'routeLines' into polygons
      await table.linesToPolygons("routeLines").log();
    • Loads geospatial data from an external file or URL into the table. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Parameters

      • file: string

        The path or URL of the external file containing the geospatial data.

      • options: { columns?: string[]; conditions?: string; toEPSG4326?: boolean } = {}

        An optional object with configuration options:

        • Optionalcolumns?: string[]

          The columns to load. Include the geometry column that should remain in the resulting table, usually "geom". By default, all columns are loaded.

        • Optionalconditions?: string

          A SQL WHERE clause expression, without the WHERE keyword, to filter source rows before materialization and reprojection. Can reference source columns excluded from columns. Geometry conditions use the source coordinate system.

        • OptionaltoEPSG4326?: boolean

          If true, the method will attempt to reproject the data to EPSG:4326 (WGS84).

      Returns this

      The table, so methods can be chained.

      // Load geospatial data from a URL
      await table.loadGeoData("https://some-website.com/some-data.geojson").log();
      // Load geospatial data from a local file
      await table.loadGeoData("./some-data.geojson").log();
      // Load only the name and geometry columns
      await table
      .loadGeoData("./boundaries.geojson", {
      columns: ["name", "geom"],
      })
      .log();
      // Load geospatial data from a shapefile (with relevant files in the same folder) and reproject to EPSG:4326 (WGS84)
      await table.loadGeoData("./some-data/some-data.shp", { toEPSG4326: true }).log();
      // Load geospatial data from a zipped shapefile and reproject to EPSG:4326 (WGS84)
      await table.loadGeoData("./some-data.shp.zip", { toEPSG4326: true }).log();
      // Filter on a source property without retaining it in the table
      await table
      .loadGeoData("./boundaries.geojson", {
      conditions: "population > 100000",
      columns: ["name", "geom"],
      })
      .log();
    • Loads OpenStreetMap data into the table from a local .osm or .osm.pbf file, a remote file URL, or an Overpass bounding-box query. Pass a path or URL string to load an existing file, or pass a bounding box with filters to download matching features.

      DuckDB's Osmium community extension materializes the complete result and reconstructs geom as EPSG:4326. By default, that canonical result is cached as GeoParquet in .sda-cache/osm. Distinct paths, local file fingerprints, normalized URLs, endpoints, queries, and bounding boxes retain independent entries. The cache has no expiration unless ttl is set and can be cleared by removing .sda-cache/osm. Immutable URLs can safely use the default indefinite cache; mutable URLs should set a TTL.

      Bounding-box filters use the standard Overpass QL filter syntax. Equality filters are passed as [key, value] tuples and serialized by the method. A raw filter fragment string can be used for advanced Overpass filters.

      The default Overpass endpoint is a shared public service. Follow the Overpass public-instance usage guidelines, and configure another endpoint or run your own instance for high-volume usage.

      OpenStreetMap data is licensed under the Open Data Commons Open Database License. Public use requires OpenStreetMap attribution, and distributing OSM or derivative databases can trigger the licence's share-alike requirements.

      Parameters

      • source: string | { east: number; north: number; south: number; west: number }

        The local path or remote URL of an existing .osm or .osm.pbf file, or a bounding box to query through Overpass.

        • string
        • { east: number; north: number; south: number; west: number }
          • east: number

            The eastern longitude, between -180 and 180 and greater than west.

          • north: number

            The northern latitude, between -90 and 90 and greater than south.

          • south: number

            The southern latitude, between -90 and 90 and less than north.

          • west: number

            The western longitude, between -180 and 180 and less than east.

      • options: {
            cache?: boolean;
            endpoint?: string;
            filters?: string | [string, string] | [string, string][];
            retries?: number;
            retryDelay?: number;
            timeout?: number;
            ttl?: number;
            verbose?: boolean;
        } = {}

        Loading options. When source is a bounding box, options.filters must be provided.

        • Optionalcache?: boolean

          Whether to read and write the processed GeoParquet cache. Defaults to true.

        • Optionalendpoint?: string

          The Overpass interpreter endpoint. Defaults to https://overpass-api.de/api/interpreter.

        • Optionalfilters?: string | [string, string] | [string, string][]

          For a bounding-box query, one [key, value] tuple or an array of tuples. Array entries are combined as a union. A raw Overpass QL filter fragment string is also accepted.

        • Optionalretries?: number

          Additional network retries after the initial request. Defaults to 3, for up to four attempts. Network-only; passing it for a local path throws an error.

        • OptionalretryDelay?: number

          Base retry delay in seconds. Defaults to 5, producing deterministic delays of 5, 10, and 20 seconds. A longer valid Retry-After value takes precedence. Network-only; passing it for a local path throws an error.

        • Optionaltimeout?: number

          A positive integer timeout in seconds, applied to both the Overpass query and HTTP request. If omitted, the endpoint's default query timeout applies and no HTTP request timeout is set.

        • Optionalttl?: number

          Cache lifetime in seconds. Omit for no expiration, use 0 to refresh the matching cache entry immediately, or provide a positive value to refresh once the processed entry reaches that age. Cannot be combined with cache: false.

        • Optionalverbose?: boolean

          Whether to log OpenStreetMap cache, network, Osmium, cleanup, and timing lifecycle details. Defaults to false. SimpleDB({ cacheVerbose: true }) also enables these messages and is not overridden by verbose: false.

      Returns this

      The table, so methods can be chained.

      // Load an existing local OpenStreetMap PBF file
      await table.loadOpenStreetMap("./montreal.osm.pbf").log();
      // Filter and project after the complete canonical source has loaded
      await table
      .loadOpenStreetMap("./montreal.osm.pbf", { verbose: true })
      .filter("tags['amenity'] = 'school'")
      .selectColumns(["id", "tags", "geom"])
      .log();
      // Download features matching one equality filter within a bounding box
      await table
      .loadOpenStreetMap(
      { west: -73.587799, south: 45.445078, east: -73.552265, north: 45.471086 },
      { filters: ["amenity", "school"] },
      )
      .log();
      // Download features matching either equality filter
      await table.loadOpenStreetMap(
      { west: -73.587799, south: 45.445078, east: -73.552265, north: 45.471086 },
      {
      filters: [["amenity", "school"], ["amenity", "college"]],
      },
      ).log();
      // Use a raw Overpass filter fragment for an advanced filter
      await table.loadOpenStreetMap(
      { west: -73.587799, south: 45.445078, east: -73.552265, north: 45.471086 },
      { filters: `["amenity"~"school|college"]` },
      ).log();
    • Computes the perimeter of polygon geometries in meters ("m") or optionally kilometers ("km"). The input geometry is assumed to be in EPSG:4326 (WGS84).

      Parameters

      • newColumn: string

        The name of the new column where the computed perimeters will be stored.

      • options: { column?: string; decimals?: number; unit?: "m" | "km" } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

        • Optionaldecimals?: number

          The number of decimal places to round the computed perimeters. Defaults to undefined (no rounding).

        • Optionalunit?: "m" | "km"

          The unit for the computed perimeter: "m" (meters) or "km" (kilometers). Defaults to "m".

      Returns this

      The table, so methods can be chained.

      // Compute the perimeter of polygon geometries in meters and store in 'perimeter_m'
      await table.perimeter("perimeter_m").log();
      // Compute the perimeter of polygon geometries in kilometers and store in 'perimeter_km'
      await table.perimeter("perimeter_km", { unit: "km" }).log();
      // Compute perimeters in kilometers rounded to two decimal places
      await table.perimeter("perimeter_km", { unit: "km", decimals: 2 }).log();
      // Compute the perimeter of geometries in a specific column named 'landParcelGeom'
      await table.perimeter("landParcelPerimeter", { column: "landParcelGeom" }).log();
    • Reduces the precision of geometries in a specified column to a given number of decimal places.

      Parameters

      • decimals: number

        The number of decimal places to keep in the coordinates of the geometries.

      • options: { column?: string } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Reduce the precision of geometries in the default column to 3 decimal places
      await table.reducePrecision(3).log();
      // Reduce the precision of geometries in a specific column named 'myGeom' to 2 decimal places
      await table.reducePrecision(2, { column: "myGeom" }).log();
    • Reprojects the geometries in a specified column to another Spatial Reference System (SRS).

      Parameters

      • crs: string

        The target SRS (e.g., "EPSG:3347", or "EPSG:4326" for EPSG:4326 (WGS84)).

      • options: { column?: string } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Reproject geometries in the default column to EPSG:3347 (NAD83/Statistics Canada Lambert)
      await table.reproject("EPSG:3347").log();
      // Reproject geometries in a specific column named 'myGeom' to EPSG:3347
      await table.reproject("EPSG:3347", { column: "myGeom" }).log();
    • Simplifies geometries while preserving their overall coverage. A higher tolerance results in more significant simplification.

      Parameters

      • tolerance: number

        A numeric value representing the simplification tolerance. A higher value leads to greater simplification.

      • options: { column?: string; simplifyBoundary?: boolean } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the method will automatically attempt to find a geometry column.

        • OptionalsimplifyBoundary?: boolean

          If true (default), the boundary of the geometries will also be simplified. If false, only the interior of the geometries will be simplified, preserving the original boundary.

      Returns this

      The table, so methods can be chained.

      // Simplify geometries in the default column with a tolerance of 0.1
      await table.simplify(0.1).log();
      // Simplify geometries in 'myGeom' column, preserving the boundary
      await table.simplify(0.05, { column: "myGeom", simplifyBoundary: false }).log();
    • Computes the union of two geometries, creating a new geometry that represents the merged area of both.

      Parameters

      • column1: string

        The name of the first column storing geometries.

      • column2: string

        The name of the second column storing geometries. Both columns must have the same projection.

      • newColumn: string

        The name of the new column where the computed union geometries will be stored.

      Returns this

      The table, so methods can be chained.

      // Compute the union of geometries in 'geomA' and 'geomB', storing results in 'unionGeom'
      await table.union("geomA", "geomB", "unionGeom").log();
    • Unnests geometries recursively, transforming multi-part geometries (e.g., MultiPolygon) into individual single-part geometries (e.g., Polygon).

      Parameters

      • Optionalcolumn: string

        The name of the column storing the geometries to be unnested. If omitted, the method will automatically attempt to find a geometry column.

      Returns this

      The table, so methods can be chained.

      // Unnest geometries in the default column
      await table.unnestGeo().log();
      // Unnest geometries in a specific column named 'multiGeom'
      await table.unnestGeo("multiGeom").log();
    • Returns the bottom n rows of the table, optionally filtered by SQL conditions. By default, the last row will be returned first. To preserve the original order, use the originalOrder option. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL. Temporal values use the same JavaScript representations as getData().

      Parameters

      • count: number

        The number of rows to return from the bottom of the table.

      • options: { conditions?: string; originalOrder?: boolean } = {}

        An optional object with configuration options:

        • Optionalconditions?: string

          The filtering conditions specified as a SQL WHERE clause (e.g., "category = 'Books'").

        • OptionaloriginalOrder?: boolean

          A boolean indicating whether the rows should be returned in their original order (true) or in reverse order (last row first, false). Defaults to false.

      Returns Promise<{ [key: string]: unknown }[]>

      A promise that resolves to an array of objects representing the bottom n rows.

      // Get the last 10 rows (last row first)
      const bottom10 = await table.getBottom(10);
      console.log(bottom10);
      // Get the last 10 rows in their original order
      const bottom10OriginalOrder = await table.getBottom(10, { originalOrder: true });
      console.log(bottom10OriginalOrder);
      // Get the last 5 rows where the 'category' is 'Books' (using JS syntax)
      const bottom5Books = await table.getBottom(5, { conditions: `category === 'Books'` });
      console.log(bottom5Books);
    • Returns the total number of characters in a column storing strings. Counts are based on Unicode code points, not grapheme clusters. A user-perceived character composed of multiple code points, such as some emoji or decomposed accented letters, counts as multiple characters.

      Parameters

      • column: string

        The name of the string column to count characters from.

      Returns Promise<number>

      A promise that resolves to the total number of characters across all rows in the specified column.

      // Get the total number of characters in the 'name' column
      const totalChars = await table.getCharacterCount("name");
      console.log(totalChars); // e.g., 523
    • Returns the number of columns in the table.

      Returns Promise<number>

      A promise that resolves to a number representing the total count of columns.

      // Get the number of columns in the table
      const columnCount = await table.getColumnCount();
      console.log(columnCount); // e.g., 3
    • Returns a list of all column names in the table.

      Returns Promise<string[]>

      A promise that resolves to an array of strings, where each string is a column name.

      // Get all column names from the table
      const columns = await table.getColumns();
      console.log(columns); // e.g., ["id", "name", "age"]
    • Returns the data from the table as an array of objects, optionally filtered by SQL conditions. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Top-level DuckDB DATE and TIMESTAMP columns are returned as JavaScript Date objects interpreted in UTC. TIMESTAMP WITH TIME ZONE values are returned as UTC strings, preserving DuckDB's microsecond precision; JavaScript Date supports only milliseconds.

      Top-level BIGINT, UBIGINT, HUGEINT, and UHUGEINT columns are returned as JavaScript numbers. Values outside Number.MIN_SAFE_INTEGER through Number.MAX_SAFE_INTEGER throw instead of losing precision. Use convert({ columnName: "string" }) before getData() to retrieve their exact digits as strings.

      SQL arrays and lists, including float vectors, return their actual elements as JavaScript arrays. Null cells and null elements remain null.

      Nested SQL lists and structs become JavaScript arrays and objects. Within them, BIGINT, UBIGINT, HUGEINT, UHUGEINT, and DECIMAL values remain strings to preserve precision, even for small integers.

      Parameters

      • options: { columns?: string | string[]; conditions?: string; limit?: number } = {}

        An optional object with configuration options:

        • Optionalcolumns?: string | string[]

          An array of column names to include in the result. If omitted, all columns will be included.

        • Optionalconditions?: string

          The filtering conditions specified as a SQL WHERE clause (e.g., "category = 'Book'").

        • Optionallimit?: number

          The maximum number of rows to return. Must be an integer greater than or equal to 0.

      Returns Promise<{ [key: string]: unknown }[]>

      A promise that resolves to an array of objects, where each object represents a row in the table.

      // Get all data from the table
      const allData = await table.getData();
      console.log(allData);
      // Get data filtered by a condition (using JS or SQL syntax)
      const booksData = await table.getData({ conditions: `category === 'Book'` });
      console.log(booksData);
      // Get data filtered by a condition and specific columns
      const booksData = await table.getData({ columns: ["title", "author"], conditions: `category === 'Book'` });
      console.log(booksData);
      // Return at most two rows.
      const preview = await table.getData({ limit: 2 });
      // Read nested integers without losing digits. SQL creates the nested types.
      await sdb.customQuery(`CREATE TABLE nested_example AS SELECT
      [42::BIGINT, 9007199254740993::BIGINT] AS ids,
      {'count': 2::INTEGER, 'amount': 123.450::DECIMAL(6,3)} AS details`);
      const nested = sdb.newTable("nested_example");
      const rows = await nested.getData();
      // [{ ids: ["42", "9007199254740993"], details: { count: 2, amount: "123.450" } }]
      await nested.log();
    • Returns the data from the table as a CSV string, optionally filtered by SQL conditions. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL. Temporal values are first converted as they are in getData(), then serialized using UTC date and timestamp text.

      Parameters

      • options: { columns?: string | string[]; conditions?: string } = {}

        An optional object with configuration options:

        • Optionalcolumns?: string | string[]

          An array of column names to include in the CSV. If omitted, all columns will be included.

        • Optionalconditions?: string

          The filtering conditions specified as a SQL WHERE clause (e.g., "category = 'Book'").

      Returns Promise<string>

      A promise that resolves to a CSV-formatted string representation of the table data.

      // Get all data from the table as CSV
      const allDataCSV = await table.getDataAsCSV();
      console.log(allDataCSV);
      // Get data filtered by a condition (using JS syntax or SQL syntax) as CSV
      const booksDataCSV = await table.getDataAsCSV({ conditions: `category === 'Book'` });
      console.log(booksDataCSV);
      // Get data filtered by a condition and specific columns as CSV
      const booksDataCSV = await table.getDataAsCSV({ columns: ["title", "author"], conditions: `category === 'Book'` });
      console.log(booksDataCSV);
    • Returns descriptive statistical information about the columns, including details like data types, number of null values, and distinct values.

      Returns Promise<{ [key: string]: unknown }[]>

      A promise that resolves to an array of objects, each representing descriptive statistics for a column.

      // Get and log descriptive information about the table's columns
      const description = await table.getDescription();
      console.table(description);
    • Returns the extent (minimum and maximum values) of a specific column as an array. Temporal values use the same JavaScript representations as getData().

      Parameters

      • column: string

        The name of the column from which to retrieve the extent.

      Returns Promise<[unknown, unknown]>

      A promise that resolves to an array [min, max] containing the minimum and maximum values of the specified column.

      // Get the extent of the 'temperature' column
      const tempExtent = await table.getExtent("temperature");
      console.log(tempExtent); // e.g., [15.2, 30.1]
    • Returns the first row of the table, optionally filtered by SQL conditions. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL. Temporal values use the same JavaScript representations as getData().

      Parameters

      • options: { conditions?: string } = {}

        An optional object with configuration options:

        • Optionalconditions?: string

          The filtering conditions specified as a SQL WHERE clause (e.g., "category = 'Book'").

      Returns Promise<{ [key: string]: unknown } | null>

      A promise that resolves to an object representing the first row, or null if no rows match the conditions.

      // Get the very first row of the table
      const firstRow = await table.getFirstRow();
      console.log(firstRow);
      // Get the first row where the 'category' is 'Book'
      const firstRowBooks = await table.getFirstRow({ conditions: `category === 'Book'` }); // Using JS syntax
      console.log(firstRowBooks);
    • Returns the table's geospatial data as a GeoJSON FeatureCollection. Each row becomes a Feature: the selected geometry column becomes its geometry, and the remaining columns become its properties. If the table has multiple geometry columns, you must specify which one to use.

      Parameters

      • options: { column?: string; rewind?: boolean } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries. If omitted, the table must have exactly one geometry column, which will be selected automatically.

        • Optionalrewind?: boolean

          If true, rewinds the coordinates of polygons to follow the spherical winding order (important for D3.js). Defaults to false.

      Returns Promise<{ features: unknown[]; type: string }>

      A promise that resolves to a GeoJSON object representing the table's geospatial data.

      // Get GeoJSON data from the default geometry column
      const geojson = await table.getGeoData();
      console.log(geojson);
      // Get GeoJSON data from a specific geometry column named 'myGeometries'
      const myGeomJson = await table.getGeoData({ column: "myGeometries" });
      console.log(myGeomJson);
      // Get GeoJSON data and rewind polygon coordinates for D3.js compatibility
      const rewoundGeojson = await table.getGeoData({ rewind: true });
      console.log(rewoundGeojson);
    • Returns a deterministic hash of the table's ordered schema and contents. Pending operations are executed before the hash is calculated. Computing the hash scans the complete table inside DuckDB without transferring its rows to JavaScript.

      Returns Promise<string>

      A promise that resolves to a SHA-256 hash string.

      const hash = await table.getHash();
      console.log(hash); // e.g., "8f14e45fceea..."
    • Returns the last row of the table, optionally filtered by SQL conditions. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL. Temporal values use the same JavaScript representations as getData().

      Parameters

      • options: { conditions?: string } = {}

        An optional object with configuration options:

        • Optionalconditions?: string

          The filtering conditions specified as a SQL WHERE clause (e.g., "category = 'Book'").

      Returns Promise<{ [key: string]: unknown } | null>

      A promise that resolves to an object representing the last row, or null if no rows match the conditions.

      // Get the very last row of the table
      const lastRow = await table.getLastRow();
      console.log(lastRow);
      // Get the last row where the 'category' is 'Book'
      const lastRowBooks = await table.getLastRow({ conditions: `category === 'Book'` }); // Using JS syntax
      console.log(lastRowBooks);
    • Returns the maximum value from a specific column. Temporal values use the same JavaScript representations as getData().

      Parameters

      • column: string

        The name of the column from which to retrieve the maximum value.

      Returns Promise<unknown>

      A promise that resolves to the maximum value of the specified column.

      // Get the maximum value from the 'price' column
      const maxPrice = await table.getMax("price");
      console.log(maxPrice); // e.g., 99.99
    • Returns the mean (average) value from a specific numeric column.

      Parameters

      • column: string

        The name of the numeric column from which to retrieve the mean value.

      • options: { decimals?: number } = {}

        An optional object with configuration options:

        • Optionaldecimals?: number

          The number of decimal places to round the result to. Defaults to undefined (no rounding).

      Returns Promise<number>

      A promise that resolves to the mean value of the specified column.

      // Get the mean of the 'age' column
      const meanAge = await table.getMean("age");
      console.log(meanAge); // e.g., 35.75
      // Get the mean of the 'salary' column, rounded to 2 decimal places
      const meanSalary = await table.getMean("salary", { decimals: 2 });
      console.log(meanSalary); // e.g., 55000.23
    • Returns the median value from a specific numeric column.

      Parameters

      • column: string

        The name of the numeric column from which to retrieve the median value.

      • options: { decimals?: number } = {}

        An optional object with configuration options:

        • Optionaldecimals?: number

          The number of decimal places to round the result to. Defaults to undefined (no rounding).

      Returns Promise<number>

      A promise that resolves to the median value of the specified column.

      // Get the median of the 'age' column
      const medianAge = await table.getMedian("age");
      console.log(medianAge); // e.g., 30
      // Get the median of the 'salary' column, rounded to 2 decimal places
      const medianSalary = await table.getMedian("salary", { decimals: 2 });
      console.log(medianSalary); // e.g., 50000.00
    • Returns the minimum value from a specific column. Temporal values use the same JavaScript representations as getData().

      Parameters

      • column: string

        The name of the column from which to retrieve the minimum value.

      Returns Promise<unknown>

      A promise that resolves to the minimum value of the specified column.

      // Get the minimum value from the 'price' column
      const minPrice = await table.getMin("price");
      console.log(minPrice); // e.g., 10.50
    • Returns the name of the table.

      Returns string

      The name of the table as a string.

      // Get the table name
      const tableName = table.getName();
      console.log(tableName); // e.g., "employees"
    • Returns the value of a specific quantile from the values in a given numeric column.

      Parameters

      • column: string

        The name of the numeric column from which to calculate the quantile.

      • quantile: number

        The quantile to calculate, expressed as a number between 0 and 1 (e.g., 0.25 for the first quartile, 0.5 for the median, 0.75 for the third quartile).

      • options: { decimals?: number } = {}

        An optional object with configuration options:

        • Optionaldecimals?: number

          The number of decimal places to round the result to. Defaults to undefined (no rounding).

      Returns Promise<number>

      A promise that resolves to the quantile value of the specified column.

      // Get the first quartile (25th percentile) of 'column1'
      const firstQuartile = await table.getQuantile("column1", 0.25);
      console.log(firstQuartile); // e.g., 15.7
      // Get the 90th percentile of 'score' values, rounded to 2 decimal places
      const ninetiethPercentile = await table.getQuantile("score", 0.9, { decimals: 2 });
      console.log(ninetiethPercentile); // e.g., 88.55
    • Returns a single row that matches the specified conditions. Always throws if no row matches. By default, also throws if more than one row matches. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL. Temporal values use the same JavaScript representations as getData().

      Parameters

      • conditions: string

        The conditions to match, specified as a SQL WHERE clause.

      • options: { strict?: boolean } = {}

        Optional settings:

        • Optionalstrict?: boolean

          If false, returns the first row when multiple rows match. A missing match always throws. Defaults to true.

      Returns Promise<{ [key: string]: unknown }>

      A promise that resolves to an object representing the matched row.

      // Get a row where 'name' is 'John'
      const johnsRow = await table.getRow(`name = 'John'`);
      console.log(johnsRow);
      // Get a row where 'id' is 123 (using JS syntax)
      const rowById = await table.getRow(`id === 123`);
      console.log(rowById);
      // Get the first matching row when multiple rows may match
      const flexibleRow = await table.getRow(`status = 'pending'`, { strict: false });
      console.log(flexibleRow);

      If no row matches, or if strict is true and more than one row matches the conditions.

    • Returns the number of rows in the table. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Parameters

      • options: { conditions?: string } = {}

        An optional object with configuration options:

        • Optionalconditions?: string

          The filtering conditions specified as a SQL WHERE clause (e.g., "category = 'Book'").

      Returns Promise<number>

      A promise that resolves to a number representing the total count of rows.

      // Get the number of rows in the table
      const rowCount = await table.getRowCount();
      console.log(rowCount); // e.g., 100
      // Get the number of rows where 'category' is 'Book'
      const bookCount = await table.getRowCount({ conditions: "category = 'Book'" });
      console.log(bookCount);
    • Returns the schema of the table, including column names and their data types.

      Returns Promise<{ [key: string]: string | null }[]>

      A promise that resolves to an array of objects, where each object represents a column with its name and data type.

      // Get the schema of the table
      const schema = await table.getSchema();
      console.table(schema); // Log the schema in a readable table format
    • Returns the skewness of values from a specific numeric column.

      Parameters

      • column: string

        The name of the numeric column from which to retrieve the skewness.

      • options: { decimals?: number } = {}

        An optional object with configuration options:

        • Optionaldecimals?: number

          The number of decimal places to round the result to. Defaults to undefined (no rounding).

      Returns Promise<number>

      A promise that resolves to the skewness value of the specified column.

      // Get the skewness of the 'data' column
      const dataSkew = await table.getSkew("data");
      console.log(dataSkew); // e.g., 0.5
      // Get the skewness of the 'values' column, rounded to 2 decimal places
      const valuesSkew = await table.getSkew("values", { decimals: 2 });
      console.log(valuesSkew); // e.g., -0.25
    • Returns the standard deviation of values from a specific numeric column.

      Parameters

      • column: string

        The name of the numeric column from which to retrieve the standard deviation.

      • options: { decimals?: number } = {}

        An optional object with configuration options:

        • Optionaldecimals?: number

          The number of decimal places to round the result to. Defaults to undefined (no rounding).

      Returns Promise<number>

      A promise that resolves to the standard deviation value of the specified column.

      // Get the standard deviation of the 'height' column
      const heightStdDev = await table.getStdDev("height");
      console.log(heightStdDev); // e.g., 5.2
      // Get the standard deviation of the 'score' column, rounded to 3 decimal places
      const scoreStdDev = await table.getStdDev("score", { decimals: 3 });
      console.log(scoreStdDev); // e.g., 12.345
    • Returns the sum of values from a specific numeric column.

      Parameters

      • column: string

        The name of the numeric column from which to retrieve the sum.

      Returns Promise<number>

      A promise that resolves to the sum of values in the specified column.

      // Get the sum of the 'quantity' column
      const totalQuantity = await table.getSum("quantity");
      console.log(totalQuantity); // e.g., 1250
    • Returns the top n rows of the table, optionally filtered by SQL conditions. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL. Temporal values use the same JavaScript representations as getData().

      Parameters

      • count: number

        The number of rows to return from the top of the table.

      • options: { conditions?: string } = {}

        An optional object with configuration options:

        • Optionalconditions?: string

          The filtering conditions specified as a SQL WHERE clause (e.g., "category = 'Books'").

      Returns Promise<{ [key: string]: unknown }[]>

      A promise that resolves to an array of objects representing the top n rows.

      // Get the first 10 rows of the table
      const top10 = await table.getTop(10);
      console.log(top10);
      // Get the first 5 rows where the 'category' is 'Books'
      const top5Books = await table.getTop(5, { conditions: `category === 'Books'` }); // Using JS syntax
      console.log(top5Books);
    • Returns the data types of all columns in the table.

      Returns Promise<{ [key: string]: string }>

      A promise that resolves to an object where keys are column names and values are their corresponding data types (e.g., { "id": "BIGINT", "name": "VARCHAR" }).

      // Get the data types of all columns
      const dataTypes = await table.getTypes();
      console.log(dataTypes);
    • Returns unique values from a specific column. The values are returned in ascending order. Temporal values use the same JavaScript representations as getData().

      Parameters

      • column: string

        The name of the column from which to retrieve unique values.

      Returns Promise<unknown[]>

      A promise that resolves to an array containing the unique values from the specified column, sorted in ascending order.

      // Get unique values from the 'category' column
      const uniqueCategories = await table.getUniques("category");
      console.log(uniqueCategories); // e.g., ["Books", "Clothing", "Electronics"]
    • Returns the total number of values in the table (number of columns multiplied by the number of rows).

      Returns Promise<number>

      A promise that resolves to a number representing the total count of values.

      // Get the total number of values in the table
      const valueCount = await table.getValueCount();
      console.log(valueCount); // e.g., 300 (if 3 columns and 100 rows)
    • Returns all values from a specific column. Temporal values use the same JavaScript representations as getData().

      Parameters

      • column: string

        The name of the column from which to retrieve values.

      Returns Promise<unknown[]>

      A promise that resolves to an array containing all values from the specified column.

      // Get all values from the 'productName' column
      const productNames = await table.getValues("productName");
      console.log(productNames); // e.g., ["Laptop", "Mouse", "Keyboard"]
    • Returns the variance of values from a specific numeric column.

      Parameters

      • column: string

        The name of the numeric column from which to retrieve the variance.

      • options: { decimals?: number } = {}

        An optional object with configuration options:

        • Optionaldecimals?: number

          The number of decimal places to round the result to. Defaults to undefined (no rounding).

      Returns Promise<number>

      A promise that resolves to the variance value of the specified column.

      // Get the variance of the 'data' column
      const dataVariance = await table.getVariance("data");
      console.log(dataVariance); // e.g., 25.5
      // Get the variance of the 'values' column, rounded to 2 decimal places
      const valuesVariance = await table.getVariance("values", { decimals: 2 });
      console.log(valuesVariance); // e.g., 10.23
    • Streams the table rows one by one as an async iterator, without materializing the whole table in memory. Values are converted to JavaScript types the same way as getData(). With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      The underlying DuckDB result is streamed chunk by chunk, so tables larger than the available memory can be iterated. Avoid running other queries on the same database while iterating.

      Parameters

      • options: { columns?: string | string[]; conditions?: string } = {}

        An optional object with configuration options:

        • Optionalcolumns?: string | string[]

          The column name or an array of column names to include. If omitted, all columns are streamed.

        • Optionalconditions?: string

          A SQL WHERE clause condition to filter the rows.

      Returns AsyncGenerator<{ [key: string]: unknown }, void, undefined>

      An async generator yielding one row object at a time.

      // Stream all rows
      for await (const row of table.stream()) {
      console.log(row);
      }
      // Stream specific columns and rows
      for await (const row of table.stream({ columns: "temperature", conditions: `temperature > 20` })) {
      console.log(row);
      }
    • Finds the neighbors shared by two different nodes. The direction option lets you find neighbors from source to target, from target to source, or in either direction. The result has one node column, sorted in ascending order.

      If either node ID is unknown, or the nodes have no shared neighbors, the result has no rows. Each shared neighbor appears once, even if connections are repeated. A self-connection can make one of the requested nodes a shared neighbor. Using the same ID for both requested nodes throws an error.

      The next three examples each start with this data:

      origin destination
      A C
      A C
      B C
      B C
      D A
      D B

      By default, the method finds shared neighbors from source to target. Both A and B connect to C:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • nodeA: string | number | bigint

        The first node ID.

      • nodeB: string | number | bigint

        The second node ID, which must differ from nodeA.

      • options: { direction?: "outgoing" | "incoming" | "both"; outputTable?: string | boolean } = {}

        An optional object with direction and result configuration.

        • Optionaldirection?: "outgoing" | "incoming" | "both"

          The direction in which to follow connections. Defaults to "outgoing".

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

      Returns SimpleTable

      The result table, so methods can be chained.

      await connections
      .commonNeighbors("origin", "destination", "A", "B")
      .log();
      node
      C

      C appears only once, even though the input repeats the connections.

      With direction: "incoming", the method follows connections from target to source. D connects to both A and B:

      await connections
      .commonNeighbors("origin", "destination", "A", "B", { direction: "incoming" })
      .log();
      node
      D

      With direction: "both", the method finds shared neighbors in either direction:

      await connections
      .commonNeighbors("origin", "destination", "A", "B", { direction: "both" })
      .log();
      node
      C
      D

      A self-connection can make a requested node a shared neighbor. For this input, A is a neighbor of both itself and B:

      source target
      A A
      B A
      await connections
      .commonNeighbors("source", "target", "A", "B")
      .log();
      node
      A
    • Groups nodes that are connected to one another, directly or through other nodes. By default, connection direction is ignored. With the mode option set to "strong", nodes belong to the same group only if each can reach all the others by following connections from source to target.

      The result has componentId and node columns. Without startTimeColumn or endTimeColumn, each node appears once, and rows are sorted by node. Groups are numbered from zero in order of their smallest node ID.

      Use the startTimeColumn or endTimeColumn options to follow connections in chronological order. The minGapMs option sets the minimum gap between consecutive connections, in milliseconds. With both time columns, gaps are measured from one connection's end to the next connection's start; with one column, between their timestamps. The strictOrdering option defaults to true, rejecting zero-duration gaps. These options require an explicitly supplied mode: "strong".

      With these options, nodes are grouped when each can reach all the others in chronological order. Groups can overlap, so a node may appear in several rows with different componentId values.

      For the first example, we start with this data:

      origin destination
      A B
      C D

      By default, the method groups connected nodes without considering direction. B and D are included even though they only appear as destinations:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • options: {
            endTimeColumn?: string;
            minGapMs?: number;
            mode?: "weak" | "strong";
            outputTable?: string | boolean;
            startTimeColumn?: string;
            strictOrdering?: boolean;
        } = {}

        An optional object with component, time, and result configuration.

        • OptionalendTimeColumn?: string

          The name of the column containing each connection's end time. Use this or startTimeColumn to follow connections in chronological order.

        • OptionalminGapMs?: number

          The minimum gap between consecutive connections, in milliseconds. Must be a non-negative integer. Defaults to 0. Requires a time column.

        • Optionalmode?: "weak" | "strong"

          Whether to find "weak" or "strong" components. Defaults to "weak". Using startTimeColumn or endTimeColumn requires explicitly setting mode: "strong".

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

        • OptionalstartTimeColumn?: string

          The name of the column containing each connection's start time. Use this or endTimeColumn to follow connections in chronological order.

        • OptionalstrictOrdering?: boolean

          Whether to reject zero-duration gaps between consecutive connections (equal timestamps). Defaults to true. Requires a time column.

      Returns SimpleTable

      The result table, so methods can be chained.

      await table
      .connectedComponents("origin", "destination")
      .log();
      componentId node
      0 A
      0 B
      1 C
      1 D

      The next two examples each start with this chain of connections:

      source target
      A B
      B C

      By default, all three nodes belong to the same group:

      await graph
      .connectedComponents("source", "target")
      .log();
      componentId node
      0 A
      0 B
      0 C

      With the mode option set to "strong", each node forms its own group. A can reach B and C, but neither can get back to A:

      await graph
      .connectedComponents("source", "target", {
      mode: "strong",
      })
      .log();
      componentId node
      0 A
      1 B
      2 C

      Adding C -> A would let every node reach the others, so all three would belong to the same group in "strong" mode too.

      Chronological strong groups can overlap. The next example uses one instantaneous timestamp column:

      time source target
      2025-01-01 08:00 B C
      2025-01-01 09:00 C B
      2025-01-01 10:00 A B
      2025-01-01 11:00 B A

      A and B can reach each other, as can B and C, but A cannot reach C after its first event. This produces the maximal groups {A, B} and {B, C}:

      await transactions
      .connectedComponents("source", "target", {
      mode: "strong",
      startTimeColumn: "time",
      })
      .log();
      componentId node
      0 A
      0 B
      1 B
      1 C
    • Counts the incoming and outgoing connections for every node found in the source and target columns. By default, each input row is counted separately, even when several rows connect the same two nodes. Set the count option to "neighbors" to count distinct neighboring nodes instead. Use the weight option to sum values from a numeric column instead of counting connections. The weight option cannot be combined with count: "neighbors".

      The result has node, incoming, outgoing, and total columns. The total is the sum of incoming and outgoing. Results are sorted by highest total first, then by node in ascending order when totals are equal. A self-connection contributes once to incoming and once to outgoing, so it contributes twice to the total. With count: "neighbors", a neighbor connected in both directions also contributes once in each direction. Endpoint IDs must be non-null strings or whole numbers in compatible columns. Supplied weights must be non-null, finite, and non-negative.

      The next three examples each start with these flights:

      origin destination passengers
      Montreal Toronto 100
      Montreal Toronto 200
      Montreal Vancouver 50
      Toronto Montreal 150

      By default, the method counts connections and overwrites the input table:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • options: {
            count?: "neighbors" | "edges";
            outputTable?: string | boolean;
            weight?: string;
        } = {}

        An optional object with counting and result configuration.

        • Optionalcount?: "neighbors" | "edges"

          Whether to count connection rows ("edges") or distinct adjacent nodes ("neighbors"). Defaults to "edges".

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

        • Optionalweight?: string

          The name of the numeric edge-weight column to sum. This requires edge counting.

      Returns SimpleTable

      The result table, so methods can be chained.

      await flights
      .degree("origin", "destination")
      .log();
      node incoming outgoing total
      Montreal 1 3 4
      Toronto 2 1 3
      Vancouver 1 0 1

      With count: "neighbors", the two Montreal-to-Toronto flights count as one outgoing neighbor for Montreal:

      await flights
      .degree("origin", "destination", { count: "neighbors" })
      .log();
      node incoming outgoing total
      Montreal 1 2 3
      Toronto 1 1 2
      Vancouver 1 0 1

      With weight: "passengers", the method adds passenger counts for each city's incoming and outgoing flights:

      await flights
      .degree("origin", "destination", { weight: "passengers" })
      .log();
      node incoming outgoing total
      Montreal 150 350 500
      Toronto 300 150 450
      Vancouver 50 0 50

      A self-connection is counted once in each direction. For this input:

      source target
      A A
      A B
      await connections
      .degree("source", "target")
      .log();
      node incoming outgoing total
      A 1 2 3
      B 1 0 1
    • Finds the shortest routes from each starting node to every reachable node. Without options, shortest means the fewest connections. Use weight to report total cost and elapsedTime to report duration including waiting between connections. Use minimize to choose which metric defines shortest.

      Each row contains start, node, and steps, plus total when weight is supplied and elapsedTimeMs when enabled. Routes tied on the chosen metric produce separate rows only when their reported summaries differ; identical summaries appear once.

      Routes cannot repeat nodes except for a final return to the start. A start appears in its own results only through an actual return or self-connection, never with zero steps. With direction: "both", a return can follow the same connection out and back. Use shortestPath() to inspect individual connections between two different nodes.

      Reporting weight or elapsed time can require substantial time and memory on dense networks because many routes may need to be explored.

      The next five examples each start with this data:

      origin destination minutes
      A B 4
      B C 1
      D B 2

      By default, the method follows connections from source to target and counts how many connections are needed:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • startNodes: string | number | bigint | (string | number | bigint)[]

        One starting node ID or a non-empty array of distinct IDs. Unknown IDs and starts with no connections to follow produce no rows. Empty arrays and duplicate IDs throw an error.

      • options: {
            direction?: "outgoing" | "incoming" | "both";
            elapsedTime?: boolean;
            endTimeColumn?: string;
            minGapMs?: number;
            minimize?: "weight" | "steps" | "elapsedTime";
            outputTable?: string | boolean;
            startTimeColumn?: string;
            strictOrdering?: boolean;
            weight?: string;
        } = {}

        An optional object with direction, time, and result configuration.

        • Optionaldirection?: "outgoing" | "incoming" | "both"

          The direction in which to follow connections. Defaults to "outgoing". Chronological traversal supports only "outgoing" and "incoming".

        • OptionalelapsedTime?: boolean

          Whether to add cumulative elapsedTimeMs in milliseconds, including connection gaps. Defaults to false. Requires both time columns. Optimization compares native timestamp precision before conversion to milliseconds.

        • OptionalendTimeColumn?: string

          The name of the column containing each connection's end time. Use this or startTimeColumn to follow connections in chronological order.

        • OptionalminGapMs?: number

          The minimum gap between consecutive connections, in milliseconds; does not apply before the first connection. With both time columns, measured from one connection's end to the next's start; with one column, between timestamps. Must be a non-negative integer. Defaults to 0. Requires a time column.

        • Optionalminimize?: "weight" | "steps" | "elapsedTime"

          The metric used to select optimal routes: "steps", "weight", or "elapsedTime". Defaults to "weight" when only weight is enabled, "elapsedTime" when only elapsed-time reporting is enabled, and "steps" when neither is enabled. Required when both are enabled. "steps" is always available; the other metrics require their corresponding options.

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

        • OptionalstartTimeColumn?: string

          The name of the column containing each connection's start time. Use this or endTimeColumn to follow connections in chronological order.

        • OptionalstrictOrdering?: boolean

          Whether to reject zero-duration gaps between consecutive connections (equal timestamps). Defaults to true. Requires a time column.

        • Optionalweight?: string

          The name of the numeric column used as the cost of each connection. Values must be non-null, finite, and non-negative. Adds total, the cumulative sum of those costs. If omitted, no total column is generated.

      Returns SimpleTable

      The table of distinct optimal summaries with start, node, and steps, plus total when weight is supplied and elapsedTimeMs when enabled, so methods can be chained. Sorted by start, the chosen metric, node, then remaining metrics in the order steps, total, elapsed time.

      await connections
      .distances("origin", "destination", "A")
      .log();
      start node steps
      A B 1
      A C 2

      With weight: "minutes", total is the sum of travel times:

      await connections
      .distances("origin", "destination", "A", { weight: "minutes" })
      .log();
      start node steps total
      A B 1 4
      A C 2 5

      With direction: "incoming", connections are followed from target to source. Starting at C:

      await connections
      .distances("origin", "destination", "C", { direction: "incoming" })
      .log();
      start node steps
      C B 1
      C A 2
      C D 2

      With direction: "both", connections can be followed in either direction:

      await connections
      .distances("origin", "destination", "A", { direction: "both" })
      .log();
      start node steps
      A B 1
      A A 2
      A C 2
      A D 2

      A appears with 2 steps because A → B → A follows a connection out and back.

      An array of starting nodes gives separate distances for each start:

      await connections
      .distances("origin", "destination", ["D", "A"], {
      weight: "minutes",
      })
      .log();
      start node steps total
      A B 1 4
      A C 2 5
      D B 1 2
      D C 2 3

      A direct self-connection and a longer return route can both lead back to the start. This example uses different data:

      origin destination minutes
      A A 10
      A B 2
      B A 3

      The shortest return to A takes 5 minutes through B, compared with 10 minutes for the direct self-connection:

      await connections
      .distances("origin", "destination", "A", { weight: "minutes" })
      .log();
      start node steps total
      A B 1 2
      A A 2 5

      Chronological options can rule out a cheaper sequence that departs too early. Here, B → C leaves before A → B arrives, while B → D meets the one-hour minimum exactly:

      origin destination departureTime arrivalTime minutes
      A B 2025-01-01 08:00 2025-01-01 10:00 2
      B C 2025-01-01 09:00 2025-01-01 10:00 1
      B D 2025-01-01 11:00 2025-01-01 12:00 3
      await scheduledFlights
      .distances("origin", "destination", "A", {
      startTimeColumn: "departureTime",
      endTimeColumn: "arrivalTime",
      minGapMs: 60 * 60 * 1000,
      weight: "minutes",
      })
      .log();
      start node steps total
      A B 1 2
      A D 2 5

      For the next four examples, flights contains these rows, with departure and arrival stored as timestamps on the same day:

      flightId origin destination departure arrival price
      F1 A B 2025-01-01 09:00 2025-01-01 10:00 40
      F2 B C 2025-01-01 12:00 2025-01-01 13:00 40
      F3 A C 2025-01-01 09:00 2025-01-01 12:00 120

      Find the fastest journey from A to each reachable destination, including layovers:

      await flights
      .distances("origin", "destination", "A", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      })
      .log();
      start node steps elapsedTimeMs
      A B 1 3600000
      A C 1 10800000

      The direct flight to C takes three hours (10,800,000 milliseconds). The route through B takes four hours including its layover.

      Find the fastest journeys from A while also adding up their prices:

      await flights
      .distances("origin", "destination", "A", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      weight: "price",
      minimize: "elapsedTime",
      })
      .log();
      start node steps total elapsedTimeMs
      A B 1 40 3600000
      A C 1 120 10800000

      Use minimize: "weight" to find the cheapest journeys instead. The selected route to C now takes two steps through B and costs 80:

      await flights
      .distances("origin", "destination", "A", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      weight: "price",
      minimize: "weight",
      })
      .log();
      start node steps total elapsedTimeMs
      A B 1 40 3600000
      A C 2 80 14400000

      Choose the fewest connections while still reporting price and elapsed time. The direct flight to C has one step, even though it costs more:

      await flights
      .distances("origin", "destination", "A", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      weight: "price",
      minimize: "steps",
      })
      .log();
      start node steps total elapsedTimeMs
      A B 1 40 3600000
      A C 1 120 10800000

      When two routes to C both take two steps but have different prices, minimizing steps while reporting price returns both summaries:

      await connections
      .loadArray([
      { origin: "A", destination: "B", price: 20 },
      { origin: "B", destination: "C", price: 30 },
      { origin: "A", destination: "D", price: 40 },
      { origin: "D", destination: "C", price: 40 },
      ])
      .distances("origin", "destination", "A", {
      weight: "price",
      minimize: "steps",
      })
      .filter("node = 'C'")
      .log();
      start node steps total
      A C 2 50
      A C 2 80
    • Finds all cycles starting and ending at each node in startNodes. Pass one node ID or an array of node IDs. A cycle cannot visit the same node twice, except to return to its start, or reuse a connection. Different cycles can share nodes. The direction option lets you follow connections from source to target, from target to source, or in either direction. By default, connections are followed from source to target.

      Each connection needs a unique, non-null ID. Pass the name of the column containing these IDs as the edgeId argument. If your table is missing an ID column, you can easily create one with addId().

      The result starts with the start, pathId, step, weight, and total columns, followed by all original columns. The start column identifies the requested starting node. Steps start at one and follow the search direction. The weight column is the connection cost, and total is the sum of those costs up to and including the current step. Without the weight option, each connection costs one.

      If an input column is already named start, pathId, step, weight, or total (regardless of capitalization), the method throws an error. Rename it with renameColumns() first.

      Each row is one connection, including the final connection back to the start. Weights must be non-null, finite, and non-negative.

      If a cycle can start at several requested nodes, each starting node produces a separate result. The pathId starts at zero for each starting node; start and pathId together identify a cycle. Passing an empty array or duplicate IDs in startNodes throws an error. Unknown IDs or starts with no cycles produce no rows.

      With direction: "both", a cycle and its reverse are returned once per starting node, choosing the smaller sequence of connection IDs. Within each start, cycles are numbered by connection-ID sequence. Rows are sorted by start, then pathId, then step.

      Use the startTimeColumn or endTimeColumn options to follow connections in chronological order. The minGapMs option sets the minimum gap between consecutive connections, in milliseconds. With both time columns, gaps are measured from one connection's end to the next connection's start; with one column, between their timestamps. The strictOrdering option defaults to true, rejecting zero-duration gaps. These options only work with direction: "outgoing" or direction: "incoming".

      Time rules are checked from each requested starting node. A cycle may therefore work from one starting node but not another. No gap is checked from the final connection back to the first. Incoming searches follow connections backward in chronological order.

      A self-connection forms a one-step cycle. With direction: "both", two separate connections between the same nodes can form a two-step cycle. A single connection cannot be followed out and back to create a cycle. Cycles using different connections remain separate, even if they visit the same nodes. If there are no cycles, the result has no rows.

      There is no limit on cycle length or the number of cycles returned. Finding all cycles can take a long time and use substantial memory.

      The next four examples each start with these connections:

      edgeId source target
      E1 A B
      E2 B C
      E3 C A

      By default, connections are followed from source to target:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • edgeId: string

        The name of the column uniquely identifying each connection (edge).

      • startNodes: string | number | bigint | (string | number | bigint)[]

        One starting node ID or an array of IDs. Finds cycles that start and end at each requested node.

      • options: {
            direction?: "outgoing" | "incoming" | "both";
            elapsedTime?: boolean;
            endTimeColumn?: string;
            minGapMs?: number;
            outputTable?: string | boolean;
            startTimeColumn?: string;
            strictOrdering?: boolean;
            weight?: string;
        } = {}

        An optional object with direction, time, cost, and result configuration.

        • Optionaldirection?: "outgoing" | "incoming" | "both"

          The direction in which to follow connections. Defaults to "outgoing".

        • OptionalelapsedTime?: boolean

          Whether to add cumulative elapsedTimeMs in milliseconds, including connection gaps. Defaults to false. Requires both time columns.

        • OptionalendTimeColumn?: string

          The name of the column containing each connection's end time. Use this or startTimeColumn to follow connections in chronological order.

        • OptionalminGapMs?: number

          The minimum gap between consecutive connections, in milliseconds. Must be a non-negative integer. Defaults to 0. Requires a time column.

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

        • OptionalstartTimeColumn?: string

          The name of the column containing each connection's start time. Use this or endTimeColumn to follow connections in chronological order.

        • OptionalstrictOrdering?: boolean

          Whether to reject zero-duration gaps between consecutive connections (equal timestamps). Defaults to true. Requires a time column.

        • Optionalweight?: string

          The name of the numeric column used as the cost of each connection. If omitted, each connection costs one.

      Returns SimpleTable

      The result table, so methods can be chained.

      await triangle
      .findCycles("source", "target", "edgeId", "A")
      .log();
      start pathId step weight total edgeId source target
      A 0 1 1 1 E1 A B
      A 0 2 1 2 E2 B C
      A 0 3 1 3 E3 C A

      Requesting both A and B returns the cycle separately for each start:

      await triangle
      .findCycles("source", "target", "edgeId", ["A", "B"])
      .log();
      start pathId step weight total edgeId source target
      A 0 1 1 1 E1 A B
      A 0 2 1 2 E2 B C
      A 0 3 1 3 E3 C A
      B 0 1 1 1 E2 B C
      B 0 2 1 2 E3 C A
      B 0 3 1 3 E1 A B

      With direction: "incoming", connections are followed from target to source:

      await triangle
      .findCycles("source", "target", "edgeId", "A", { direction: "incoming" })
      .log();
      start pathId step weight total edgeId source target
      A 0 1 1 1 E3 C A
      A 0 2 1 2 E2 B C
      A 0 3 1 3 E1 A B

      With direction: "both", either direction is allowed. This cycle appears once, in the direction that starts with E1:

      await triangle
      .findCycles("source", "target", "edgeId", "A", { direction: "both" })
      .log();
      start pathId step weight total edgeId source target
      A 0 1 1 1 E1 A B
      A 0 2 1 2 E2 B C
      A 0 3 1 3 E3 C A

      Two separate connections between A and B form a cycle with direction: "both". The self-connection at C forms another cycle. For this input:

      edgeId source target cost
      P1 A B 1
      P2 A B 2
      L1 C C 4
      await parallelAndLoop
      .findCycles("source", "target", "edgeId", ["A", "C"], {
      direction: "both",
      weight: "cost",
      })
      .log();
      start pathId step weight total edgeId source target cost
      A 0 1 1 1 P1 A B 1
      A 0 2 2 3 P2 A B 2
      C 0 1 4 4 L1 C C 4

      With the weight option, total is a running total of the selected values. For these flights:

      flightId origin destination minutes
      F1 A B 1
      F2 B C 2
      F3 C A 3
      await flights
      .findCycles("origin", "destination", "flightId", "A", {
      weight: "minutes",
      })
      .log();
      start pathId step weight total flightId origin destination minutes
      A 0 1 1 1 F1 A B 1
      A 0 2 2 3 F2 B C 2
      A 0 3 3 6 F3 C A 3

      For an input without connection IDs, use addId() first:

      source target
      A B
      B C
      C A
      await unnumberedConnections
      .addId("edgeId", { prefix: "edge-" })
      .findCycles("source", "target", "edgeId", "A")
      .log();
      start pathId step weight total source target edgeId
      A 0 1 1 1 A B edge-0
      A 0 2 1 2 B C edge-1
      A 0 3 1 3 C A edge-2

      For these instantaneous events, requesting A and B as starting nodes returns only the cycle B → C → A → B. Starting at A would break chronological order:

      flightId origin destination departureTime
      F1 B C 2025-01-01 09:00
      F2 C A 2025-01-01 10:00
      F3 A B 2025-01-01 11:00
      await timedFlights
      .findCycles("origin", "destination", "flightId", ["A", "B"], {
      startTimeColumn: "departureTime",
      })
      .log();
      start pathId step weight total flightId origin destination departureTime
      B 0 1 1 1 F1 B C 2025-01-01 09:00:00
      B 0 2 1 2 F2 C A 2025-01-01 10:00:00
      B 0 3 1 3 F3 A B 2025-01-01 11:00:00

      Searching the same events from B in the incoming direction follows connections backward, from the latest to the earliest:

      await timedFlights
      .findCycles("origin", "destination", "flightId", "B", {
      direction: "incoming",
      startTimeColumn: "departureTime",
      })
      .log();
      start pathId step weight total flightId origin destination departureTime
      B 0 1 1 1 F3 A B 2025-01-01 11:00:00
      B 0 2 1 2 F2 C A 2025-01-01 10:00:00
      B 0 3 1 3 F1 B C 2025-01-01 09:00:00

      For the next two examples, flights contains these rows, with departure and arrival stored as timestamps on the same day:

      flightId origin destination departure arrival price
      F1 A B 2025-01-01 09:00 2025-01-01 10:00 40
      F2 B A 2025-01-01 12:00 2025-01-01 13:00 60

      Report elapsed time for every cycle, including layovers:

      await flights
      .findCycles("origin", "destination", "flightId", "A", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      })
      .log();
      start pathId step weight total elapsedTimeMs flightId origin destination departure arrival price
      A 0 1 1 1 3600000 F1 A B 2025-01-01 09:00:00 2025-01-01 10:00:00 40
      A 0 2 1 2 14400000 F2 B A 2025-01-01 12:00:00 2025-01-01 13:00:00 60

      The return journey takes four hours (14,400,000 milliseconds), including the two-hour layover at B.

      Add up prices while continuing to report elapsed time for every cycle:

      await flights
      .findCycles("origin", "destination", "flightId", "A", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      weight: "price",
      })
      .log();
      start pathId step weight total elapsedTimeMs flightId origin destination departure arrival price
      A 0 1 40 40 3600000 F1 A B 2025-01-01 09:00:00 2025-01-01 10:00:00 40
      A 0 2 60 100 14400000 F2 B A 2025-01-01 12:00:00 2025-01-01 13:00:00 60
    • Finds the distinct nodes directly connected to one or more starting nodes. The direction option lets you find neighbors from source to target, from target to source, or in either direction. The result has start and node columns, sorted in ascending order by start, then node.

      Unknown starting IDs and starts with no neighbors in the selected direction produce no rows. A self-connection includes the starting node once. Empty start arrays and duplicate starting IDs throw an error.

      The next three examples each start with this data:

      origin destination
      A B
      A B
      C A

      By default, the method finds neighbors from source to target:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • startNodes: string | number | bigint | (string | number | bigint)[]

        One starting node ID or an array of distinct starting node IDs.

      • options: { direction?: "outgoing" | "incoming" | "both"; outputTable?: string | boolean } = {}

        An optional object with direction and result configuration.

        • Optionaldirection?: "outgoing" | "incoming" | "both"

          The direction in which to follow connections. Defaults to "outgoing".

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

      Returns SimpleTable

      The result table, so methods can be chained.

      await table
      .neighbors("origin", "destination", "A")
      .log();
      start node
      A B

      B appears only once, even though the input has two connections from A to B.

      With direction: "incoming", the method finds neighbors from target to source. Here, C connects to A:

      await table
      .neighbors("origin", "destination", "A", { direction: "incoming" })
      .log();
      start node
      A C

      With direction: "both", the method finds neighbors in either direction:

      await table
      .neighbors("origin", "destination", "A", { direction: "both" })
      .log();
      start node
      A B
      A C

      Multiple starts are evaluated independently. For this input:

      source target cost
      Montreal Ottawa 2
      Ottawa Toronto 3
      await table
      .neighbors("source", "target", ["Montreal", "Ottawa"])
      .log();
      start node
      Montreal Ottawa
      Ottawa Toronto
    • Finds all routes between two different nodes. A route cannot visit the same node twice, to avoid loops. Different routes can still share the same nodes.

      The direction option lets you follow connections from source to target, from target to source, or in either direction.

      Each connection needs a unique, non-null ID. Pass the name of the column containing these IDs as the edgeId argument. If your table is missing an ID column, you can easily create one with addId().

      The result starts with the pathId, step, weight, and total columns, followed by all original columns. Steps start at one and follow the search direction. The weight column is the connection cost, and total is the sum of those costs up to and including the current step. Without the weight option, each connection costs one.

      If an input column is already named pathId, step, weight, or total (regardless of capitalization), the method throws an error. Rename it with renameColumns() first.

      Each row is one connection along a route.

      Use the startTimeColumn or endTimeColumn options to follow connections in chronological order. The minGapMs option sets the minimum gap between consecutive connections, in milliseconds. With both time columns, gaps are measured from one connection's end to the next connection's start; with one column, between their timestamps. The strictOrdering option defaults to true, rejecting zero-duration gaps. These options only work with direction: "outgoing" or direction: "incoming".

      The minimum gap applies only between consecutive connections, not before the first connection.

      Routes are numbered from zero by comparing their sequences of connection IDs, element by element. Rows are sorted by pathId, then step.

      Unknown IDs or nodes with no route between them produce no rows. The start and end must differ. Self-connections are ignored. Weights must be non-null, finite, and non-negative.

      There is no limit on route length or the number of routes returned. Finding all routes can take a long time and use substantial memory.

      For the first example, we start with these connections:

      edgeId source target
      E1 A B
      E2 A C
      E3 B D
      E4 C D

      By default, connections are followed from source to target. Both routes from A to D are returned:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • edgeId: string

        The name of the column uniquely identifying each connection (edge).

      • start: string | number | bigint

        The starting node ID.

      • end: string | number | bigint

        The ending node ID, which must differ from start.

      • options: {
            direction?: "outgoing" | "incoming" | "both";
            elapsedTime?: boolean;
            endTimeColumn?: string;
            minGapMs?: number;
            outputTable?: string | boolean;
            startTimeColumn?: string;
            strictOrdering?: boolean;
            weight?: string;
        } = {}

        An optional object with direction, time, and result configuration.

        • Optionaldirection?: "outgoing" | "incoming" | "both"

          The direction in which to follow connections. Defaults to "outgoing".

        • OptionalelapsedTime?: boolean

          Whether to add cumulative elapsedTimeMs in milliseconds, including connection gaps. Defaults to false. Requires both time columns.

        • OptionalendTimeColumn?: string

          The name of the column containing each connection's end time. Use this or startTimeColumn to follow connections in chronological order.

        • OptionalminGapMs?: number

          The minimum gap between consecutive connections, in milliseconds. Must be a non-negative integer. Defaults to 0. Requires a time column.

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

        • OptionalstartTimeColumn?: string

          The name of the column containing each connection's start time. Use this or endTimeColumn to follow connections in chronological order.

        • OptionalstrictOrdering?: boolean

          Whether to reject zero-duration gaps between consecutive connections (equal timestamps). Defaults to true. Requires a time column.

        • Optionalweight?: string

          The name of the numeric column used as the cost of each connection. If omitted, each connection costs one.

      Returns SimpleTable

      The result table, so methods can be chained.

      await connections
      .paths("source", "target", "edgeId", "A", "D")
      .log();
      pathId step weight total edgeId source target
      0 1 1 1 E1 A B
      0 2 1 2 E3 B D
      1 1 1 1 E2 A C
      1 2 1 2 E4 C D

      For an input without connection IDs, use addId() first:

      source target
      A B
      B D

      The generated IDs identify the connections in the result:

      await unnumberedConnections
      .addId("edgeId", { prefix: "edge-" })
      .paths("source", "target", "edgeId", "A", "D")
      .log();
      pathId step weight total source target edgeId
      0 1 1 1 A B edge-0
      0 2 1 2 B D edge-1

      The next three examples each start with these flights. The direct flight takes ten minutes; the route through B takes three:

      flightId origin destination minutes
      F1 A D 10
      F2 A B 1
      F3 B D 2

      With weight: "minutes", both routes are returned with their travel times:

      await flights
      .paths("origin", "destination", "flightId", "A", "D", {
      weight: "minutes",
      })
      .log();
      pathId step weight total flightId origin destination minutes
      0 1 10 10 F1 A D 10
      1 1 1 1 F2 A B 1
      1 2 2 3 F3 B D 2

      Without the weight option, the same routes are returned and total counts connections:

      await flights
      .paths("origin", "destination", "flightId", "A", "D")
      .log();
      pathId step weight total flightId origin destination minutes
      0 1 1 1 F1 A D 10
      1 1 1 1 F2 A B 1
      1 2 1 2 F3 B D 2

      With direction: "incoming", the search runs from D to A. Steps follow that direction, while the original origin and destination values stay unchanged:

      await flights
      .paths("origin", "destination", "flightId", "D", "A", {
      direction: "incoming",
      weight: "minutes",
      })
      .log();
      pathId step weight total flightId origin destination minutes
      0 1 10 10 F1 A D 10
      1 1 2 2 F3 B D 2
      1 2 1 3 F2 A B 1

      Chronological routes enforce the connection time between every pair of steps. For the next two examples, the 09:00 flight leaves before F1 arrives, while F3 meets the one-hour minimum exactly:

      flightId origin destination departureTime arrivalTime cost
      F1 A B 2025-01-01 08:00 2025-01-01 10:00 2
      F2 B C 2025-01-01 09:00 2025-01-01 10:00 9
      F3 B D 2025-01-01 11:00 2025-01-01 12:00 3

      The route from A to D contains F1 and F3:

      await scheduledFlights
      .paths("origin", "destination", "flightId", "A", "D", {
      startTimeColumn: "departureTime",
      endTimeColumn: "arrivalTime",
      minGapMs: 60 * 60 * 1000,
      weight: "cost",
      })
      .log();
      pathId step weight total flightId origin destination departureTime arrivalTime cost
      0 1 2 2 F1 A B 2025-01-01 08:00:00 2025-01-01 10:00:00 2
      0 2 3 5 F3 B D 2025-01-01 11:00:00 2025-01-01 12:00:00 3

      Searching backward from D finds the same physical journey. The steps and cumulative totals follow the incoming search from D to A:

      await scheduledFlights
      .paths("origin", "destination", "flightId", "D", "A", {
      direction: "incoming",
      startTimeColumn: "departureTime",
      endTimeColumn: "arrivalTime",
      minGapMs: 60 * 60 * 1000,
      weight: "cost",
      })
      .log();
      pathId step weight total flightId origin destination departureTime arrivalTime cost
      0 1 3 3 F3 B D 2025-01-01 11:00:00 2025-01-01 12:00:00 3
      0 2 2 5 F1 A B 2025-01-01 08:00:00 2025-01-01 10:00:00 2

      For the next two examples, flights contains these rows, with departure and arrival stored as timestamps on the same day:

      flightId origin destination departure arrival price
      F1 A B 2025-01-01 09:00 2025-01-01 10:00 40
      F2 B C 2025-01-01 12:00 2025-01-01 13:00 40
      F3 A C 2025-01-01 09:00 2025-01-01 12:00 120

      Report elapsed time for every path, including layovers:

      await flights
      .paths("origin", "destination", "flightId", "A", "C", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      })
      .log();
      pathId step weight total elapsedTimeMs flightId origin destination departure arrival price
      0 1 1 1 3600000 F1 A B 2025-01-01 09:00:00 2025-01-01 10:00:00 40
      0 2 1 2 14400000 F2 B C 2025-01-01 12:00:00 2025-01-01 13:00:00 40
      1 1 1 1 10800000 F3 A C 2025-01-01 09:00:00 2025-01-01 12:00:00 120

      The route through B takes four hours (14,400,000 milliseconds), including its two-hour layover. The direct flight takes three hours.

      Add up prices while continuing to report elapsed time for every path:

      await flights
      .paths("origin", "destination", "flightId", "A", "C", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      weight: "price",
      })
      .log();
      pathId step weight total elapsedTimeMs flightId origin destination departure arrival price
      0 1 40 40 3600000 F1 A B 2025-01-01 09:00:00 2025-01-01 10:00:00 40
      0 2 40 80 14400000 F2 B C 2025-01-01 12:00:00 2025-01-01 13:00:00 40
      1 1 120 120 10800000 F3 A C 2025-01-01 09:00:00 2025-01-01 12:00:00 120
    • Finds all nodes you can reach from one or more starting nodes, following one or more connections. The direction option lets you follow connections from source to target, from target to source, or in either direction. The result has start and node columns, sorted by start, then node.

      A starting node appears in its own results only if following connections leads back to it, such as a self-connection or a cycle. Each reachable node appears only once per start. With direction: "both", following the same connection out and back also reaches the starting node.

      Unknown starting IDs and starts with no connections to follow produce no rows. Empty start arrays and duplicate starting IDs throw an error.

      Use the startTimeColumn or endTimeColumn options to follow connections in chronological order. The minGapMs option sets the minimum gap between consecutive connections, in milliseconds. With both time columns, gaps are measured from one connection's end to the next connection's start; with one column, between their timestamps. The strictOrdering option defaults to true, rejecting zero-duration gaps. These options only work with direction: "outgoing" or direction: "incoming".

      The next four examples each start with this data:

      origin destination
      A B
      A C
      B D
      C D
      D A

      By default, the method follows connections from source to target. A appears in its own results because A → B → D → A leads back to it:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • startNodes: string | number | bigint | (string | number | bigint)[]

        One starting node ID or an array of distinct starting node IDs.

      • options: {
            direction?: "outgoing" | "incoming" | "both";
            endTimeColumn?: string;
            minGapMs?: number;
            outputTable?: string | boolean;
            startTimeColumn?: string;
            strictOrdering?: boolean;
        } = {}

        An optional object with direction, time, and result configuration.

        • Optionaldirection?: "outgoing" | "incoming" | "both"

          The direction in which to follow connections. Defaults to "outgoing".

        • OptionalendTimeColumn?: string

          The name of the column containing each connection's end time. Use this or startTimeColumn to follow connections in chronological order.

        • OptionalminGapMs?: number

          The minimum gap between consecutive connections, in milliseconds. Must be a non-negative integer. Defaults to 0. Requires a time column.

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

        • OptionalstartTimeColumn?: string

          The name of the column containing each connection's start time. Use this or endTimeColumn to follow connections in chronological order.

        • OptionalstrictOrdering?: boolean

          Whether to reject zero-duration gaps between consecutive connections (equal timestamps). Defaults to true. Requires a time column.

      Returns SimpleTable

      The result table, so methods can be chained.

      await connections
      .reachable("origin", "destination", "A")
      .log();
      start node
      A A
      A B
      A C
      A D

      With direction: "incoming", connections are followed from target to source. Starting at B:

      await connections
      .reachable("origin", "destination", "B", { direction: "incoming" })
      .log();
      start node
      B A
      B B
      B C
      B D

      With direction: "both", connections can be followed in either direction:

      await connections
      .reachable("origin", "destination", "B", { direction: "both" })
      .log();
      start node
      B A
      B B
      B C
      B D

      With multiple starting nodes, each has its own results:

      await connections
      .reachable("origin", "destination", ["B", "A"])
      .log();
      start node
      A A
      A B
      A C
      A D
      B A
      B B
      B C
      B D

      Chronological options retain only journeys with valid connection times. Here, A → B arrives at 10:00, so the 09:00 connection to C is unavailable, while the 11:00 connection to D meets the one-hour minimum gap:

      origin destination departureTime arrivalTime
      A B 2025-01-01 08:00 2025-01-01 10:00
      B C 2025-01-01 09:00 2025-01-01 10:00
      B D 2025-01-01 11:00 2025-01-01 12:00

      The departure and arrival columns are timestamps:

      await flights
      .reachable("origin", "destination", "A", {
      startTimeColumn: "departureTime",
      endTimeColumn: "arrivalTime",
      minGapMs: 60 * 60 * 1000,
      })
      .log();
      start node
      A B
      A D
    • Finds the shortest route between two different nodes. If several routes tie for shortest, all of them are returned. By default, the shortest route uses the fewest connections. Use the weight option to find the route with the smallest sum of values from a numeric column, such as travel time. A route cannot visit the same node twice, to avoid loops. Different routes can still share the same nodes.

      The direction option lets you follow connections from source to target, from target to source, or in either direction.

      Each connection needs a unique, non-null ID. Pass the name of the column containing these IDs as the edgeId argument. If your table is missing an ID column, you can easily create one with addId().

      The result starts with pathId, step, and weight, followed by the optional metric columns and all original columns. Steps start at one and follow the search direction. The weight column is the connection cost, or one when no weight option is supplied. Supplying weight also adds total, the sum of those costs up to and including the current step. Enabling elapsedTime adds cumulative elapsedTimeMs, including gaps.

      If an input column conflicts with a generated result column (regardless of capitalization), the method throws an error. Rename it with renameColumns() first. total is reserved only when weight is supplied, and elapsedTimeMs only when elapsed-time reporting is enabled.

      Each row is one connection along a route.

      Use the startTimeColumn or endTimeColumn options to follow connections in chronological order. The minGapMs option sets the minimum gap between consecutive connections, in milliseconds. With both time columns, gaps are measured from one connection's end to the next connection's start; with one column, between their timestamps. The strictOrdering option defaults to true, rejecting zero-duration gaps. These options only work with direction: "outgoing" or direction: "incoming".

      The minimum gap applies only between consecutive connections, not before the first connection.

      Use minimize: "steps" to choose the fewest connections even when weight or elapsed-time reporting is enabled. Without an explicit minimize, the method minimizes weight when only weight is enabled, elapsed time when only elapsedTime is enabled, and steps when neither is enabled. With both enabled, specify "steps", "weight", or "elapsedTime" explicitly. Elapsed-time reporting requires both time columns.

      All routes tied on the selected metric are returned, even if their other metrics differ. Each route reports its own weights and elapsed times.

      Routes are numbered from zero by comparing their sequences of connection IDs, element by element. Rows are sorted by pathId, then step.

      Unknown IDs or nodes with no route between them produce no rows. The start and end must differ. Self-connections are ignored. Weights must be non-null, finite, and non-negative.

      There is no limit on route length or the number of tied routes returned. Finding many tied routes can take a long time and use substantial memory.

      The first example uses this table:

      edgeId source target
      E1 A B
      E2 A C
      E3 B D
      E4 C D
      E5 D E

      By default, the method finds routes from source to target with the fewest connections. Two routes from A to E tie at three connections each:

      Parameters

      • sourceColumn: string

        The name of the column containing each connection's source node ID.

      • targetColumn: string

        The name of the column containing each connection's target node ID.

      • edgeId: string

        The name of the column uniquely identifying each connection (edge).

      • start: string | number | bigint

        The starting node ID.

      • end: string | number | bigint

        The ending node ID, which must differ from start.

      • options: {
            direction?: "outgoing" | "incoming" | "both";
            elapsedTime?: boolean;
            endTimeColumn?: string;
            minGapMs?: number;
            minimize?: "weight" | "steps" | "elapsedTime";
            outputTable?: string | boolean;
            startTimeColumn?: string;
            strictOrdering?: boolean;
            weight?: string;
        } = {}

        An optional object with direction, time, and result configuration.

        • Optionaldirection?: "outgoing" | "incoming" | "both"

          The direction in which to follow connections. Defaults to "outgoing".

        • OptionalelapsedTime?: boolean

          Whether to add cumulative elapsedTimeMs in milliseconds, including connection gaps. Defaults to false. Requires both time columns.

        • OptionalendTimeColumn?: string

          The name of the column containing each connection's end time. Use this or startTimeColumn to follow connections in chronological order.

        • OptionalminGapMs?: number

          The minimum gap between consecutive connections, in milliseconds. Must be a non-negative integer. Defaults to 0. Requires a time column.

        • Optionalminimize?: "weight" | "steps" | "elapsedTime"

          The metric used to select optimal routes: "steps", "weight", or "elapsedTime". Defaults to "weight" when only weight is enabled, "elapsedTime" when only elapsed-time reporting is enabled, and "steps" when neither is enabled. Required when both are enabled. "steps" is always available; the other metrics require their corresponding options.

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

        • OptionalstartTimeColumn?: string

          The name of the column containing each connection's start time. Use this or endTimeColumn to follow connections in chronological order.

        • OptionalstrictOrdering?: boolean

          Whether to reject zero-duration gaps between consecutive connections (equal timestamps). Defaults to true. Requires a time column.

        • Optionalweight?: string

          The name of the numeric column used as the cost of each connection. Adds total, the cumulative sum of those costs. If omitted, no total column is generated.

      Returns SimpleTable

      The table of route connections with optional cumulative total and elapsedTimeMs metrics, so methods can be chained.

      await connections
      .shortestPath("source", "target", "edgeId", "A", "E")
      .log();
      pathId step weight edgeId source target
      0 1 1 E1 A B
      0 2 1 E3 B D
      0 3 1 E5 D E
      1 1 1 E2 A C
      1 2 1 E4 C D
      1 3 1 E5 D E

      For an input without connection IDs, use addId() first:

      source target
      A B
      B E

      The generated IDs identify the connections in the result:

      await unnumberedConnections
      .addId("edgeId", { prefix: "edge-" })
      .shortestPath("source", "target", "edgeId", "A", "E")
      .log();
      pathId step weight source target edgeId
      0 1 1 A B edge-0
      0 2 1 B E edge-1

      The next two examples each start with these flights. The direct flight takes ten minutes; the route through B and D takes three:

      flightId origin destination minutes
      F1 A E 10
      F2 A B 1
      F3 B D 1
      F4 D E 1

      With weight: "minutes", the route with the shortest travel time is chosen:

      await flights
      .shortestPath("origin", "destination", "flightId", "A", "E", {
      weight: "minutes",
      })
      .log();
      pathId step weight total flightId origin destination minutes
      0 1 1 1 F2 A B 1
      0 2 1 2 F3 B D 1
      0 3 1 3 F4 D E 1

      Without the weight option, the direct flight is shortest because it uses only one connection:

      await flights
      .shortestPath("origin", "destination", "flightId", "A", "E")
      .log();
      pathId step weight flightId origin destination minutes
      0 1 1 F1 A E 10

      With time options, the cheaper route through B is ruled out because F2 leaves before F1 arrives. The route through C meets the one-hour minimum gap:

      flightId origin destination departureTime arrivalTime cost
      F1 A B 2025-01-01 08:00 2025-01-01 10:00 1
      F2 B E 2025-01-01 09:00 2025-01-01 10:00 1
      F3 A C 2025-01-01 08:00 2025-01-01 09:00 2
      F4 C E 2025-01-01 10:00 2025-01-01 11:00 2
      await scheduledFlights
      .shortestPath("origin", "destination", "flightId", "A", "E", {
      startTimeColumn: "departureTime",
      endTimeColumn: "arrivalTime",
      minGapMs: 60 * 60 * 1000,
      weight: "cost",
      })
      .log();
      pathId step weight total flightId origin destination departureTime arrivalTime cost
      0 1 2 2 F3 A C 2025-01-01 08:00:00 2025-01-01 09:00:00 2
      0 2 2 4 F4 C E 2025-01-01 10:00:00 2025-01-01 11:00:00 2

      With direction: "incoming", connections are followed from target to source. The original source and target values remain unchanged. For this input:

      edgeId source target
      F1 A B
      await reverseExample
      .shortestPath("source", "target", "edgeId", "B", "A", {
      direction: "incoming",
      })
      .log();
      pathId step weight edgeId source target
      0 1 1 F1 A B

      For the next three examples, flights contains these rows, with departure and arrival stored as timestamps on the same day:

      flightId origin destination departure arrival price
      F1 A B 2025-01-01 09:00 2025-01-01 10:00 40
      F2 B C 2025-01-01 12:00 2025-01-01 13:00 40
      F3 A C 2025-01-01 09:00 2025-01-01 12:00 120
      F4 A D 2025-01-01 09:00 2025-01-01 09:30 60
      F5 D C 2025-01-01 10:30 2025-01-01 11:00 80

      Find the fastest journey from A to C, including layovers:

      await flights
      .shortestPath("origin", "destination", "flightId", "A", "C", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      })
      .log();
      pathId step weight elapsedTimeMs flightId origin destination departure arrival price
      0 1 1 1800000 F4 A D 2025-01-01 09:00:00 2025-01-01 09:30:00 60
      0 2 1 7200000 F5 D C 2025-01-01 10:30:00 2025-01-01 11:00:00 80

      The route through D takes two hours (7,200,000 milliseconds), including a one-hour layover. The direct flight takes three hours.

      Find the fastest journey while also adding up its price:

      await flights
      .shortestPath("origin", "destination", "flightId", "A", "C", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      weight: "price",
      minimize: "elapsedTime",
      })
      .log();
      pathId step weight total elapsedTimeMs flightId origin destination departure arrival price
      0 1 60 60 1800000 F4 A D 2025-01-01 09:00:00 2025-01-01 09:30:00 60
      0 2 80 140 7200000 F5 D C 2025-01-01 10:30:00 2025-01-01 11:00:00 80

      Use minimize: "weight" to find the cheapest journey instead. All ties on the chosen metric are returned.

      Choose the fewest flights while still reporting price and elapsed time. The direct flight takes one step, although the route through D is faster:

      await flights
      .shortestPath("origin", "destination", "flightId", "A", "C", {
      startTimeColumn: "departure",
      endTimeColumn: "arrival",
      minGapMs: 60 * 60 * 1000,
      elapsedTime: true,
      weight: "price",
      minimize: "steps",
      })
      .log();
      pathId step weight total elapsedTimeMs flightId origin destination departure arrival price
      0 1 120 120 10800000 F3 A C 2025-01-01 09:00:00 2025-01-01 12:00:00 120
    • Orders nodes so each prerequisite comes before the nodes that depend on it. Each input row means the source node must come before the target node. For example, A → B means A must come before B.

      The result has node, componentId, and order columns, sorted by componentId, then order. Nodes connected directly or through other nodes belong to the same group, ignoring connection direction. Groups are numbered from zero, with the group containing the smallest node ID first.

      Within each group, order starts at one. When several nodes are ready, the method chooses the smallest node ID first: numeric order for numbers and character order for strings. All nodes in the source and target columns are included.

      If dependencies form a loop, there is no valid order and the method throws an error. A node depending on itself also causes an error. An empty input produces no rows.

      For this example, we start with these connections:

      source target
      A B
      B C
      D C

      A must come before B, and both B and D must come before C:

      Parameters

      • sourceColumn: string

        The name of the column containing each prerequisite node ID.

      • targetColumn: string

        The name of the column containing each dependent node ID.

      • options: { outputTable?: string | boolean } = {}

        An optional object with result configuration.

        • OptionaloutputTable?: string | boolean

          If true, stores the result in a new table with a generated name. If a string, uses it as the new table's name. If false or omitted, overwrites the current table. Defaults to false.

      Returns SimpleTable

      The result table, so methods can be chained.

      await table
      .topologicalSort("source", "target")
      .log();
      node componentId order
      A 0 1
      B 0 2
      D 0 3
      C 0 4

      C comes last because it depends on both B and D. A comes before D because both are initially ready and A has the smaller ID. After A, B is ready and comes before D for the same reason.

      With two independent groups, each has its own order starting at one. This example starts with different data:

      source target
      A D
      B C
      await table
      .topologicalSort("source", "target")
      .log();
      node componentId order
      A 0 1
      D 0 2
      B 1 1
      C 1 2
    • Inserts rows, provided as an array of JavaScript objects, into the table.

      Parameters

      • rows: { [key: string]: unknown }[]

        An array of objects, where each object represents a row to be inserted and its properties correspond to column names.

      Returns this

      The table, so methods can be chained.

      // Insert new rows into the table
      const newRows = [
      { letter: "c", number: 3 },
      { letter: "d", number: 4 }
      ];
      await table.insertRows(newRows).log();
    • Inserts all rows from one or more other tables into this table. If tables do not have the same columns, an error will be thrown unless the unifyColumns option is set to true.

      Parameters

      • tables: SimpleTable | SimpleTable[]

        The name(s) of the table(s) or SimpleTable instance(s) from which rows will be inserted.

      • options: { unifyColumns?: boolean } = {}

        An optional object with configuration options:

        • OptionalunifyColumns?: boolean

          A boolean indicating whether to unify the columns of the tables. If true, missing columns in a table will be filled with NULL values. Defaults to false.

      Returns this

      The table, so methods can be chained.

      // Insert all rows from 'tableB' into 'tableA'.
      await tableA.insertTables("tableB").log();
      // Insert all rows from 'tableB' and 'tableC' into 'tableA'.
      await tableA.insertTables(["tableB", "tableC"]).log();
      // Insert rows from multiple tables, unifying columns. Missing columns will be filled with NULL.
      await tableA.insertTables(["tableB", "tableC"], { unifyColumns: true }).log();
    • Loads an array of JavaScript objects into the table. Types are inferred for numbers, bigints, strings, booleans, and Date values. Array and object cells require an explicit supported type in columnTypes. Types can also be specified for scalar columns instead of inferred from their values. Inferred bigints use signed 64-bit BIGINT; values outside that range throw when written instead of wrapping.

      JavaScript Date values are inferred as DuckDB TIMESTAMP values. Their instant is preserved, but JavaScript Date does not retain the timezone or offset originally used to construct it.

      Declare JSON for general nested data, FLOAT[n] for a fixed-size float vector, or GEOMETRY('EPSG:4326') for GeoJSON geometries in WGS84.

      Parameters

      • rows: { [key: string]: unknown }[]

        An array of objects, where each object represents a row and its properties represent columns.

      • options: {
            columnTypes?: {
                [key: string]:
                    | "string"
                    | "number"
                    | "bigint"
                    | "boolean"
                    | "date"
                    | "VARCHAR"
                    | "BIGINT"
                    | "DOUBLE"
                    | "TIMESTAMP"
                    | "DATE"
                    | "integer"
                    | "float"
                    | "time"
                    | "datetime"
                    | "datetimeTz"
                    | "double"
                    | "varchar"
                    | "timestamp"
                    | "timestamp with time zone"
                    | "json"
                    | "JSON"
                    | `float[${number}]`
                    | `FLOAT[${number}]`
                    | "TIME"
                    | "TIMESTAMP WITH TIME ZONE"
                    | "INTEGER"
                    | "BOOLEAN"
                    | "GEOMETRY('EPSG:4326')"
                    | "geometry('EPSG:4326')";
            };
        } = {}

        Options for loading the array, captured when called.

        • OptionalcolumnTypes?: {
              [key: string]:
                  | "string"
                  | "number"
                  | "bigint"
                  | "boolean"
                  | "date"
                  | "VARCHAR"
                  | "BIGINT"
                  | "DOUBLE"
                  | "TIMESTAMP"
                  | "DATE"
                  | "integer"
                  | "float"
                  | "time"
                  | "datetime"
                  | "datetimeTz"
                  | "double"
                  | "varchar"
                  | "timestamp"
                  | "timestamp with time zone"
                  | "json"
                  | "JSON"
                  | `float[${number}]`
                  | `FLOAT[${number}]`
                  | "TIME"
                  | "TIMESTAMP WITH TIME ZONE"
                  | "INTEGER"
                  | "BOOLEAN"
                  | "GEOMETRY('EPSG:4326')"
                  | "geometry('EPSG:4326')";
          }

          Types for specific columns. Required for array and object cells; omitted scalar columns are inferred, and all-null columns default to VARCHAR. Values must be compatible with the selected type without losing information.

      Returns this

      The table, so methods can be chained.

      // Load scalar columns without columnTypes; strings and numbers are inferred.
      const data = [
      { letter: "a", number: 1 },
      { letter: "b", number: 2 }
      ];
      await table.loadArray(data).log();
      // Choose a numeric type when all-null values provide nothing to infer.
      await table.loadArray(
      [{ name: "A", value: null }, { name: "B", value: null }],
      { columnTypes: { value: "DOUBLE" } },
      ).log();
      // Store the Date as a UTC timestamp with JavaScript's millisecond precision.
      // The original -04:00 offset is not retained.
      await table.loadArray([{
      observedAt: new Date("2024-04-07T13:00:00-04:00"),
      }]).log();
      // Declare JSON to load nested objects and variable-length arrays directly.
      await table.loadArray([{
      name: "Station",
      metadata: { active: true, tags: ["weather", "urban"] },
      measurements: [1, 2.5, null],
      }], { columnTypes: { metadata: "JSON", measurements: "JSON" } }).log();
      // Declare FLOAT[3] to store each three-element array as a fixed-size vector.
      await table.loadArray([{ vector: [1, 2, 3] }], {
      columnTypes: { vector: "FLOAT[3]" },
      }).log();
      // Declare a geometry type to store WGS84 GeoJSON as a spatial column.
      await table.loadArray([{
      name: "Montreal",
      geom: { type: "Point", coordinates: [-73.57, 45.50] },
      }], { columnTypes: { geom: "GEOMETRY('EPSG:4326')" } }).log();
    • Loads data from one or more local or remote files into the table. Supported file formats include CSV, JSON, Parquet, and Excel.

      With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Parameters

      • files: string | string[]

        The path(s) or URL(s) of the file(s) containing the data to be loaded.

      • options: {
            allText?: boolean;
            autoDetect?: boolean;
            columns?: string[];
            columnTypes?: { [key: string]: string };
            compression?: "none" | "gzip" | "zstd";
            conditions?: string;
            delim?: string;
            encoding?: string;
            fileType?: "json" | "csv" | "dsv" | "parquet" | "excel";
            header?: boolean;
            ignoreErrors?: boolean;
            includeFilename?: boolean;
            jsonFormat?: "array" | "unstructured" | "newlineDelimited";
            limit?: number;
            nullPadding?: boolean;
            records?: boolean;
            sheet?: string;
            skip?: number;
            strict?: boolean;
            unifyColumns?: boolean;
        } = {}

        An optional object with configuration options:

        • OptionalallText?: boolean

          A boolean indicating whether all columns should be treated as text. Applicable to CSV files. Defaults to false.

        • OptionalautoDetect?: boolean

          A boolean indicating whether to automatically detect the data format. Defaults to true.

        • Optionalcolumns?: string[]

          An array of column names to load. When provided, only the specified columns are loaded, reducing memory usage and improving load times. Not supported for Excel files — combining columns with Excel files throws an error. If an invalid column name is provided, DuckDB will throw its native error. An empty array behaves the same as omitting the option (loads all columns). Defaults to loading all columns.

        • OptionalcolumnTypes?: { [key: string]: string }

          An object mapping column names to their expected data types. By default, types are inferred. Geometry types are not supported; use loadGeoData() instead.

        • Optionalcompression?: "none" | "gzip" | "zstd"

          The compression type of the file. Applicable to CSV files. Defaults to none.

        • Optionalconditions?: string

          A SQL WHERE clause expression, without the WHERE keyword, to filter source rows before applying limit. Can reference source columns excluded from columns.

        • Optionaldelim?: string

          The delimiter used in the file. Applicable to CSV and DSV files. By default, the delimiter is inferred.

        • Optionalencoding?: string

          The encoding of the file. Applicable to CSV files. Defaults to utf-8.

        • OptionalfileType?: "json" | "csv" | "dsv" | "parquet" | "excel"

          The type of file to load ("csv", "dsv", "json", "parquet", "excel"). Defaults to being inferred from the file extension.

        • Optionalheader?: boolean

          A boolean indicating whether the file has a header row. Applicable to CSV files. Defaults to true.

        • OptionalignoreErrors?: boolean

          If true, parsing errors encountered will be ignored, and rows with errors will be skipped. Defaults to false.

        • OptionalincludeFilename?: boolean

          A boolean indicating whether to include the filename as a new column in the loaded data. Defaults to false.

        • OptionaljsonFormat?: "array" | "unstructured" | "newlineDelimited"

          The format of JSON files ("unstructured", "newlineDelimited", "array"). By default, the format is inferred.

        • Optionallimit?: number

          A number indicating the maximum number of matching rows to load, after applying conditions if provided. Defaults to all matching rows.

        • OptionalnullPadding?: boolean

          If true, when a row has fewer columns than expected, the remaining columns on the right will be padded with NULL values. Defaults to false.

        • Optionalrecords?: boolean

          A boolean indicating whether each line in a newline-delimited JSON file represents a record. Applicable to JSON files. By default, it's inferred.

        • Optionalsheet?: string

          A string indicating a specific sheet to import from an Excel file. By default, the first sheet is imported.

        • Optionalskip?: number

          The number of lines to skip at the beginning of the file. Applicable to CSV files. Defaults to 0.

        • Optionalstrict?: boolean

          If true, an error will be thrown when encountering any issues. If false, structurally incorrect files will be parsed tentatively. Defaults to true.

        • OptionalunifyColumns?: boolean

          A boolean indicating whether to unify columns across multiple files when their structures differ. Missing columns will be filled with NULL values. Defaults to false.

      Returns this

      The table, so methods can be chained.

      // Load data from a single local CSV file
      await table.loadData("./some-data.csv").log();
      // Load data from a remote Parquet file
      await table.loadData("https://some-website.com/some-data.parquet").log();
      // Load data from multiple local JSON files
      await table.loadData([
      "./some-data1.json",
      "./some-data2.json",
      "./some-data3.json"
      ]).log();
      // Load multiple CSV files and unify columns that differ between files
      await table.loadData("./data/*.csv", { unifyColumns: true }).log();
      // Keep the source filename when loading multiple files
      await table
      .loadData("./data/*.csv", { includeFilename: true })
      .log();
      // Load only specific columns from a CSV file
      await table.loadData("./employees.csv", { columns: ["name", "salary"] }).log();
      // Load up to 100 matching employees, keeping only their names
      await table
      .loadData("./employees.parquet", {
      conditions: "salary > 100000",
      columns: ["name"],
      limit: 100,
      })
      .log();
    • Fetches sample data from the simple-data-analysis-core GitHub repository.

      Parameters

      Returns this

      // Load the fires sample data
      await table.loadSample("fires").log();
    • Downloads a complete Statistics Canada table and loads it into this table.

      Results are cached as Parquet files in .sda-cache/statcan by default. Cached data does not expire unless a TTL is provided.

      Parameters

      • pid: string

        The Statistics Canada Product ID. Eight-digit PIDs, ten-digit view PIDs, and hyphenated table identifiers are accepted.

      • options: { cache?: boolean; lang?: "en" | "fr"; ttl?: number } = {}

        Optional retrieval and cache settings.

        • Optionalcache?: boolean

          Whether to read and write the cache. Defaults to true.

        • Optionallang?: "en" | "fr"

          The language of the table data. Defaults to "en".

        • Optionalttl?: number

          Cache lifetime in seconds. Omit for no expiration, use 0 to refresh the matching cache entry immediately, or provide a positive value to refresh once the entry reaches that age.

      Returns this

      The table, so methods can be chained.

      await sdb
      .newTable("population")
      .loadStatCanData("17-10-0005-01")
      .filter("GEO = 'Canada'")
      .log();
      // Refresh French data when the cached table is at least one day old.
      await table
      .loadStatCanData("17-10-0005", {
      lang: "fr",
      ttl: 24 * 60 * 60,
      })
      .log();
    • Downloads historical market data from Yahoo Finance and loads it into this table as datetime, open, high, low, close, adjustedClose, and volume columns. datetime contains Yahoo's timestamps as JavaScript Date values. Unavailable values are preserved as null.

      All range boundaries are evaluated in UTC. Use ISO date-time strings ending in Z. Yahoo trading sessions may occur on a different UTC date from their exchange-local date, so choose the range appropriate for the market and interval. Avoid numeric constructors such as new Date(2025, 2, 15), which use the runtime's local timezone.

      This method uses an undocumented Yahoo Finance endpoint and is not affiliated with or endorsed by Yahoo. It is provided for educational, research, and journalistic purposes. Before using it, review Yahoo's terms and any applicable data-provider restrictions.

      Parameters

      • symbol: string

        The stock or index symbol, such as "AAPL" or "^GSPTSE".

      • startDate: Date

        The inclusive UTC start of the requested range.

      • endDate: Date

        The inclusive UTC end of the requested range. The UTC day, hour, or minute containing this instant is included, according to interval.

      • interval: "1d" | "1h" | "1m"

        The interval between observations: daily, hourly, or every minute.

      Returns this

      The table, so methods can be chained.

      // Request daily observations using explicit UTC boundaries.
      await table
      .loadYahooFinanceData(
      "^GSPTSE",
      new Date("2025-03-01T00:00:00Z"),
      new Date("2025-03-15T00:00:00Z"),
      "1d",
      )
      .log();
      // Include the UTC hours from 13:00 through 16:00.
      await table
      .loadYahooFinanceData(
      "AAPL",
      new Date("2025-03-14T13:00:00Z"),
      new Date("2025-03-14T16:00:00Z"),
      "1h",
      )
      .log();

      If either date is invalid or endDate is before startDate.

      If Yahoo rejects the request or returns no data.

    • Logs up to a specified number of rows from the table to the console. By default, the first 10 rows are logged. You can optionally log the column types and filter the data based on conditions. The footer reports the total number of matching rows and, only when some rows are omitted, the number actually shown (for example, 100 rows in total / showing 15 rows). SQL dates and timestamps retain their native precision for display, including temporal infinities. Lists and objects are stringified using the same nested representations as getData(), then truncated according to charsToLog. Unsafe top-level large integers throw, just as with getData(). Type annotations describe the SQL type and the JavaScript extraction type before display formatting; nulls do not determine the column's annotation. Colors reflect the SQL value's meaning, so exact decimal strings use numeric coloring. Column width is independent of the content truncation budget.

      With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Parameters

      • options:
            | number
            | "all"
            | { conditions?: string; count?: number
            | "all"; types?: boolean } = {}

        Either the number of rows to log (a specific number or "all") or an object with configuration options:

        • number
        • "all"
        • { conditions?: string; count?: number | "all"; types?: boolean }
          • Optionalconditions?: string

            A SQL WHERE clause condition to filter the data before logging. Defaults to no condition.

          • Optionalcount?: number | "all"

            The maximum number of rows to log. Defaults to 10 or the value set in the SimpleDB instance. Use "all" to log all rows.

          • Optionaltypes?: boolean

            Whether to log the column types along with the data. Defaults to the value set in the SimpleDB instance.

      Returns Promise<SimpleTable>

      A promise that resolves to the table, so methods can be chained.

      // Log the first 10 rows (default behavior)
      await table.log();
      // Log the first 50 rows
      await table.log(50);
      // Log all rows
      await table.log("all");
      // Log the first 20 rows and include column types
      await table.log({ count: 20, types: true });
      // Log rows where 'status' is 'active' (using JS syntax for conditions)
      await table.log({ conditions: `status === 'active'` });
    • Logs the bottom n rows of the table to the console. By default, the last row will be returned first. To preserve the original order, use the originalOrder option.

      Uses the same precision-preserving temporal display, nested stringification, numeric coloring, and charsToLog truncation as log(). Unsafe top-level large integers throw, just as with getData().

      Parameters

      • Optionalcount: number

        The number of rows to log from the bottom of the table. Defaults to the table's rowsToLog option if not specified.

      • options: { originalOrder?: boolean } = {}

        An optional object with logging preferences.

        • OptionaloriginalOrder?: boolean

          If true, the rows are displayed in their original order (top to bottom). Defaults to false.

      Returns Promise<SimpleTable>

      A promise that resolves to the table, so methods can be chained.

      // Log bottom rows with default count (uses table's rowsToLog option)
      await table.logBottom();
      // Log the last 10 rows (displayed with last row first)
      await table.logBottom(10);
      // Log the last 5 rows in original order (top to bottom)
      await table.logBottom(5, { originalOrder: true });
    • Logs the columns in the table to the console. You can optionally include their data types.

      Parameters

      • options: { types?: boolean } = {}

        An optional object with configuration options:

        • Optionaltypes?: boolean

          If true, logs the column names along with their data types. Defaults to false.

      Returns Promise<SimpleTable>

      A promise that resolves to the SimpleTable instance after logging the columns.

      // Log only the column names
      await table.logColumns();
      // Log column names along with their types
      await table.logColumns({ types: true });
    • Logs descriptive information about the columns in the table to the console. This includes details such as data types, number of null values, and number of distinct values for each column. It internally calls the getDescription method to retrieve the descriptive statistics.

      Returns Promise<SimpleTable>

      A promise that resolves to the table, so methods can be chained.

      // Log descriptive information for all columns in the table
      await table.logDescription();
    • Logs the extent (minimum and maximum values) of a numeric column to the console.

      Parameters

      • column: string

        The name of the numeric column for which to log the extent.

      Returns Promise<SimpleTable>

      A promise that resolves to the table, so methods can be chained.

      // Log the extent of the 'price' column
      await table.logExtent("price");
    • Logs the projections of the geospatial data (if any) to the console.

      Returns Promise<SimpleTable>

      A promise that resolves to the SimpleTable instance after logging the projections.

      // Log the geospatial projections of the table
      await table.logProjections();
    • Logs the total number of rows in the table to the console.

      Returns Promise<SimpleTable>

      A promise that resolves to the SimpleTable instance after logging the row count.

      // Log the total number of rows in the table
      await table.logRowCount();
    • Logs the types of all columns in the table to the console.

      Returns Promise<SimpleTable>

      A promise that resolves to the SimpleTable instance after logging the column types.

      // Log the data types of all columns in the table
      await table.logTypes();
    • Logs unique values for a specified column to the console. By default, a maximum of 100 values are logged (depending on your runtime). You can optionally stringify the values to see them all.

      Parameters

      • column: string

        The name of the column from which to retrieve and log unique values.

      • options: { stringify?: boolean } = {}

        An optional object with configuration options:

        • Optionalstringify?: boolean

          If true, converts the unique values to a JSON string before logging. Defaults to false.

      Returns Promise<SimpleTable>

      A promise that resolves to the SimpleTable instance after logging the unique values.

      // Logs unique values for the column "name"
      await table.logUniques("name");
      // Logs unique values for the column "name" and stringifies them
      await table.logUniques("name", { stringify: true });
    charsToLog: number | undefined

    The maximum number of characters to display for text and stringified nested cells, including the truncation marker. This does not control column width. Set to Infinity to show the entire text.

    const sdb = new SimpleDB({ charsToLog: 150 });
    await sdb.newTable().loadArray([{ text: "x".repeat(200) }]).log();

    75

    connection: DuckDBConnection

    A connection to a DuckDB database.

    db: DuckDBInstance

    A DuckDB database instance.

    defaultTableName: boolean

    A flag indicating if the table name was assigned by default.

    false

    rowsToLog: number

    The number of rows to display when logging a table.

    10

    runQuery: (
        query: string,
        connection: DuckDBConnection,
        returnData: boolean,
        options: {
            explainSQL: boolean;
            logSQL: boolean;
            method: string | null;
            parameters: { [key: string]: unknown } | null;
            table?: string | null;
            values?: DuckDBValue[];
        },
    ) => Promise<{ [key: string]: unknown }[] | null>

    A function for running SQL queries. Subclasses can wrap this seam to observe execution while forwarding every argument. To run custom SQL, use SimpleDB.customQuery() instead.

    Type Declaration

      • (
            query: string,
            connection: DuckDBConnection,
            returnData: boolean,
            options: {
                explainSQL: boolean;
                logSQL: boolean;
                method: string | null;
                parameters: { [key: string]: unknown } | null;
                table?: string | null;
                values?: DuckDBValue[];
            },
        ): Promise<{ [key: string]: unknown }[] | null>
      • Parameters

        • query: string

          The exact SQL statement to execute.

        • connection: DuckDBConnection

          The DuckDB connection that executes the statement.

        • returnData: boolean

          Whether to convert and return result rows.

        • options: {
              explainSQL: boolean;
              logSQL: boolean;
              method: string | null;
              parameters: { [key: string]: unknown } | null;
              table?: string | null;
              values?: DuckDBValue[];
          }

          Error-attribution, binding, and observability options.

          • explainSQL: boolean

            Whether to log a supported DuckDB query plan before execution.

          • logSQL: boolean

            Whether to log the SQL immediately before execution.

          • method: string | null

            The SDA method responsible for the query, when available.

          • parameters: { [key: string]: unknown } | null

            The method arguments recorded for error attribution.

          • Optionaltable?: string | null

            The table associated with returned values and warnings.

          • Optionalvalues?: DuckDBValue[]

            Data values bound to placeholders in the SQL statement.

        Returns Promise<{ [key: string]: unknown }[] | null>

        The converted rows when requested, otherwise null.

    const original = table.runQuery;
    table.runQuery = (query, connection, returnData, options) => {
    console.log(query);
    return original(query, connection, returnData, options);
    };
    typesToLog: boolean

    A flag indicating whether to include data types when logging a table.

    false

    • Generates a random point within the geometries of a specified column.

      Parameters

      • newColumn: string

        The name of the new column where the random points will be stored.

      • tries: number

        The number of points to generate within the bounding box of each geometry to find one that is within the geometry itself.

      • options: { column?: string; strict?: boolean } = {}

        An optional object with configuration options:

        • Optionalcolumn?: string

          The name of the column storing the geometries within which the random points will be generated. If omitted, the method will automatically attempt to find a geometry column.

        • Optionalstrict?: boolean

          If false, the method will not throw an error if some points cannot be generated. Corresponding rows will have NULL in the new column. Defaults to true.

      Returns this

      // Generate a random point for each geometry in the default column, trying 100 points
      await table.randomPoint("randomPoint", 100).log();
      // Generate a random point for each geometry in a specific column named 'areaGeom', trying 50 points
      await table.randomPoint("pointInArea", 50, { column: "areaGeom" }).log();
      // Generate a random point for each geometry, but don't throw if some points cannot be generated
      await table.randomPoint("pointInArea", 1, { strict: false }).log();
    indexes: (
        | {
            column: string;
            kind: "vss";
            name: string;
            options: { efConstruction?: number; efSearch?: number; M?: number };
        }
        | {
            idColumn: string;
            kind: "fts";
            name: string;
            options: {
                ignore?: string;
                lower?: boolean;
                stemmer?: string;
                stopwords?: string;
                stripAccents?: boolean;
            };
            textColumn: string;
        }
    )[]

    The definitions of the indexes belonging to the table, if any. Do not mutate this array directly.

    console.log(table.indexes);
    // [{ kind: "vss", name: "vss_cosine_index_articles", ... }]

    []

    The SimpleDB instance that created this table.

    • get name(): string

      Name of the table in the database.

      Returns string

      console.log(table.name); // e.g., "employees"
      
    • Restructures this table by stacking (unpivoting) columns. This is useful for tidying up data from a wide format to a long format.

      For example, given a table showing employee counts per department per year:

      Department 2021 2022 2023
      Accounting 10 9 15
      Sales 52 75 98

      We can restructure it by putting all year columns into a new column named Year and their corresponding employee counts into a new column named Employees.

      Parameters

      • columns: string[]

        An array of strings representing the names of the columns to be stacked (unpivoted).

      • namesTo: string

        The name of the new column that will contain the original column names (e.g., "Year").

      • valuesTo: string

        The name of the new column that will contain the values from the stacked columns (e.g., "Employees").

      Returns this

      The table, so methods can be chained.

      // Restructure the table by stacking year columns into 'year' and 'employees'
      await table.longer(["2021", "2022", "2023"], "year", "employees").log();

      The table will then look like this:

      Department Year Employees
      Accounting 2021 10
      Accounting 2022 9
      Accounting 2023 15
      Sales 2021 52
      Sales 2022 75
      Sales 2023 98
    • Sorts the rows of the table based on specified column(s) and order(s). If no columns are specified, all columns are sorted from left to right in ascending order.

      Order-preserving transformations after a sort retain that order. Operations such as joins, grouping, aggregation, and sampling do not guarantee input order; chain sort() after them when deterministic output order matters.

      Parameters

      • order: { [key: string]: "asc" | "desc" } | null = null

        An object mapping column names to their sorting order: "asc" for ascending or "desc" for descending. If null, all columns are sorted ascendingly.

      • options: { lang?: { [key: string]: string } } = {}

        An optional object with configuration options:

      Returns this

      The table, so methods can be chained.

      // Sort all columns from left to right in ascending order
      await table.sort().log();
      // Sort 'column1' in ascending order
      await table.sort({ column1: "asc" }).log();
      // Sort 'column1' ascendingly, then 'column2' descendingly
      await table.sort({ column1: "asc", column2: "desc" }).log();
      // Sort 'column1' considering French accents
      await table.sort({ column1: "asc" }, { lang: { column1: "fr" } }).log();
    • Restructures this table by unstacking (pivoting) values, transforming data from a long format to a wide format.

      For example, given a table showing employee counts per department per year:

      Department Year Employees
      Accounting 2021 10
      Accounting 2022 9
      Accounting 2023 15
      Sales 2021 52
      Sales 2022 75
      Sales 2023 98

      We can restructure it by creating new columns for each year, with the associated employee counts as values.

      Parameters

      • namesFrom: string

        The name of the column containing the values that will be transformed into new column headers (e.g., "Year").

      • valuesFrom: string

        The name of the column containing the values to be spread across the new columns (e.g., "Employees").

      • options: { stat?: "count" | "min" | "max" | "mean" | "median" | "sum" | "first" } = {}

        An optional object with configuration options:

        • Optionalstat?: "count" | "min" | "max" | "mean" | "median" | "sum" | "first"

          The stat function applied when multiple rows share the same namesFrom/grouping combination: "sum", "count", "min", "max", "mean", "median", or "first". Defaults to "sum".

      Returns this

      The table, so methods can be chained.

      // Restructure the table by pivoting 'Year' into new columns with 'Employees' as values
      await table.wider("Year", "Employees").log();

      The table will then look like this:

      Department 2021 2022 2023
      Accounting 10 9 15
      Sales 52 75 98

      When multiple rows share the same namesFrom/grouping combination, their valuesFrom values are combined with the options.stat function ("sum" by default).

    • Filters rows from this table based on SQL conditions. Note that it's often faster to use the removeRows method for simple removals. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Parameters

      • conditions: string

        The filtering conditions specified as a SQL WHERE clause (e.g., "column1 > 10 AND column2 = 'value'").

      Returns this

      The table, so methods can be chained.

      // Keep only rows where the 'fruit' column is not 'apple'
      await table.filter(`fruit != 'apple'`).log();
      // Keep rows where 'price' is greater than 100 AND 'quantity' is greater than 0
      await table.filter(`price > 100 && quantity > 0`).log(); // Using JS syntax
      // Keep rows where 'category' is 'Electronics' OR 'Appliances'
      await table.filter(`category === 'Electronics' || category === 'Appliances'`).log(); // Using JS syntax
      // Keep rows where 'lastPurchaseDate' is on or after '2023-01-01'
      await table.filter(`lastPurchaseDate >= '2023-01-01'`).log();
    • Keeps rows in this table that have specific values in specified columns, removing all other rows.

      Parameters

      • columnsAndValues: { [key: string]: unknown }

        An object where keys are column names and values are the specific values (or an array of values) to keep in those columns. Use null to keep rows where a column is NULL.

      Returns this

      The table, so methods can be chained.

      // Keep only rows where 'job' is 'accountant' or 'developer', AND 'city' is 'Montreal'
      await table.keepValues({ job: ["accountant", "developer"], city: "Montreal" }).log();
      // Keep only rows where 'status' is 'active'
      await table.keepValues({ status: "active" }).log();
      // Keep only rows where 'status' is NULL
      await table.keepValues({ status: null }).log();
    • Removes duplicate rows from this table, keeping only unique rows. Note that the resulting data order might differ from the original.

      Parameters

      • options: { on?: string | string[] } = {}

        An optional object with configuration options:

        • Optionalon?: string | string[]

          A column name or an array of column names to consider when identifying duplicates. If specified, duplicates are determined based only on the values in these columns. If omitted, all columns are considered.

      Returns this

      The table, so methods can be chained.

      // Remove duplicate rows based on all columns
      await table.removeDuplicates().log();
      // Remove duplicate rows based only on the 'email' column
      await table.removeDuplicates({ on: "email" }).log();
      // Remove duplicate rows based on 'firstName' and 'lastName' columns
      await table.removeDuplicates({ on: ["firstName", "lastName"] }).log();
    • Removes rows with missing values from this table. By default, missing values include SQL NULL, as well as string representations like "NULL", "null", "NaN", "undefined", and empty strings "".

      Parameters

      • options: {
            columns?: string | string[];
            invert?: boolean;
            missingValues?: (string | number)[];
        } = {}

        An optional object with configuration options:

        • Optionalcolumns?: string | string[]

          A string or an array of strings specifying the columns to consider for missing values. If omitted, all columns are considered.

        • Optionalinvert?: boolean

          A boolean indicating whether to invert the condition. If true, only rows containing missing values will be kept. Defaults to false.

        • OptionalmissingValues?: (string | number)[]

          An array of values to be treated as missing values instead of the default ones. Defaults to ["undefined", "NaN", "null", "NULL", ""].

      Returns this

      The table, so methods can be chained.

      // Remove rows with missing values in any column
      await table.removeMissing().log();
      // Remove rows with missing values only in 'firstName' or 'lastName' columns
      await table.removeMissing({ columns: ["firstName", "lastName"] }).log();
      // Keep only rows with missing values in any column
      await table.removeMissing({ invert: true }).log();
      // Remove rows where 'age' is missing or is equal to -1
      await table.removeMissing({ columns: "age", missingValues: [-1] }).log();
    • Removes rows from this table based on SQL conditions. This method is similar to filter(), but removes rows instead of keeping them. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Parameters

      • conditions: string

        The filtering conditions specified as a SQL WHERE clause (e.g., "fruit = 'apple'").

      Returns this

      The table, so methods can be chained.

      // Remove rows where the 'fruit' column is 'apple'
      await table.removeRows(`fruit = 'apple'`).log();
      // Remove rows where 'quantity' is less than 5
      await table.removeRows(`quantity < 5`).log();
      // Remove rows where 'price' is less than 100 AND 'quantity' is 0
      await table.removeRows(`price < 100 && quantity === 0`).log(); // Using JS syntax
      // Remove rows where 'category' is 'Electronics' OR 'Appliances'
      await table.removeRows(`category === 'Electronics' || category === 'Appliances'`).log(); // Using JS syntax
    • Removes rows from this table that have specific values in specified columns.

      Parameters

      • columnsAndValues: { [key: string]: unknown }

        An object where keys are column names and values are the specific values (or an array of values) to remove from those columns. Use null to remove rows where a column is NULL; otherwise, NULL rows are retained.

      Returns this

      The table, so methods can be chained.

      // Remove rows where 'job' is 'accountant' or 'developer', AND 'city' is 'Montreal'
      await table.removeValues({ job: ["accountant", "developer"], city: "Montreal" }).log();
      // Remove rows where 'status' is 'inactive'
      await table.removeValues({ status: "inactive" }).log();
      // Remove rows where 'status' is NULL
      await table.removeValues({ status: null }).log();
    • Selects random rows from the table, removing all others. You can optionally specify a seed to ensure repeatable sampling.

      Parameters

      • count: string | number

        The number of rows to select (e.g., 100) or a percentage string (e.g., "10%") specifying the sampling size.

      • options: { seed?: number } = {}

        An optional object with configuration options:

        • Optionalseed?: number

          A number specifying the seed for repeatable sampling. Using the same seed will always yield the same random rows. Defaults to a random seed.

      Returns this

      The table, so methods can be chained.

      // Select 100 random rows from the table
      await table.sample(100).log();
      // Select 10% of the rows randomly
      await table.sample("10%").log();
      // Select random rows with a specific seed for repeatable results
      await table.sample("10%", { seed: 123 }).log();
    • Selects specific columns in the table, removing all others.

      Parameters

      • columns: string | string[]

        The name or an array of names of the columns to be selected.

      Returns this

      The table, so methods can be chained.

      // Select only the 'firstName' and 'lastName' columns, removing all other columns.
      await table.selectColumns(["firstName", "lastName"]).log();
      // Select only the 'productName' column.
      await table.selectColumns("productName").log();
    • Selects a specified number of rows from this table. An offset can be applied to skip initial rows, and the results can be output to a new table.

      Parameters

      • count: string | number

        The number of rows to select.

      • options: { offset?: number; outputTable?: string | boolean } = {}

        An optional object with configuration options:

        • Optionaloffset?: number

          The number of rows to skip from the beginning of the table before selecting. Defaults to 0.

        • OptionaloutputTable?: string | boolean

          If true, the selected rows will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be modified. Defaults to false.

      Returns this

      A table instance containing the selected rows (either the current table or a new table), so methods can be chained.

      // Select the first 100 rows of the current table
      await table.selectRows(100).log();
      // Select 100 rows after skipping the first 50 rows
      await table.selectRows(100, { offset: 50 }).log();
      // Select 50 rows and store them in a new table with a generated name
      const newTable = await table.selectRows(50, { outputTable: true }).log();
      // Select 75 rows and store them in a new table named "top_customers"
      const topCustomersTable = await table.selectRows(75, { outputTable: "top_customers" }).log();
    • Skips the first n rows of the table, effectively removing them.

      Parameters

      • count: number

        The number of rows to skip from the beginning of the table.

      Returns this

      The table, so methods can be chained.

      // Skip the first 10 rows of the table
      await table.skip(10).log();
    • Returns a new table with the same structure and data as this table. The data can be optionally filtered, limited to a specific number of rows, and offset. With the default SimpleDB.expressionSyntax: "js", conditions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      If conditions, limit, and offset are all used, they are applied in this order: conditions (WHERE clause) first, then offset, and finally limit (LIMIT).

      Note that cloning large tables can be a slow operation.

      Parameters

      • nameOrOptions:
            | string
            | {
                columns?: string
                | string[];
                conditions?: string;
                limit?: number;
                name?: string;
                offset?: number;
            } = {}

        Either a string specifying the name of the new table, or an optional object with configuration options. If not provided, a default name (e.g., "table1", "table2") will be generated.

        • string
        • {
              columns?: string | string[];
              conditions?: string;
              limit?: number;
              name?: string;
              offset?: number;
          }
          • Optionalcolumns?: string | string[]

            An array of column names to include in the cloned table. If not provided, all columns will be included.

          • Optionalconditions?: string

            A SQL WHERE clause condition to filter the data during cloning. Defaults to no condition (clones all rows).

          • Optionallimit?: number

            The number of rows to include in the cloned table. If provided, only the first X rows (potentially after filtering and offset) will be cloned.

          • Optionalname?: string

            The name of the new table to be created in the database. If not provided, a default name (e.g., "table1", "table2") will be generated.

          • Optionaloffset?: number

            The number of rows to skip before starting to clone rows.

      Returns this

      A new table instance containing the cloned data, so methods can be chained.

      // Clone tableA to a new table with a default generated name (e.g., "table1")
      const tableB = await tableA.clone().log();
      // Clone tableA to a new table named "my_cloned_table" using string parameter
      const tableB = await tableA.clone("my_cloned_table").log();
      // Clone tableA to a new table named "my_cloned_table" using options object
      const tableB = await tableA.clone({ name: "my_cloned_table" }).log();
      // Clone tableA, including only rows where 'column1' is greater than 10
      const tableB = await tableA.clone({ conditions: `column1 > 10` }).log();
      // Clone tableA with only specific columns
      const tableB = await tableA.clone({ columns: ["name", "age", "city"] }).log();
      // Clone only the first 10 rows of tableA
      const tableB = await tableA.clone({ limit: 10 }).log();
      // Clone 10 rows after skipping the first 5 rows
      const tableB = await tableA.clone({ limit: 10, offset: 5 }).log();
      // Clone tableA to a specific table name with filtered data, specific columns, and limited rows
      const tableB = await tableA.clone({
      name: "filtered_data",
      conditions: `status = 'active' AND created_date >= '2023-01-01'`,
      columns: ["name", "status", "created_date"],
      limit: 100
      }).log();
    • Removes the table from the database. After this operation, invoking methods on this SimpleTable instance will result in an error.

      Returns Promise<SimpleTable>

      A promise that resolves after the table is removed.

      // Remove the current table from the database
      await table.removeTable();
    • Renames the current table. The new name must not belong to another registered table, ignoring ASCII letter case.

      Parameters

      • name: string

        The new name for the table.

      Returns Promise<SimpleTable>

      A promise that resolves to the renamed table.

      // Rename the table to "new_employees"
      await table.renameTable("new_employees");
      await table.log();
    • Executes all queued methods across every table in the database, not just this table. Sync builder methods (like filter() or convert()) only queue their operation; execution happens when an async observer method (like getData(), log(), or writeData()) is awaited. Use run() when a chain ends in pure mutations with nothing to observe and you want the work done now.

      Because the whole database is flushed in program order, this behaves identically to SimpleDB.run(); call sdb.run() when your intent is to flush the database rather than this specific table.

      Returns Promise<SimpleTable>

      A promise that resolves to the table once the queued methods have been executed.

      // Nothing is observed after convert(), so run() executes the chain.
      await table
      .loadData("data.csv")
      .convert({ price: "number" })
      .run();
      await table.log();
    • Sets the data types for columns in a new table. If the table already exists, it will be replaced. To convert the types of an existing table, use the .convert() method instead.

      Parameters

      • types: {
            [key: string]:
                | "string"
                | "number"
                | "bigint"
                | "boolean"
                | "date"
                | "integer"
                | "float"
                | "time"
                | "datetime"
                | "datetimeTz"
                | "double"
                | "varchar"
                | "timestamp"
                | "timestamp with time zone"
                | "json"
                | "JSON"
                | `float[${number}]`
                | `FLOAT[${number}]`
                | `geometry('${string}')`
                | `GEOMETRY('${string}')`;
        }

        An object specifying the column names and their target data types (JavaScript or SQL types), including JSON, FLOAT[n] vectors, and GEOMETRY with a CRS.

      Returns this

      The table, so methods can be chained.

      // Set types for a new table
      await table.setTypes({
      name: "string",
      salary: "integer",
      raise: "float",
      }).log();
      // Create an empty table with JSON, embedding, and geometry columns.
      await table.setTypes({
      details: "JSON",
      embedding: "FLOAT[3]",
      geom: "GEOMETRY('EPSG:4326')",
      }).log();
    • Performs a cross join operation with another table. A cross join returns the Cartesian product of the rows from both tables, meaning all possible pairs of rows will be in the resulting table. This means that if the left table has n rows and the right table has m rows, the result will have n * m rows.

      Parameters

      • rightTable: SimpleTable

        The SimpleTable instance to cross join with.

      • options: { outputTable?: string | boolean } = {}

        An optional object with configuration options:

        • OptionaloutputTable?: string | boolean

          If true, the results will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be overwritten. Defaults to false.

      Returns this

      A table instance containing the cross-joined data (either the current table or a new table), so methods can be chained.

      // Perform a cross join with 'tableB', overwriting the current table (tableA)
      await tableA.crossJoin(tableB).log();
      // Perform a cross join with 'tableB' and store the results in a new table with a generated name
      const tableC = await tableA.crossJoin(tableB, { outputTable: true }).log();
      // Perform a cross join with 'tableB' and store the results in a new table named 'tableC'
      const tableC = await tableA.crossJoin(tableB, { outputTable: "tableC" }).log();
    • Performs a fuzzy left join between this table (considered the left table) and another table (the right table) based on string similarity between two text columns. Uses the rapidfuzz DuckDB community extension.

      If a similarity score column is added to the results, the rows will be ordered alphabetically by the left column, and then by descending similarity score within each group of identical left column values. Otherwise, the rows will be order alphabetically by the left column and then by the right column.

      This operation might create temporary files in a .tmp folder; consider adding .tmp to your .gitignore.

      Parameters

      • rightTable: SimpleTable

        The SimpleTable instance to be joined with this table.

      • leftColumn: string

        The name of the column in this (left) table containing the text to compare.

      • rightColumn: string

        The name of the column in the right table containing the text to compare.

      • threshold: number

        The minimum similarity score (0–100) required for two rows to be joined. For method: "ratio", a length-based pre-filter is automatically applied based on the threshold to improve performance without losing accuracy.

      • options: {
            method?:
                | "ratio"
                | "partial_ratio"
                | "token_sort_ratio"
                | "token_set_ratio";
            outputTable?: string
            | boolean;
            prefilterPrefixLength?: number;
            similarityColumn?: string;
        } = {}

        An optional object with configuration options:

        • Optionalmethod?: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio"

          The rapidfuzz similarity algorithm to use. Defaults to "ratio".

          • "ratio": Overall similarity (Levenshtein-based).
          • "partial_ratio": Best partial/substring similarity.
          • "token_sort_ratio": Similarity after sorting tokens (words), useful for reordered words.
          • "token_set_ratio": Similarity based on sets of tokens, ignoring duplicates and word order.
        • OptionaloutputTable?: string | boolean

          If true, the results will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be overwritten. Defaults to false.

        • OptionalprefilterPrefixLength?: number

          An optional prefix length. Only strings sharing the same first N characters are compared. Note that prefix filtering is lossy (e.g. "John" vs. "Phon" will not match despite high similarity).

        • OptionalsimilarityColumn?: string

          If provided, a column with this name is added to the result containing the similarity score (0–100). If omitted, the score is not included in the output.

      Returns this

      A table instance containing the fuzzy-joined data (either the current table or a new table), so methods can be chained.

      // Fuzzy left join tableA with tableB on 'name' (left) and 'standardName' (right) with a threshold of 80
      // A length-based pre-filter is automatically applied.
      await tableA.fuzzyJoin(tableB, "name", "standardName", 80).log();
      // Fuzzy join with a prefix-based pre-filter and a threshold of 80
      await tableA.fuzzyJoin(tableB, "name", "standardName", 80, {
      prefilterPrefixLength: 3, // Must share the same first 3 characters
      }).log();
      // Fuzzy join with a custom threshold and method, storing results in a new table
      const tableC = await tableA.fuzzyJoin(tableB, "name", "standardName", 90, {
      method: "token_sort_ratio",
      outputTable: "tableC",
      }).log();
      // Fuzzy join with a custom similarity column name and a threshold of 80
      await tableA.fuzzyJoin(tableB, "name", "standardName", 80, {
      similarityColumn: "matchScore",
      }).log();
    • Merges the data of this table (considered the left table) with another table (the right table) based on a common column or multiple columns. Note that the order of rows in the returned data is not guaranteed to be the same as in the original tables. This operation might create temporary files in a .tmp folder; consider adding .tmp to your .gitignore.

      Parameters

      • rightTable: SimpleTable

        The SimpleTable instance to be joined with this table.

      • options: {
            on?: string | string[];
            outputTable?: string | boolean;
            type?: "left" | "right" | "inner" | "full";
        } = {}

        An optional object with configuration options:

        • Optionalon?: string | string[]

          The column(s) to join on. If omitted, the method automatically searches for a column name that exists in both tables. Can be a single string or an array of strings for multiple join keys.

        • OptionaloutputTable?: string | boolean

          If true, the results will be stored in a new table with a generated name. If a string, it will be used as the name for the new table. If false or omitted, the current table will be overwritten. Defaults to false.

        • Optionaltype?: "left" | "right" | "inner" | "full"

          The type of join operation to perform. Possible values are "inner", "left" (default), "right", or "full".

      Returns this

      A table instance containing the joined data (either the current table or a new table), so methods can be chained.

      // Perform a left join with 'tableB' on a common column (auto-detected), overwriting tableA
      await tableA.join(tableB).log();
      // Perform an inner join with 'tableB' on the 'id' column, storing results in a new table named 'tableC'
      const tableC = await tableA.join(tableB, { on: "id", type: 'inner', outputTable: "tableC" }).log();
      // Perform a join on multiple columns ('name' and 'category')
      await tableA.join(tableB, { on: ["name", "category"] }).log();
    • Adds a new column containing the number of characters in each string in the specified column. Counts are based on Unicode code points, not grapheme clusters. A user-perceived character composed of multiple code points, such as some emoji or decomposed accented letters, counts as multiple characters. NULL input values produce NULL counts.

      Parameters

      • column: string

        The name of the column containing the strings to count.

      • newColumn: string

        The name of the new column where the character counts will be stored.

      Returns this

      The table, so methods can be chained.

      // Add a character count for each value in the 'name' column
      await table.addCharacterCount("name", "nameCharacterCount").log();
    • Adds a new column containing the word count for each string in the specified column. A word is any contiguous sequence of non-whitespace characters. Spaces, tabs, and line breaks separate words. Punctuation is not removed, so a standalone punctuation sequence counts as a word. Text without whitespace counts as one word, regardless of language. Empty or whitespace-only strings produce 0, and NULL input values produce NULL counts.

      Parameters

      • column: string

        The name of the column containing the strings to count.

      • newColumn: string

        The name of the new column where the word counts will be stored.

      Returns this

      The table, so methods can be chained.

      // Add a word count for each value in the 'article' column
      await table.addWordCount("article", "wordCount").log();
    • Normalizes string values in a column by:

      1. Stripping accents
      2. Optionally stripping punctuation (default: true)
      3. Converting to lowercase
      4. Normalizing whitespace (multiple spaces/tabs/newlines → single space)
      5. Trimming leading/trailing whitespace

      Produces identical output to journalism-format's normalizeString() function for all common cases including accented Latin characters.

      Parameters

      • column: string

        The column containing the text to normalize

      • newColumn: string

        The column to store the normalized results

      • options: { stripPunctuation?: boolean } = {}

        Configuration options

        • OptionalstripPunctuation?: boolean

          Strip punctuation and underscores (default: true)

      Returns this

      The table, so methods can be chained.

      // Normalize text column and store in new column
      await table.normalizeString("recipeName", "recipeNameNormalized").log();
      // "Épicerie Parisienne!" → "epicerie parisienne"
      // Keep punctuation for emails and URLs
      await table.normalizeString("email", "emailNormalized", { stripPunctuation: false }).log();
      // "User@Example.com" → "user@example.com"
      await table.normalizeString("url", "urlNormalized", { stripPunctuation: false }).log();
      // "https://Example.com/path" → "https://example.com/path"
    • Searches a text column using DuckDB's BM25 ranking function, which scores matches using factors including term frequency and document length.

      This method creates the required index with DuckDB's FTS extension. It reuses the table's existing FTS index unless overwriteIndex is true. DuckDB FTS indexes do not update automatically when the source table changes; use overwriteIndex: true to rebuild the index after modifying the table.

      Parameters

      • text: string

        The search query text to match against the text column.

      • idColumn: string

        The name of the column containing unique identifiers for each row.

      • textColumn: string

        The name of the column containing the text to search.

      • count: number

        The number of top-ranked results to return.

      • options: {
            b?: number;
            conjunctive?: boolean;
            ignore?: string;
            k?: number;
            lower?: boolean;
            minScore?: number;
            outputTable?: string;
            overwriteIndex?: boolean;
            scoreColumn?: string;
            stemmer?:
                | "arabic"
                | "basque"
                | "catalan"
                | "danish"
                | "dutch"
                | "english"
                | "finnish"
                | "french"
                | "german"
                | "greek"
                | "hindi"
                | "hungarian"
                | "indonesian"
                | "irish"
                | "italian"
                | "lithuanian"
                | "nepali"
                | "norwegian"
                | "porter"
                | "portuguese"
                | "romanian"
                | "russian"
                | "serbian"
                | "spanish"
                | "swedish"
                | "tamil"
                | "turkish"
                | "none";
            stopwords?: string;
            stripAccents?: boolean;
            verbose?: boolean;
        } = {}

        An optional object with configuration options:

        • Optionalb?: number

          The BM25 b parameter controlling document length normalization (0-1 range). Defaults to 0.75.

        • Optionalconjunctive?: boolean

          If true, all terms in the query string must be present in order for a document to be retrieved. Defaults to false.

        • Optionalignore?: string

          The regular expression of patterns to be ignored. Defaults to "(\.|[^a-z])+".

        • Optionalk?: number

          The BM25 k parameter controlling term frequency saturation. Defaults to 1.2.

        • Optionallower?: boolean

          A boolean indicating whether to convert all text to lowercase. Defaults to true.

        • OptionalminScore?: number

          A threshold to filter out results with a BM25 score below this value.

        • OptionaloutputTable?: string

          The name of a new table where the results will be stored. If not provided, the current table will be replaced with the search results.

        • OptionaloverwriteIndex?: boolean

          If true, drops and recreates the FTS index even if it already exists. Defaults to false.

        • OptionalscoreColumn?: string

          If provided, the BM25 score will be included in the output table under this column name.

        • Optionalstemmer?:
              | "arabic"
              | "basque"
              | "catalan"
              | "danish"
              | "dutch"
              | "english"
              | "finnish"
              | "french"
              | "german"
              | "greek"
              | "hindi"
              | "hungarian"
              | "indonesian"
              | "irish"
              | "italian"
              | "lithuanian"
              | "nepali"
              | "norwegian"
              | "porter"
              | "portuguese"
              | "romanian"
              | "russian"
              | "serbian"
              | "spanish"
              | "swedish"
              | "tamil"
              | "turkish"
              | "none"

          The language stemmer to apply for word normalization. Supports multiple languages or "none" to disable stemming. Defaults to 'porter'.

        • Optionalstopwords?: string

          The table containing the stopwords to use for the FTS index. Supports multiple languages or "none" to disable stopwords. Defaults to "english".

        • OptionalstripAccents?: boolean

          A boolean indicating whether to remove accents. Defaults to true.

        • Optionalverbose?: boolean

          If true, logs FTS index creation status. Defaults to false.

      Returns this

      A table instance containing the search results, ordered by relevance (best matches first), so methods can be chained.

      // Load a dataset of recipes
      const dishes = await table
      .loadData("recipes.parquet")
      .bm25("italian food", "Dish", "Recipe", 5)
      .log();
      // Logs the five most relevant dishes.
      // Search with a specific language stemmer
      await table.bm25("french food", "Dish", "Recipe", 5, {
      stemmer: "french",
      }).log();
      // Recreate the index with different settings and perform search
      await table.bm25("italian food", "Dish", "Recipe", 5, {
      stemmer: "english",
      overwriteIndex: true,
      }).log();
      // Save results to a new table without modifying the original
      const italianDishes = await table.bm25("italian food", "Dish", "Recipe", 5, {
      outputTable: "italian_results",
      }).log();

      // Original table remains unchanged
      const allDishes = await table.getValues("Dish");
      console.log(allDishes.length); // 336 (all dishes)

      // New table contains only search results
      const italianOnly = await italianDishes.getValues("Dish");
      console.log(italianOnly.length); // 5 (top results)
      // Multiple searches reuse the same index for better performance
      // The first search creates the index
      const italian = await table.bm25("italian food", "Dish", "Recipe", 5, {
      outputTable: "italian",
      }).log();

      // The second search reuses the existing index, so it's faster
      const french = await table.bm25("french food", "Dish", "Recipe", 5, {
      outputTable: "french",
      }).log();
      // Filter results by a minimum BM25 score and include the score in the output
      await table.bm25("spicy noodles", "Dish", "Recipe", 10, {
      minScore: 5.5,
      scoreColumn: "bm25_score",
      }).log();
      // Use the conjunctive option to require all terms
      await table.bm25("italian sauce", "Dish", "Recipe", 5, {
      conjunctive: true,
      }).log();
    • Creates a full-text search (FTS) index on a specified text column using DuckDB's FTS extension.

      If an FTS index already exists on the table, this method will skip creation and log a message (when verbose is enabled), unless the overwrite option is set to true. The index definition is recorded in indexes. The bm25 method requires an FTS index and creates one automatically when needed. DuckDB FTS indexes do not update automatically when the table changes; use overwrite: true to rebuild the index after modifying the table.

      Parameters

      • idColumn: string

        The column containing the document identifiers.

      • textColumn: string

        The column containing the text to search.

      • options: {
            ignore?: string;
            lower?: boolean;
            overwrite?: boolean;
            stemmer?:
                | "arabic"
                | "basque"
                | "catalan"
                | "danish"
                | "dutch"
                | "english"
                | "finnish"
                | "french"
                | "german"
                | "greek"
                | "hindi"
                | "hungarian"
                | "indonesian"
                | "irish"
                | "italian"
                | "lithuanian"
                | "nepali"
                | "norwegian"
                | "porter"
                | "portuguese"
                | "romanian"
                | "russian"
                | "serbian"
                | "spanish"
                | "swedish"
                | "tamil"
                | "turkish"
                | "none";
            stopwords?: string;
            stripAccents?: boolean;
            verbose?: boolean;
        } = {}

        An optional object with configuration options:

        • Optionalignore?: string

          The regular expression of patterns to be ignored. Defaults to "(\.|[^a-z])+".

        • Optionallower?: boolean

          A boolean indicating whether to convert all text to lowercase. Defaults to true.

        • Optionaloverwrite?: boolean

          A boolean indicating whether to overwrite the existing FTS index. Defaults to false.

        • Optionalstemmer?:
              | "arabic"
              | "basque"
              | "catalan"
              | "danish"
              | "dutch"
              | "english"
              | "finnish"
              | "french"
              | "german"
              | "greek"
              | "hindi"
              | "hungarian"
              | "indonesian"
              | "irish"
              | "italian"
              | "lithuanian"
              | "nepali"
              | "norwegian"
              | "porter"
              | "portuguese"
              | "romanian"
              | "russian"
              | "serbian"
              | "spanish"
              | "swedish"
              | "tamil"
              | "turkish"
              | "none"

          The stemmer to use for the FTS index. Supports multiple languages or "none" to disable stemming. Defaults to "porter".

        • Optionalstopwords?: string

          The table containing the stopwords to use for the FTS index. Supports multiple languages or "none" to disable stopwords. Defaults to "english".

        • OptionalstripAccents?: boolean

          A boolean indicating whether to remove accents. Defaults to true.

        • Optionalverbose?: boolean

          If true, logs FTS index creation status. Defaults to false.

      Returns this

      The table, so methods can be chained.

      // Load a dataset and create an FTS index for later searches
      await table
      .loadData("recipes.parquet")
      .createFtsIndex("Dish", "Recipe").log();
      // Create an index with a specific language stemmer
      await table.createFtsIndex("Dish", "Recipe", {
      stemmer: "french",
      }).log();
      // Recreate an existing index with different settings
      await table.createFtsIndex("Dish", "Recipe", {
      stemmer: "english",
      overwrite: true,
      }).log();
      // Create index with verbose logging
      await table.createFtsIndex("Dish", "Recipe", {
      verbose: true,
      }).log();
      // Logs: 'Creating FTS index on "Recipe" column...'
      // Logs: "FTS index created successfully."
    • Adds independently generated uniform random noise to numeric values in one or more columns. Each changed value receives an offset between -max and max. Selected integer and decimal columns become DOUBLE columns so fractional noise is retained. This method adds random jitter; it does not provide anonymization or differential privacy guarantees.

      Parameters

      • columns: string | string[]

        The numeric column name or array of numeric column names to which noise will be added. When multiple columns are provided, each value receives an independent random offset.

      • max: number

        The maximum absolute offset, expressed in each column's units. Must be a finite number greater than or equal to 0.

      • options: { onlyDuplicates?: boolean } = {}

        An optional object with configuration options:

        • OptionalonlyDuplicates?: boolean

          If true, adds noise only to values that occur more than once in their column. Each selected column is evaluated independently, and every occurrence of a duplicated value is changed. Defaults to false.

      Returns this

      The table, so methods can be chained.

      // Add an offset between -0.01 and 0.01 to every value
      await table.addNoise("measurement", 0.01).log();
      // Add noise independently to duplicated x values and duplicated y values
      await table.addNoise(["x", "y"], 0.1, {
      onlyDuplicates: true,
      }).log();
    • Capitalizes the first letter of each string in the specified columns and converts the rest of the string to lowercase.

      Parameters

      • columns: string | string[]

        The column name or an array of column names to be capitalized.

      Returns this

      The table, so methods can be chained.

      // Capitalize strings in 'column1' (e.g., "hello world" becomes "Hello world")
      await table.capitalize("column1").log();
      // Capitalize strings in 'column1' and 'column2'
      await table.capitalize(["column1", "column2"]).log();
    • Concatenates values from specified columns into a new column.

      Parameters

      • columns: string[]

        An array of column names whose values will be concatenated.

      • newColumn: string

        The name of the new column to store the concatenated values.

      • options: { separator?: string } = {}

        An optional object with configuration options:

        • Optionalseparator?: string

          The string used to separate concatenated values. Defaults to an empty string ("").

      Returns this

      The table, so methods can be chained.

      // Concatenate 'firstName' and 'lastName' into a new 'fullName' column
      await table.concatenate(["firstName", "lastName"], "fullName").log();
      // Concatenate 'city' and 'country' into 'location', separated by a comma and space
      await table.concatenate(["city", "country"], "location", { separator: ", " }).log();
    • Converts data types of specified columns to target types (JavaScript or SQL types).

      When converting non-standard timestamp, date, or time strings, provide a datetimeFormat option using DuckDB's format specifiers. Strings converted to datetimeTz or timestamp with time zone use an explicit Z or numeric offset when present; strings without an offset are interpreted as UTC. Returned TIMESTAMP WITH TIME ZONE values are rendered as UTC strings.

      When converting timestamps, dates, or times to/from numbers, the numerical representation will be in milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC).

      When converting strings to numbers, commas (often used as thousand separators) will be automatically removed before conversion.

      Parameters

      • types: {
            [key: string]:
                | "string"
                | "number"
                | "bigint"
                | "boolean"
                | "date"
                | "integer"
                | "float"
                | "time"
                | "datetime"
                | "datetimeTz"
                | "double"
                | "varchar"
                | "timestamp"
                | "timestamp with time zone"
                | "json"
                | "JSON"
                | `float[${number}]`
                | `FLOAT[${number}]`
                | `geometry('${string}')`
                | `GEOMETRY('${string}')`;
        }

        An object mapping column names to their target data types for conversion, including JSON, FLOAT[n] vectors, and GEOMETRY with a CRS.

      • options: { datetimeFormat?: string; strict?: boolean } = {}

        An optional object with configuration options:

        • OptionaldatetimeFormat?: string

          A string specifying the format for date and time conversions. Uses strftime and strptime functions from DuckDB. For format specifiers, see DuckDB's documentation.

        • Optionalstrict?: boolean

          If false, values that cannot be converted will be replaced by NULL instead of throwing an error. Defaults to true.

      Returns this

      The table, so methods can be chained.

      // Convert 'column1' to string and 'column2' to integer (JavaScript types)
      await table.convert({ column1: "string", column2: "integer" }).log();
      // Convert 'column1' to VARCHAR and 'column2' to BIGINT (SQL types)
      await table.convert({ column1: "varchar", column2: "bigint" }).log();
      // Convert strings in 'column3' to datetime using a specific format
      await table.convert({ column3: "datetime" }, { datetimeFormat: "%Y-%m-%d" }).log();
      // Both values identify instants and are rendered in UTC.
      await table
      .loadArray([
      { observedAt: "2024-04-07T13:00:00-04:00" },
      { observedAt: "2024-04-07T17:00:00Z" },
      ])
      .convert({ observedAt: "datetimeTz" }).log();
      // Convert datetime values in 'column3' to strings using a specific format
      await table.convert({ column3: "string" }, { datetimeFormat: "%Y-%m-%d %H:%M:%S" }).log();
      // Convert 'amount' to float, replacing unconvertible values with NULL
      await table.convert({ amount: "float" }, { strict: false }).log();
      // Parse JSON, a fixed-size float vector, and WKT geometry from text.
      await table.loadArray([{
      details: '{"count":3}',
      embedding: "[0.25,0.5,0.75]",
      geom: "POINT (-73 45)",
      }])
      .convert({ details: "JSON", embedding: "FLOAT[3]", geom: "GEOMETRY('EPSG:4326')" })
      .log();
    • Fills NULL values in specified columns. By default, each NULL is replaced with the last non-NULL value from the preceding row. When interpolate is true, NULL values are replaced using linear interpolation (or extrapolation at the ends). Pass interpolateBy with a real numeric or date column name to use it as the X-axis, so that interpolated values are proportional to the actual distances between X-axis values rather than treating every row as equidistant. When interpolateBy is set, interpolate is automatically assumed true.

      Parameters

      • columns: string | string[]

        The column(s) for which to fill NULL values.

      • options: { by?: string | string[]; interpolate?: boolean; interpolateBy?: string } = {}

        An optional object with configuration options:

        • Optionalby?: string | string[]

          A column name or an array of column names to partition by. The fill is applied independently within each group.

        • Optionalinterpolate?: boolean

          If true, replaces NULL values with linearly interpolated values using DuckDB's fill() window function. When interpolateBy is not set, row positions are used as the X-axis, treating rows as equidistant. For NULL values at the ends, linear extrapolation is used. Both the column values and the X-axis values must support arithmetic. If false or omitted, the previous non-NULL value is used instead. Automatically assumed true when interpolateBy is set.

        • OptionalinterpolateBy?: string

          A column name to use as the X-axis for interpolation instead of equidistant row positions. When provided, interpolate is automatically assumed true. Use this when rows are not evenly spaced (e.g., timestamps or non-uniform numeric indices) so that interpolated values are proportional to the actual distance between X-axis values.

      Returns this

      The table, so methods can be chained.

      // Fill NULL values in 'column1' with the previous non-NULL value
      await table.fill("column1").log();
      // Fill NULL values in multiple columns
      await table.fill(["columnA", "columnB"]).log();
      // Fill NULL values in 'value' independently within each 'group'
      await table.fill("value", { by: "group" }).log();
      // Fill NULL values in 'value' using linear interpolation
      await table.fill("value", { interpolate: true }).log();
      // Fill NULL values in 'value' using linear interpolation, independently within each 'group'
      await table.fill("value", { by: "group", interpolate: true }).log();
      // Fill NULL values in 'value' using linear interpolation proportional to 'x' distances
      await table.fill("value", { interpolate: true, interpolateBy: "x" }).log();
      // interpolateBy implies interpolate: true, so this is equivalent to the previous example
      await table.fill("value", { interpolateBy: "x" }).log();
    • Extracts a specific number of characters from the beginning (left side) of string values in the specified column.

      Parameters

      • column: string

        The name of the column containing the strings to be modified.

      • count: number

        The number of characters to extract from the left side of each string.

      Returns this

      The table, so methods can be chained.

      // Replace strings in 'productCode' with their first two characters
      // e.g., "ABC-123" becomes "AB"
      await table.firstChars("productCode", 2).log();
    • Normalizes string values in a column by detecting fuzzy duplicates and replacing them with a single canonical value.

      Similar strings are grouped into clusters. Matching is transitive: if "New York" is similar to "New Yorke" and "New Yorke" is similar to "New Yorkk", all three land in the same cluster even if "New York" and "New Yorkk" would not match directly. Each cluster is then collapsed to one representative value based on the strategy option.

      Similarity is computed using the rapidfuzz DuckDB community extension, which is installed and loaded automatically.

      Parameters

      • column: string

        The name of the column containing the strings to normalize.

      • newColumn: string

        The name of the column to write the normalized values to. Use the same name as column to normalize in-place.

      • threshold: number

        The minimum similarity score (0–100) for two strings to be considered duplicates. For method: "ratio", a length-based pre-filter is automatically applied based on the threshold to improve performance without losing accuracy.

      • options: {
            method?:
                | "ratio"
                | "partial_ratio"
                | "token_sort_ratio"
                | "token_set_ratio";
            prefilterPrefixLength?: number;
            strategy?: | "mostCommon"
            | "longestString"
            | "shortestString"
            | "mostCentral"
            | "maxScore";
        } = {}

        An optional object with configuration options:

        • Optionalmethod?: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio"

          The rapidfuzz similarity algorithm to use. Defaults to "ratio".

          • "ratio": Overall similarity.
          • "partial_ratio": Best partial/substring similarity.
          • "token_sort_ratio": Similarity after sorting tokens (words), useful for reordered words.
          • "token_set_ratio": Similarity based on sets of tokens, ignoring duplicates and word order.
        • OptionalprefilterPrefixLength?: number

          An optional prefix length. Only strings sharing the same first N characters are compared. Note that prefix filtering is lossy (e.g. "John" vs. "Phon" will not match despite high similarity).

        • Optionalstrategy?: "mostCommon" | "longestString" | "shortestString" | "mostCentral" | "maxScore"

          The strategy for choosing the canonical value within each cluster of similar strings. Defaults to "mostCommon".

          • "mostCommon": Keep the value that appears most frequently in the original column.
          • "longestString": Keep the longest string in the cluster.
          • "shortestString": Keep the shortest string in the cluster.
          • "mostCentral": Keep the string with the highest total similarity score to all other cluster members (the most "central" string).
          • "maxScore": Keep the string that participates in the single highest-scoring pairwise match within the cluster.

      Returns this

      The table, so methods can be chained.

      // Normalize 'city' into a new 'cityClean' column, keeping the most common string per cluster with a threshold of 80
      // A length-based pre-filter is automatically applied.
      await table.fuzzyClean("city", "cityClean", 80).log();
      // Normalize with a prefix-based pre-filter and a threshold of 80
      await table.fuzzyClean("city", "cityClean", 80, {
      prefilterPrefixLength: 5, // Must share the same first 5 characters
      }).log();
      // Normalize 'companyName' into a new column using token_sort_ratio and a threshold of 90
      await table.fuzzyClean("companyName", "companyNameClean", 90, { method: "token_sort_ratio" }).log();
      // Normalize 'category' in-place, keeping the longest string in each cluster and a threshold of 80
      await table.fuzzyClean("category", "category", 80, { strategy: "longestString" }).log();
    • Extracts a specific number of characters from the end (right side) of string values in the specified column.

      Parameters

      • column: string

        The name of the column containing the strings to be modified.

      • count: number

        The number of characters to extract from the right side of each string.

      Returns this

      The table, so methods can be chained.

      // Replace strings in 'productCode' with their last two characters
      // e.g., "ABC-123" becomes "23"
      await table.lastChars("productCode", 2).log();
    • Converts string values in the specified columns to lowercase.

      Parameters

      • columns: string | string[]

        The column name or an array of column names to be converted to lowercase.

      Returns this

      The table, so methods can be chained.

      // Convert strings in 'column1' to lowercase
      await table.lower("column1").log();
      // Convert strings in 'column1' and 'column2' to lowercase
      await table.lower(["column1", "column2"]).log();
    • Nests (collapses) rows by aggregating a column's values into a single string per group, separated by a delimiter.

      This is the inverse operation of unnest(). Multiple rows are combined into fewer rows by grouping on specified category columns and concatenating the target column values with a separator.

      Parameters

      • column: string

        The name of the column whose values will be aggregated and concatenated.

      • separator: string

        The delimiter string used to join the column values.

      • by: string | string[]

        The column name or an array of column names to group by.

      Returns this

      The table, so methods can be chained.

      // Nest 'neighborhoods' column separated by " / " for each city
      // Before: [{ city: "Montreal", neighborhoods: "Old Montreal" },
      // { city: "Montreal", neighborhoods: "Chinatown" },
      // { city: "Montreal", neighborhoods: "Griffintown" }]
      // After: [{ city: "Montreal", neighborhoods: "Old Montreal / Chinatown / Griffintown" }]
      await table.nest("neighborhoods", " / ", "city").log();
      // Nest with multiple category columns
      // Before: [{ country: "Canada", city: "Montreal", tags: "red" },
      // { country: "Canada", city: "Montreal", tags: "blue" }]
      // After: [{ country: "Canada", city: "Montreal", tags: "red,blue" }]
      await table.nest("tags", ",", ["country", "city"]).log();
    • Pads the strings in the specified columns to a target length.

      The columns must contain string (VARCHAR) values. An error is thrown if any column is of a different type. null values remain null. If any string already exceeds the target length, an error is thrown (no silent truncation).

      Parameters

      • columns: string | string[]

        The column name(s) containing strings to be padded.

      • length: number

        The target length of the padded strings.

      • options: { character?: string; side?: "left" | "right" } = {}

        An optional object with configuration options:

        • Optionalcharacter?: string

          The character to use for padding. Defaults to '0'.

        • Optionalside?: "left" | "right"

          Which side to pad. 'left' (default) or 'right'.

      Returns this

      The table, so methods can be chained.

      // Left-pad 'id' column to 3 characters with zeros (default)
      await table.pad("id", 3).log();
      // Result: '1' -> '001', '23' -> '023', null -> null
      // Right-pad 'code' column to 5 characters with spaces
      await table.pad("code", 5, { side: "right", character: " " }).log();
      // Result: '123' -> '123 ', '45' -> '45 ', null -> null
      // Left-pad multiple columns to 5 characters with dashes
      await table.pad(["id", "code"], 5, { side: "left", character: "-" }).log();
      // Result: '1' -> '----1', '23' -> '---23'

      If any column is not of string (VARCHAR) type.

      If any string value exceeds the target length.

    • Repeats rows based on the values in a column.

      If a row has a value of 3 in the specified column, it will be repeated 3 times. If the value is 0 or negative, the row will be removed.

      Parameters

      • column: string

        The name of the column containing the number of times each row should be repeated.

      • options: { index?: string } = {}

        An optional object with configuration options:

        • Optionalindex?: string

          The name of a new column to store the index of the repeated row (starting at 0).

      Returns this

      The table, so methods can be chained.

      // Before: [{ id: 1, count: 2, category: "A" }, { id: 2, count: 3, category: "B" }]
      await table.repeatRows("count").log();
      // After: [{ id: 1, count: 2, category: "A" }, { id: 1, count: 2, category: "A" },
      // { id: 2, count: 3, category: "B" }, { id: 2, count: 3, category: "B" }, { id: 2, count: 3, category: "B" }]
      // With an index column
      await table.repeatRows("count", { index: "copyId" }).log();
      // After: [{ id: 1, count: 2, category: "A", copyId: 0 }, { id: 1, count: 2, category: "A", copyId: 1 },
      // { id: 2, count: 3, category: "B", copyId: 0 }, { id: 2, count: 3, category: "B", copyId: 1 }, { id: 2, count: 3, category: "B", copyId: 2 }]
    • Replaces specified strings in the selected columns. Use ["all"] to target a column literally named all.

      Parameters

      • columns: string | string[]

        The column name, an array of column names, or "all" to apply the replacement to every column in the table.

      • replacements: { [key: string]: string }

        An object mapping old strings to new strings (e.g., { "oldValue": "newValue" }).

      • options: { entireString?: boolean; regex?: boolean } = {}

        An optional object with configuration options:

        • OptionalentireString?: boolean

          A boolean indicating whether the entire cell content must match the oldString for replacement to occur. Defaults to false (replaces substrings).

        • Optionalregex?: boolean

          A boolean indicating whether the oldString should be treated as a regular expression for global replacement. Cannot be used with entireString: true. Defaults to false.

      Returns this

      The table, so methods can be chained.

      // Replace all occurrences of "kilograms" with "kg" in 'column1'
      await table.replace("column1", { "kilograms": "kg" }).log();
      // Replace "kilograms" with "kg" and "liters" with "l" in 'column1' and 'column2'
      await table.replace(["column1", "column2"], { "kilograms": "kg", "liters": "l" }).log();
      // Replace only if the entire string in 'column1' is "kilograms"
      await table.replace("column1", { "kilograms": "kg" }, { entireString: true }).log();
      // Replace any sequence of one or more digits with a hyphen in 'column1' using regex
      await table.replace("column1", { "\d+": "-" }, { regex: true }).log();
      // Replace "%" with "" in all columns
      await table.replace("all", { "%": "" }).log();
    • Replaces NULL values in the specified columns with a given value. Use ["all"] to target a column literally named all.

      Parameters

      • columns: string | string[]

        The column name, an array of column names, or "all" to apply the replacement to every column in the table.

      • value: unknown

        The value to replace NULL occurrences with.

      Returns this

      The table, so methods can be chained.

      // Replace NULL values in 'column1' with 0
      await table.replaceNulls("column1", 0).log();
      // Replace NULL values in 'columnA' and 'columnB' with the string "N/A"
      await table.replaceNulls(["columnA", "columnB"], "N/A").log();
      // Replace NULL values in 'dateColumn' with a specific date
      await table.replaceNulls("dateColumn", new Date("2023-01-01")).log();
      // Replace NULL values in all columns with 0
      await table.replaceNulls("all", 0).log();
    • Rounds numeric values in specified columns.

      Parameters

      • columns: string | string[]

        The column name or an array of column names containing numeric values to be rounded.

      • options: number | { decimals?: number; method?: "round" | "ceiling" | "floor" } = {}

        An optional integer specifying the number of decimal places, or an object with configuration options:

        • number
        • { decimals?: number; method?: "round" | "ceiling" | "floor" }
          • Optionaldecimals?: number

            The number of decimal places to round to. Defaults to 0 (rounds to the nearest integer).

          • Optionalmethod?: "round" | "ceiling" | "floor"

            The rounding method to use: "round" (rounds to the nearest integer, with halves rounding up), "ceiling" (rounds up to the nearest integer), or "floor" (rounds down to the nearest integer). Defaults to "round".

      Returns this

      The table, so methods can be chained.

      // Round 'column1' values to the nearest integer
      await table.round("column1").log();
      // Round 'column1' values to 2 decimal places
      await table.round("column1", { decimals: 2 }).log();
      // Round 'column1' values down to the nearest integer (floor)
      await table.round("column1", { method: "floor" }).log();
      // Round 'columnA' and 'columnB' values to 1 decimal place using ceiling method
      await table.round(["columnA", "columnB"], { decimals: 1, method: "ceiling" }).log();
      // Round 'column1' values to 2 decimal places using the shorthand
      await table.round("column1", 2).log();
    • Concatenates values from multiple columns into a new column with labeled rows.

      This method creates a new column where each value is a concatenation of the specified columns, with each column value prefixed by its column name and a colon, followed by a newline. Column entries are separated by double newlines ("\n\n").

      All values must be string, otherwise an error will be thrown. Use the convert() method first to convert non-string columns to string.

      If a column value is NULL, it will be replaced by 'Unknown' in the concatenated result.

      Parameters

      • columns: string[]

        An array of column names whose values will be concatenated with labels.

      • newColumn: string

        The name of the new column to create with the concatenated values.

      Returns this

      The table, so methods can be chained.

      // Concatenate multiple string columns into a labeled text field
      await table.rowToText(
      ["summary", "findings", "context", "date", "quote"],
      "fullText"
      ).log();
      // Result in "fullText" will look like:
      // summary:
      // [value]
      //
      // findings:
      // [value]
      //
      // context:
      // [value]
      //
      // date:
      // [value]
      //
      // quote:
      // [value]
      // Convert numeric columns to strings first, then concatenate
      // NULL values will appear as 'Unknown'
      await table
      .convert({ age: "string", salary: "string" })
      .rowToText(["name", "age", "salary"], "profile").log();
    • Combines numeric scalar columns into a fixed-size vector. The input column order determines the vector dimension order, and null values remain null vector elements.

      When all input columns have the same numeric type, that type is preserved. Mixed numeric types require an explicit type option. Casting exact decimals or large integers to FLOAT or DOUBLE can lose precision.

      Parameters

      • columns: string[]

        Numeric scalar columns to combine, in vector dimension order.

      • newColumn: string

        The name of the vector column to create.

      • options: { type?: "float" | "double" } = {}

        Optional vector element type settings.

        • Optionaltype?: "float" | "double"

          Cast every element to "float" or "double". Required when the input column types differ.

      Returns this

      The table, so methods can be chained.

      await table
      .rowToVector(["height", "weight"], "measurements")
      .log();
      await table
      .rowToVector(["count", "score"], "features", { type: "double" })
      .log();
    • Splits strings in a specified column by a separator and extracts a substring at a given index, storing the result in a new or existing column. If the index is out of bounds, an empty string will be returned for that row.

      Parameters

      • column: string

        The name of the column containing the strings to be split.

      • separator: string

        The substring to use as a delimiter for splitting the strings.

      • index: number

        The zero-based index of the substring to extract after splitting. For example, 0 for the first part, 1 for the second, etc.

      • newColumn: string

        The name of the column where the extracted substrings will be stored. To overwrite the original column, use the same name as column.

      Returns this

      The table, so methods can be chained.

      // Split 'address' by comma and extract the second part (index 1) into a new 'city' column
      // e.g., "123 Main St, Anytown, USA" -> "Anytown"
      await table.splitExtract("address", ",", 1, "city").log();
      // Split 'filename' by dot and extract the first part (index 0), overwriting 'filename'
      // e.g., "document.pdf" -> "document"
      await table.splitExtract("filename", ".", 0, "filename").log();
    • Splits strings in a specified column by a separator and spreads the resulting parts into multiple new columns.

      Each part of the split string will be stored in a separate column. The number of columns created is determined by the length of the newColumns array. If a row has fewer parts than the number of new columns, a warning will be logged and the extra columns will contain empty strings (unless strict is set to false). If a row has more parts than the number of new columns, an error will be thrown unless strict is set to false.

      Parameters

      • column: string

        The name of the column containing the strings to be split.

      • separator: string

        The substring to use as a delimiter for splitting the strings.

      • newColumns: string[]

        An array of column names for the extracted parts.

      • options: { strict?: boolean } = {}

        Optional configuration.

        • Optionalstrict?: boolean

          If false, skips all validation checks (both max and min parts). Defaults to true.

      Returns this

      The table, so methods can be chained.

      // Split 'fullName' by comma and spread into 'lastName' and 'firstName'
      // e.g., "Shiab, Nael" -> lastName: "Shiab", firstName: "Nael"
      await table.splitSpread("fullName", ",", ["lastName", "firstName"]).log();
      // Split 'address' by comma and spread into three columns
      // e.g., "123 Main St, Anytown, USA" -> street: "123 Main St", city: "Anytown", country: "USA"
      await table.splitSpread("address", ",", ["street", "city", "country"]).log();
      // Skip validation for performance
      await table.splitSpread("data", "|", ["col1", "col2"], { strict: false }).log();
    • Trims specified characters from the beginning, end, or both sides of string values in the given columns. Pass "all" to trim every string (VARCHAR) column, leaving other types unchanged. If there are no string columns, "all" leaves the table unchanged. Use ["all"] to trim a column literally named all.

      Parameters

      • columns: string | string[]

        The column name, an array of column names, or "all" to trim every string (VARCHAR) column.

      • options: { character?: string; side?: "left" | "right" | "both" } = {}

        An optional object with configuration options:

        • Optionalcharacter?: string

          The string to trim. Defaults to whitespace characters.

        • Optionalside?: "left" | "right" | "both"

          The side to trim: "left" (removes from the beginning), "right" (removes from the end), or "both" (removes from both sides). Defaults to "both".

      Returns this

      The table, so methods can be chained.

      // Trim whitespace from 'column1'
      await table.trim("column1").log();
      // Trim whitespace from every string column
      await table.trim("all").log();
      // Trim leading and trailing asterisks from 'productCode'
      await table.trim("productCode", { character: "*" }).log();
      // Right-trim whitespace from 'description' and 'notes' columns
      await table.trim(["description", "notes"], { side: "right" }).log();
    • Truncates string values in a specified column to a maximum number of characters.

      Parameters

      • column: string

        The column name containing strings to be truncated.

      • length: number

        The maximum number of characters to keep.

      Returns this

      The table, so methods can be chained.

      // Truncate strings in 'description' column to 50 characters
      await table.truncate("description", 50).log();
      // Truncate strings in 'name' column to 10 characters
      await table.truncate("name", 10).log();
    • Unnests (expands) rows by splitting a column's string values into multiple rows based on a separator.

      Each value in the specified column is split using the provided separator, and a new row is created for each resulting substring. All other column values are duplicated across the newly created rows.

      Parameters

      • column: string

        The name of the column containing string values to be split and unnested.

      • separator: string

        The delimiter string used to split the column values.

      Returns this

      The table, so methods can be chained.

      // Unnest 'tags' column separated by commas
      // Before: [{ id: 1, tags: "red,blue,green" }]
      // After: [{ id: 1, tags: "red" }, { id: 1, tags: "blue" }, { id: 1, tags: "green" }]
      await table.unnest("tags", ",").log();
      // Unnest 'neighborhoods' column separated by " / "
      // Before: [{ city: "Montreal", neighborhoods: "Old Montreal / Chinatown / Griffintown" }]
      // After: [{ city: "Montreal", neighborhoods: "Old Montreal" },
      // { city: "Montreal", neighborhoods: "Chinatown" },
      // { city: "Montreal", neighborhoods: "Griffintown" }]
      await table.unnest("neighborhoods", " / ").log();
    • Updates values in a specified column using a SQL expression. With the default SimpleDB.expressionSyntax: "js", expressions support JavaScript-style operators (&&, ||, ===, !==). Set expressionSyntax: "sql" for unchanged SQL.

      Parameters

      • column: string

        The name of the column to be updated.

      • definition: string

        The SQL expression used to set the new values in the column (e.g., "column1 * 2", "UPPER(column_name)").

      Returns this

      The table, so methods can be chained.

      // Update 'column1' with the left 5 characters of 'column2'
      await table.updateColumn("column1", `LEFT(column2, 5)`).log();
      // Double the values in 'price' column
      await table.updateColumn("price", `price * 2`).log();
      // Set 'status' to 'active' where 'isActive' is true
      await table.updateColumn("status", `CASE WHEN isActive THEN 'active' ELSE 'inactive' END`).log();
      // Update an existing column with a value from a JSON object using dot notation.
      await table.loadArray([{ count: 0, details: { count: 3 } }], {
      columnTypes: { details: "JSON" },
      })
      .updateColumn("count", "details.count")
      .log();
    • Updates data in the table using a JavaScript function. The function receives the existing rows as an array of objects and must return the modified rows as an array of objects. This method offers high flexibility for data manipulation but can be slow for large tables as it involves transferring data between DuckDB and JavaScript. Before writing a JavaScript callback, check for an existing SDA method that performs the same operation; it will usually be faster and more efficient. Existing integer and enum columns retain their types. Fractional or out-of-range integers and unknown enum members throw when written. The original table is replaced after all callback results have been converted successfully.

      If the table has geometry columns, the callback can read and modify their GeoJSON geometry objects directly. Extra properties added to these objects are ignored; create new columns on the returned rows to store additional attributes. All geometry columns must use EPSG:4326; use reproject() first if needed.

      Parameters

      • dataModifier:
            | (
                (
                    rows: { [key: string]: unknown }[],
                ) => Promise<{ [key: string]: unknown }[]>
            )
            | ((rows: { [key: string]: unknown }[]) => { [key: string]: unknown }[])

        A synchronous or asynchronous function that takes the existing rows (as an array of objects) and returns the modified rows (as an array of objects).

      • options: { batchSize?: number; columnTypes?: { [key: string]: string } } = {}

        An optional object with configuration options:

        • OptionalbatchSize?: number

          If provided, rows are processed in batches of this size instead of all at once, so large tables don't have to be materialized entirely in memory. This limits input rows per batch, but does not eliminate conversion work or bound geometry size or callback output expansion. The modifier function is called once per batch. Tables with a column named rowid or __sda_rowid (case-insensitive) are rejected before the modifier runs; rename the column or omit batchSize.

        • OptionalcolumnTypes?: { [key: string]: string }

          Explicit types for newly added columns, following loadArray's columnTypes contract. Declare geometry as GEOMETRY('EPSG:4326'); geometry is never inferred from object shape. Existing columns cannot be redeclared.

      Returns this

      The table, so methods can be chained.

      // Double the value in each row with a JavaScript callback.
      await table.loadArray([{ value: 1 }, { value: 2 }])
      .updateWithJS((rows) => rows.map((row) => ({
      value: Number(row.value) * 2,
      })))
      .log();
      // Process two rows per callback to limit how many rows are handled at once.
      await table.loadArray([{ value: 1 }, { value: 2 }, { value: 3 }])
      .updateWithJS((rows) => rows.map((row) => ({
      value: Number(row.value) * 2,
      })), { batchSize: 2 })
      .log();
      // Add a JSON object column to keep related attributes together.
      await table.loadArray([{ name: "Station" }])
      .updateWithJS((rows) => rows.map((row) => ({
      ...row, details: { active: true, category: "weather" },
      })), { columnTypes: { details: "JSON" } })
      .log();
      // Add a three-element float vector, such as an embedding.
      await table.loadArray([{ name: "Station" }])
      .updateWithJS((rows) => rows.map((row) => ({
      ...row, embedding: [0.25, 0.5, 0.75],
      })), { columnTypes: { embedding: "FLOAT[3]" } })
      .log();
      // Move each point slightly east by editing its longitude.
      await sdb.newTable().loadArray([{
      geom: { type: "Point", coordinates: [-73, 45] },
      }], { columnTypes: { geom: "GEOMETRY('EPSG:4326')" } })
      .updateWithJS((rows) => rows.map((row) => {
      // This example loads only Points. Narrow the type for coordinate access.
      const geom = row.geom as { type: "Point"; coordinates: number[] } | null;
      if (geom !== null) geom.coordinates[0] += 0.01;
      return row;
      })).log();
      // Create a geometry column from longitude and latitude values.
      await sdb.newTable().loadArray([{ longitude: -73, latitude: 45 }])
      .updateWithJS((rows) => rows.map((row) => ({
      ...row, geom: { type: "Point", coordinates: [row.longitude, row.latitude] },
      })), { columnTypes: { geom: "GEOMETRY('EPSG:4326')" } }).log();
    • Converts string values in the specified columns to uppercase.

      Parameters

      • columns: string | string[]

        The column name or an array of column names to be converted to uppercase.

      Returns this

      The table, so methods can be chained.

      // Convert strings in 'column1' to uppercase
      await table.upper("column1").log();
      // Convert strings in 'column1' and 'column2' to uppercase
      await table.upper(["column1", "column2"]).log();
    • Normalizes a numeric vector column. By default, each dimension is scaled independently to [0, 1] across rows: all first elements together, all second elements together, and so on. With normalization: "rowL2", each row is scaled to Euclidean unit length while preserving its direction; zero vectors remain zero.

      The input may be a numeric LIST or fixed-size ARRAY. The output is a fixed-size DOUBLE ARRAY. Converting large integers and exact decimals to DOUBLE can lose precision. With "dimensionMinMax", a dimension whose converted values are all equal cannot be normalized.

      Parameters

      • column: string

        The numeric vector column to normalize.

      • newColumn: string

        The output column. Use the source column's name to replace the source column with the normalized DOUBLE vector.

      • options: { normalization?: "dimensionMinMax" | "rowL2" } = {}

        Normalization settings.

        • Optionalnormalization?: "dimensionMinMax" | "rowL2"

          "dimensionMinMax" scales each dimension across rows; "rowL2" scales each row to unit length. Defaults to "dimensionMinMax".

      Returns this

      The table, so methods can be chained.

      // Scale each dimension across rows to [0, 1] in a new column.
      await table
      .normalizeVector("features", "scaledFeatures")
      .log();
      // Replace each vector with its unit-length version; zero vectors stay zero.
      await table
      .normalizeVector("features", "features", { normalization: "rowL2" })
      .log();
    • Creates a vector similarity search (VSS) index on a specified column using DuckDB's VSS extension.

      If a VSS index already exists on the table, this method will skip creation and log a message (when verbose is enabled), unless the overwrite option is set to true. The index definition is recorded in indexes.

      Parameters

      • column: string

        The name of the column containing vector embeddings (must be FLOAT array type).

      • options: {
            efConstruction?: number;
            efSearch?: number;
            M?: number;
            overwrite?: boolean;
            verbose?: boolean;
        } = {}

        An optional object with configuration options:

        • OptionalefConstruction?: number

          The number of candidate vertices to consider during index construction. Higher values result in more accurate indexes but increase build time. Defaults to 128.

        • OptionalefSearch?: number

          The number of candidate vertices to consider during search. Higher values result in more accurate searches but increase search time. Defaults to 64.

        • OptionalM?: number

          The maximum number of neighbors to keep for each vertex in the graph. Higher values result in more accurate indexes but increase build time and memory usage. Defaults to 16.

        • Optionaloverwrite?: boolean

          If true, drops and recreates the index even if it already exists. Defaults to false.

        • Optionalverbose?: boolean

          If true, logs VSS index creation status. Defaults to false.

      Returns this

      The table, so methods can be chained.

      // Load data that already contains an embedding column
      await table
      .loadData("data.csv")
      .createVssIndex("embedding_column").log();
      // Recreate an existing index
      await table.createVssIndex("embedding_column", {
      overwrite: true,
      }).log();
      // Create index with verbose logging
      await table.createVssIndex("embedding_column", {
      verbose: true,
      }).log();
      // Logs: 'Creating VSS index on "embedding_column" column...'
      // Logs: "VSS index created successfully."
      // Create index with custom HNSW parameters for higher accuracy
      await table.createVssIndex("embedding_column", {
      efConstruction: 256,
      efSearch: 128,
      M: 32,
      }).log();
    • Groups rows with similar numeric features using HDBSCAN, without requiring a predefined number of clusters. Adds a VARCHAR column containing "cluster-0", "cluster-1", etc., or "noise" for rows outside clusters. Labels identify groups, not their rank.

      Accepts a numeric vector column or several numeric scalar columns. Features are used as supplied. If their scales differ, consider preparing them with normalize(), zScore(), or normalizeVector() before clustering.

      Exact clustering is the default and can be expensive for large datasets. Set approximate: true to use approximate clustering, which can produce different results, including between repeated runs.

      Parameters

      • columns: string | string[]

        A numeric vector column, or numeric scalar columns in feature-dimension order.

      • newColumn: string

        The cluster-label column to create.

      • options: {
            allowSingleCluster?: boolean;
            approximate?: boolean;
            membershipScoreColumn?: string;
            metric?: "euclidean" | "cosine";
            minClusterSize?: number;
            minSamples?: number;
            outlierScoreColumn?: string;
        } = {}

        HDBSCAN clustering and output settings.

        • OptionalallowSingleCluster?: boolean

          Allow a result with just one cluster, possibly alongside noise. Defaults to false. Setting this to true can favor one broad group, even when there is little meaningful structure.

        • Optionalapproximate?: boolean

          Use approximate clustering. Defaults to false.

        • OptionalmembershipScoreColumn?: string

          Optional DOUBLE column for membership strength from 0 to 1. Higher values indicate stronger membership in the assigned cluster; noise scores 0.

        • Optionalmetric?: "euclidean" | "cosine"

          Use "euclidean" to compare feature values, or "cosine" to compare vector direction regardless of overall magnitude. Defaults to "euclidean".

        • OptionalminClusterSize?: number

          Minimum cluster size, from 2 through the row count. Defaults to 5.

        • OptionalminSamples?: number

          Number of neighbors used to estimate local density, excluding the point itself. Higher values make clustering more conservative, generally labeling more rows as noise. Set this separately to adjust density sensitivity independently of minimum cluster size. Defaults to minClusterSize.

        • OptionaloutlierScoreColumn?: string

          Optional DOUBLE column for outlier scores from 0 to 1. Higher values indicate more unusual rows relative to their surroundings, including within clusters. This is not necessarily 1 - membershipScore.

      Returns this

      The table, so methods can be chained.

      // Cluster numeric columns and add membership and outlier scores.
      await table
      .hdbscan(["height", "weight"], "cluster", {
      minClusterSize: 5,
      membershipScoreColumn: "membership",
      outlierScoreColumn: "outlierScore",
      })
      .log();
      // Scale vector dimensions before clustering.
      await table
      .normalizeVector("features", "scaledFeatures")
      .hdbscan("scaledFeatures", "cluster")
      .log();
    • Reduces numeric features, such as embeddings, to a two-dimensional UMAP projection. The resulting coordinates are added as umapX and umapY, while all existing columns (including the source feature columns), their values and types, and the input row order are preserved. DuckDB computes neighbors and the fuzzy graph; TypeScript optimizes the coordinates without copying the input vectors into JavaScript. Neighbor search is selected automatically.

      Accepts a numeric LIST or ARRAY column, or an array of numeric scalar column names combined internally into a vector in the supplied order. Features must be finite and non-null; vectors must be nonempty and equally sized. Features are converted to DOUBLE without automatic scaling. Large integers and exact decimals can lose precision in this conversion. If feature scales differ, consider normalize() or zScore() before projection.

      The defaults are a starting point for exploration. To adjust the projection:

      • neighbors (default 15): How many nearby points influence the layout. Smaller values emphasize local detail but can fragment groups. Larger values emphasize broader structure, can hide local detail, and generally require more time and memory.
      • metric (default "euclidean"): How similarity is measured. Euclidean compares distance, including differences in vector magnitude. Cosine compares direction, ignoring magnitude. Choose according to what makes vectors similar in your data; neither is universally better.
      • epochs (default 200): How many passes refine the coordinates. Fewer passes finish sooner but may leave the layout unfinished. More passes allow further refinement and take longer, with diminishing returns.
      • minDistance (default 0.1): How tightly points can group in the projection. Smaller values allow tighter clumps; larger values spread points out. This changes the layout's appearance, not an accuracy level or the number of optimization passes.
      • learningRate (default 1): The initial size of coordinate adjustments. Smaller values make gentler adjustments and may need more epochs. Larger values make bigger adjustments but can overshoot useful positions. This does not change the number of passes.
      • negativeSamples (default 5): How many random points are sampled for repulsion per attractive update. Smaller values reduce work and repulsion; larger values increase both, tending to separate unrelated points more strongly. More samples do not guarantee a better projection.

      Parameters

      • columns: string | string[]

        A numeric vector column name, or a nonempty array of numeric scalar column names in feature order. Use a one-element array for a single scalar feature.

      • options: {
            epochs?: number;
            learningRate?: number;
            metric?: "euclidean" | "cosine";
            minDistance?: number;
            negativeSamples?: number;
            neighbors?: number;
            seed?: number;
        } = {}

        Optional projection settings.

        • Optionalepochs?: number

          Integer number of refinement passes, at least 1. Defaults to 200.

        • OptionallearningRate?: number

          Finite, positive initial learning rate. Defaults to 1.

        • Optionalmetric?: "euclidean" | "cosine"

          Input distance metric: "euclidean" or "cosine". Defaults to "euclidean".

        • OptionalminDistance?: number

          Grouping distance parameter between 0 and 1. Defaults to 0.1.

        • OptionalnegativeSamples?: number

          Integer number of samples used to separate unrelated points, at least 1. Defaults to 5.

        • Optionalneighbors?: number

          Neighborhood size, including the point itself. Integer of at least 2, clamped to row count minus one. Defaults to 15.

        • Optionalseed?: number

          Integer used to initialize random choices. Defaults to 42.

      Returns this

      The table, so methods can be chained.

      await table.umap("embedding", {
      metric: "cosine",
      seed: 42,
      }).log();
      await table.umap("embedding", {
      neighbors: 30,
      minDistance: 0.25,
      }).selectColumns(["label", "umapX", "umapY"]).log();
      await table
      .umap(["height", "weight", "age"], { seed: 42 })
      .log();