mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-09-16 13:34:10 +02:00
Implemented --convert for different things, renamed --all to --text
This commit is contained in:
parent
33223856ff
commit
686db981be
4 changed files with 104 additions and 25 deletions
10
docs/cli.rst
10
docs/cli.rst
|
|
@ -878,8 +878,8 @@ If you do this, the table will be created with column names called ``untitled_1`
|
||||||
|
|
||||||
.. _cli_insert_unstructured:
|
.. _cli_insert_unstructured:
|
||||||
|
|
||||||
Inserting unstructured data with \-\-lines and \-\-all
|
Inserting unstructured data with \-\-lines and \-\-text
|
||||||
======================================================
|
=======================================================
|
||||||
|
|
||||||
If you have an unstructured file you can insert its contents into a table with a single ``line`` column containing each line from the file using ``--lines``. This can be useful if you intend to further analyze those lines using SQL string functions or :ref:`sqlite-utils convert <cli_convert>`::
|
If you have an unstructured file you can insert its contents into a table with a single ``line`` column containing each line from the file using ``--lines``. This can be useful if you intend to further analyze those lines using SQL string functions or :ref:`sqlite-utils convert <cli_convert>`::
|
||||||
|
|
||||||
|
|
@ -893,16 +893,16 @@ This will produce the following schema:
|
||||||
[line] TEXT
|
[line] TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
You can also insert the entire contents of the file into a single column called ``all`` using ``--all``::
|
You can also insert the entire contents of the file into a single column called ``text`` using ``--text``::
|
||||||
|
|
||||||
$ sqlite-utils insert content.db content file.txt --all
|
$ sqlite-utils insert content.db content file.txt --text
|
||||||
|
|
||||||
The schema here will be:
|
The schema here will be:
|
||||||
|
|
||||||
.. code-block:: sql
|
.. code-block:: sql
|
||||||
|
|
||||||
CREATE TABLE [content] (
|
CREATE TABLE [content] (
|
||||||
[all] TEXT
|
[text] TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
.. _cli_insert_replace:
|
.. _cli_insert_replace:
|
||||||
|
|
|
||||||
|
|
@ -657,7 +657,9 @@ def insert_upsert_options(fn):
|
||||||
help="Treat each line as a single value called 'line'",
|
help="Treat each line as a single value called 'line'",
|
||||||
),
|
),
|
||||||
click.option(
|
click.option(
|
||||||
"--all", is_flag=True, help="Treat input as a single value called 'all'"
|
"--text",
|
||||||
|
is_flag=True,
|
||||||
|
help="Treat input as a single value called 'text'",
|
||||||
),
|
),
|
||||||
click.option("--convert", help="Python code to convert each item"),
|
click.option("--convert", help="Python code to convert each item"),
|
||||||
click.option(
|
click.option(
|
||||||
|
|
@ -723,7 +725,7 @@ def insert_upsert_implementation(
|
||||||
csv,
|
csv,
|
||||||
tsv,
|
tsv,
|
||||||
lines,
|
lines,
|
||||||
all,
|
text,
|
||||||
convert,
|
convert,
|
||||||
imports,
|
imports,
|
||||||
delimiter,
|
delimiter,
|
||||||
|
|
@ -785,11 +787,8 @@ def insert_upsert_implementation(
|
||||||
docs = tracker.wrap(docs)
|
docs = tracker.wrap(docs)
|
||||||
elif lines:
|
elif lines:
|
||||||
docs = ({"line": line.strip()} for line in decoded)
|
docs = ({"line": line.strip()} for line in decoded)
|
||||||
elif all:
|
elif text:
|
||||||
docs = ({"all": decoded.read()},)
|
docs = ({"text": decoded.read()},)
|
||||||
elif convert:
|
|
||||||
fn = _compile_code(convert, imports)
|
|
||||||
docs = (fn(line) for line in decoded)
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
if nl:
|
if nl:
|
||||||
|
|
@ -805,6 +804,20 @@ def insert_upsert_implementation(
|
||||||
if flatten:
|
if flatten:
|
||||||
docs = (dict(_flatten(doc)) for doc in docs)
|
docs = (dict(_flatten(doc)) for doc in docs)
|
||||||
|
|
||||||
|
if convert:
|
||||||
|
variable = "row"
|
||||||
|
if lines:
|
||||||
|
variable = "line"
|
||||||
|
elif text:
|
||||||
|
variable = "text"
|
||||||
|
fn = _compile_code(convert, imports, variable=variable)
|
||||||
|
if lines:
|
||||||
|
docs = (fn(doc["line"]) for doc in docs)
|
||||||
|
elif text:
|
||||||
|
docs = (fn(doc["text"]) for doc in docs)
|
||||||
|
else:
|
||||||
|
docs = (fn(doc) for doc in docs)
|
||||||
|
|
||||||
extra_kwargs = {"ignore": ignore, "replace": replace, "truncate": truncate}
|
extra_kwargs = {"ignore": ignore, "replace": replace, "truncate": truncate}
|
||||||
if not_null:
|
if not_null:
|
||||||
extra_kwargs["not_null"] = set(not_null)
|
extra_kwargs["not_null"] = set(not_null)
|
||||||
|
|
@ -812,8 +825,10 @@ def insert_upsert_implementation(
|
||||||
extra_kwargs["defaults"] = dict(default)
|
extra_kwargs["defaults"] = dict(default)
|
||||||
if upsert:
|
if upsert:
|
||||||
extra_kwargs["upsert"] = upsert
|
extra_kwargs["upsert"] = upsert
|
||||||
|
|
||||||
# Apply {"$base64": true, ...} decoding, if needed
|
# Apply {"$base64": true, ...} decoding, if needed
|
||||||
docs = (decode_base64_values(doc) for doc in docs)
|
docs = (decode_base64_values(doc) for doc in docs)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db[table].insert_all(
|
db[table].insert_all(
|
||||||
docs, pk=pk, batch_size=batch_size, alter=alter, **extra_kwargs
|
docs, pk=pk, batch_size=batch_size, alter=alter, **extra_kwargs
|
||||||
|
|
@ -889,7 +904,7 @@ def insert(
|
||||||
csv,
|
csv,
|
||||||
tsv,
|
tsv,
|
||||||
lines,
|
lines,
|
||||||
all,
|
text,
|
||||||
convert,
|
convert,
|
||||||
imports,
|
imports,
|
||||||
delimiter,
|
delimiter,
|
||||||
|
|
@ -918,7 +933,7 @@ def insert(
|
||||||
- Use --nl for newline-delimited JSON objects
|
- Use --nl for newline-delimited JSON objects
|
||||||
- Use --csv or --tsv for comma-separated or tab-separated input
|
- Use --csv or --tsv for comma-separated or tab-separated input
|
||||||
- Use --lines to write each incoming line to a column called "line"
|
- Use --lines to write each incoming line to a column called "line"
|
||||||
- Use --all to write the entire input to a column called "all"
|
- Use --text to write the entire input to a column called "text"
|
||||||
|
|
||||||
You can also use --convert to pass a fragment of Python code that will
|
You can also use --convert to pass a fragment of Python code that will
|
||||||
be used to convert each input.
|
be used to convert each input.
|
||||||
|
|
@ -927,7 +942,7 @@ def insert(
|
||||||
imported row, and can return a modified row.
|
imported row, and can return a modified row.
|
||||||
|
|
||||||
If you are using --lines your code will be passed a "line" variable,
|
If you are using --lines your code will be passed a "line" variable,
|
||||||
and for --all an "all" variable.
|
and for --text an "text" variable.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
insert_upsert_implementation(
|
insert_upsert_implementation(
|
||||||
|
|
@ -940,7 +955,7 @@ def insert(
|
||||||
csv,
|
csv,
|
||||||
tsv,
|
tsv,
|
||||||
lines,
|
lines,
|
||||||
all,
|
text,
|
||||||
convert,
|
convert,
|
||||||
imports,
|
imports,
|
||||||
delimiter,
|
delimiter,
|
||||||
|
|
@ -976,7 +991,7 @@ def upsert(
|
||||||
csv,
|
csv,
|
||||||
tsv,
|
tsv,
|
||||||
lines,
|
lines,
|
||||||
all,
|
text,
|
||||||
convert,
|
convert,
|
||||||
imports,
|
imports,
|
||||||
batch_size,
|
batch_size,
|
||||||
|
|
@ -1008,7 +1023,7 @@ def upsert(
|
||||||
csv,
|
csv,
|
||||||
tsv,
|
tsv,
|
||||||
lines,
|
lines,
|
||||||
all,
|
text,
|
||||||
convert,
|
convert,
|
||||||
imports,
|
imports,
|
||||||
delimiter,
|
delimiter,
|
||||||
|
|
|
||||||
|
|
@ -281,14 +281,14 @@ def progressbar(*args, **kwargs):
|
||||||
yield bar
|
yield bar
|
||||||
|
|
||||||
|
|
||||||
def _compile_code(code, imports):
|
def _compile_code(code, imports, variable="value"):
|
||||||
locals = {}
|
locals = {}
|
||||||
globals = {"r": recipes, "recipes": recipes}
|
globals = {"r": recipes, "recipes": recipes}
|
||||||
# If user defined a convert() function, return that
|
# If user defined a convert() function, return that
|
||||||
try:
|
try:
|
||||||
exec(code, globals, locals)
|
exec(code, globals, locals)
|
||||||
return locals["convert"]
|
return locals["convert"]
|
||||||
except (SyntaxError, NameError, KeyError, TypeError):
|
except (AttributeError, SyntaxError, NameError, KeyError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Try compiling their code as a function instead
|
# Try compiling their code as a function instead
|
||||||
|
|
@ -297,7 +297,7 @@ def _compile_code(code, imports):
|
||||||
if "\n" not in code and not code.strip().startswith("return "):
|
if "\n" not in code and not code.strip().startswith("return "):
|
||||||
code = "return {}".format(code)
|
code = "return {}".format(code)
|
||||||
|
|
||||||
new_code = ["def fn(value):"]
|
new_code = ["def fn({}):".format(variable)]
|
||||||
for line in code.split("\n"):
|
for line in code.split("\n"):
|
||||||
new_code.append(" {}".format(line))
|
new_code.append(" {}".format(line))
|
||||||
code_o = compile("\n".join(new_code), "<string>", "exec")
|
code_o = compile("\n".join(new_code), "<string>", "exec")
|
||||||
|
|
|
||||||
|
|
@ -339,14 +339,78 @@ def test_insert_lines(db_path):
|
||||||
] == list(db.query("select line from from_lines"))
|
] == list(db.query("select line from from_lines"))
|
||||||
|
|
||||||
|
|
||||||
def test_insert_all(db_path):
|
def test_insert_text(db_path):
|
||||||
result = CliRunner().invoke(
|
result = CliRunner().invoke(
|
||||||
cli.cli,
|
cli.cli,
|
||||||
["insert", db_path, "from_all", "-", "--all"],
|
["insert", db_path, "from_text", "-", "--text"],
|
||||||
input='First line\nSecond line\n{"foo": "baz"}',
|
input='First line\nSecond line\n{"foo": "baz"}',
|
||||||
)
|
)
|
||||||
assert 0 == result.exit_code, result.output
|
assert 0 == result.exit_code, result.output
|
||||||
db = Database(db_path)
|
db = Database(db_path)
|
||||||
assert [{"all": 'First line\nSecond line\n{"foo": "baz"}'}] == list(
|
assert [{"text": 'First line\nSecond line\n{"foo": "baz"}'}] == list(
|
||||||
db.query("select [all] from from_all")
|
db.query("select text from from_text")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"options,input",
|
||||||
|
(
|
||||||
|
([], '[{"id": "1", "name": "Bob"}, {"id": "2", "name": "Cat"}]'),
|
||||||
|
(["--csv"], "id,name\n1,Bob\n2,Cat"),
|
||||||
|
(["--nl"], '{"id": "1", "name": "Bob"}\n{"id": "2", "name": "Cat"}'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_insert_convert_json_csv_jsonnl(db_path, options, input):
|
||||||
|
result = CliRunner().invoke(
|
||||||
|
cli.cli,
|
||||||
|
["insert", db_path, "rows", "-", "--convert", '{**row, **{"extra": 1}}']
|
||||||
|
+ options,
|
||||||
|
input=input,
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
db = Database(db_path)
|
||||||
|
rows = list(db.query("select id, name, extra from rows"))
|
||||||
|
assert rows == [
|
||||||
|
{"id": "1", "name": "Bob", "extra": 1},
|
||||||
|
{"id": "2", "name": "Cat", "extra": 1},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_insert_convert_text(db_path):
|
||||||
|
result = CliRunner().invoke(
|
||||||
|
cli.cli,
|
||||||
|
[
|
||||||
|
"insert",
|
||||||
|
db_path,
|
||||||
|
"text",
|
||||||
|
"-",
|
||||||
|
"--text",
|
||||||
|
"--convert",
|
||||||
|
'{"text": text.upper()}',
|
||||||
|
],
|
||||||
|
input="This is text\nwill be upper now",
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
db = Database(db_path)
|
||||||
|
rows = list(db.query("select [text] from [text]"))
|
||||||
|
assert rows == [{"text": "THIS IS TEXT\nWILL BE UPPER NOW"}]
|
||||||
|
|
||||||
|
|
||||||
|
def test_insert_convert_lines(db_path):
|
||||||
|
result = CliRunner().invoke(
|
||||||
|
cli.cli,
|
||||||
|
[
|
||||||
|
"insert",
|
||||||
|
db_path,
|
||||||
|
"all",
|
||||||
|
"-",
|
||||||
|
"--lines",
|
||||||
|
"--convert",
|
||||||
|
'{"line": line.upper()}',
|
||||||
|
],
|
||||||
|
input="This is text\nwill be upper now",
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
db = Database(db_path)
|
||||||
|
rows = list(db.query("select [line] from [all]"))
|
||||||
|
assert rows == [{"line": "THIS IS TEXT"}, {"line": "WILL BE UPPER NOW"}]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue