// Copyright (c) 2023-2025 ParadeDB, Inc.
//
// This file is part of ParadeDB - Postgres for Search and Analytics
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
use pgrx::pg_sys::panic::ErrorReport;
use pgrx::*;
use serde_json::{json, Map, Value};
#[pg_extern(immutable, parallel_safe)]
#[allow(clippy::too_many_arguments)]
pub fn field(
name: &str,
indexed: default!(Option, "NULL"),
stored: default!(Option, "NULL"),
fast: default!(Option, "NULL"),
fieldnorms: default!(Option, "NULL"),
record: default!(Option, "NULL"),
expand_dots: default!(Option, "NULL"),
tokenizer: default!(Option, "NULL"),
normalizer: default!(Option, "NULL"),
) -> JsonB {
if let Some(true) = stored {
ErrorReport::new(
PgSqlErrorCode::ERRCODE_WARNING_DEPRECATED_FEATURE,
"the `stored` option is deprecated",
function_name!(),
)
.set_detail("the `stored` option is deprecated sine it is no longer needed by the index")
.set_hint("remove it from the index configuration")
.report(PgLogLevel::WARNING);
}
let mut config = Map::new();
indexed.map(|v| config.insert("indexed".to_string(), Value::Bool(v)));
fast.map(|v| config.insert("fast".to_string(), Value::Bool(v)));
fieldnorms.map(|v| config.insert("fieldnorms".to_string(), Value::Bool(v)));
record.map(|v| config.insert("record".to_string(), Value::String(v)));
expand_dots.map(|v| config.insert("expand_dots".to_string(), Value::Bool(v)));
tokenizer.map(|v| config.insert("tokenizer".to_string(), v.0));
normalizer.map(|v| config.insert("normalizer".to_string(), Value::String(v)));
JsonB(json!({ name: config }))
}
#[pg_extern(immutable, parallel_safe)]
#[allow(clippy::too_many_arguments)]
pub fn tokenizer(
name: &str,
remove_long: default!(Option, "255"),
lowercase: default!(Option, "true"),
min_gram: default!(Option, "NULL"),
max_gram: default!(Option, "NULL"),
prefix_only: default!(Option, "NULL"),
language: default!(Option, "NULL"),
pattern: default!(Option, "NULL"),
stemmer: default!(Option, "NULL"),
stopwords_language: default!(Option, "NULL"),
stopwords: default!(Option>, "NULL"),
) -> JsonB {
let mut config = Map::new();
config.insert("type".to_string(), Value::String(name.to_string()));
// Options for all types
remove_long.map(|v| config.insert("remove_long".to_string(), Value::Number(v.into())));
lowercase.map(|v| config.insert("lowercase".to_string(), Value::Bool(v)));
stemmer.map(|v| config.insert("stemmer".to_string(), Value::String(v)));
stopwords_language.map(|v| config.insert("stopwords_language".to_string(), Value::String(v)));
stopwords.map(|v| {
config.insert(
"stopwords".to_string(),
Value::Array(v.into_iter().map(Value::String).collect()),
)
});
// Options for type = ngram
min_gram.map(|v| config.insert("min_gram".to_string(), Value::Number(v.into())));
max_gram.map(|v| config.insert("max_gram".to_string(), Value::Number(v.into())));
prefix_only.map(|v| config.insert("prefix_only".to_string(), Value::Bool(v)));
// Options for type = stem
language.map(|v| config.insert("language".to_string(), Value::String(v)));
// Options for type = regex
pattern.map(|v| config.insert("pattern".to_string(), Value::String(v)));
JsonB(json!(config))
}