Got the digikam database working via a custom SQL.js import. Need to test performance.

master
Sander Vocke 6 years ago
parent c122755451
commit 640247c7a1
  1. BIN
      public/digikam4.db
  2. BIN
      public/test_photos/Australie/IMG_0700.JPG
  3. BIN
      public/test_photos/Australie/IMG_0706.JPG
  4. BIN
      public/test_photos/Australie/IMG_0711.JPG
  5. BIN
      public/test_photos_db/digikam4.db
  6. BIN
      public/test_photos_db/thumbnails-digikam.db
  7. 127
      src/database.js
  8. 52
      src/index.js
  9. 6
      src/media.js

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

@ -3,9 +3,24 @@ import React from 'react';
export const DBTypeEnum = {
ALASQL_SQLITE: 1,
ALASQL_INDEXEDDB: 2,
ALASQL_NATIVE: 3
ALASQL_NATIVE: 3,
SQLJS_SQLITE: 4
};
export const DBSourceEnum = {
ATTACHFILE: 1,
CREATE: 2
}
export function alasql_async_queries(alasql_object, queries) {
var p = Promise.resolve(null);
for (let i = 0; i < queries.length; i++) {
p = p.then(() => { return alasql_object.promise(queries[i]); });
}
return p;
}
export class DB {
state = {
db_type: false,
@ -27,12 +42,7 @@ export class DB {
this.state.db_type === DBTypeEnum.ALASQL_INDEXEDDB ||
this.state.db_type === DBTypeEnum.ALASQL_NATIVE) {
var self = this;
var p = Promise.resolve(null);
for (let i = 0; i < s.length; i++) {
p = p.then(() => { return self.state.db_object.promise(s[i]); });
}
return p;
return alasql_async_queries(self.state.db_object, s);
}
throw new Error("Unsupported database type for async operations: " + this.state.db_type);
}
@ -47,6 +57,12 @@ export class DB {
}
return this.state.db_object(s[s.length - 1]);
}
if (this.state.db_type === DBTypeEnum.SQLJS_SQLITE) {
for (var i = 0; i < (s.length - 1); i++) {
this.state.db_object.exec(s[i]);
}
return this.state.db_object.exec(s[s.length - 1]);
}
throw new Error("Unsupported database type for sync operations: " + this.state.db_type);
}
@ -91,6 +107,67 @@ export class DB {
resolve();
});
});
} else if (this.state.db_type === DBTypeEnum.SQLJS_SQLITE && db_type === DBTypeEnum.ALASQL_NATIVE) {
return new Promise(function (resolve, reject) {
var tables = self.queries_sync(["SELECT * FROM sqlite_master WHERE type='table';"])[0].values;
console.log(tables);
// Get all table definition lines.
var table_definitions = [];
var table_names = [];
tables.forEach(elem => {
table_definitions.push(elem[4]);
table_names.push(elem[1]);
});
// We want to create all these tables in the AlaSQL database as well. However, that will require
// us to escape the table names because AlaSQL may break on colums with names equal to keywords
// (see https://github.com/agershun/alasql/issues/1155).
// Here we take a dirty shortcut: explicitly escape all occurrences of "value", "query" and "matrix".
var new_table_definitions = [];
table_definitions.forEach(elem => {
var newelem = elem
.replace(/value/g, "`value`")
.replace(/query/g, "`query`")
.replace(/matrix/g, "`matrix`");
new_table_definitions.push(newelem);
});
console.log(new_table_definitions);
var asql = require('alasql');
var queries = [];
queries.push("CREATE DATABASE IF NOT EXISTS " + db_name + ";");
queries.push("USE " + db_name + ";");
new_table_definitions.forEach(elem => {
queries.push(elem); // Creates the table
});
alasql_async_queries(asql, queries)
.then(() => {
// Fill the tables by passing through a JS dictionary object.
table_names.forEach(elem => {
var res = self.queries_sync(["SELECT * FROM " + elem + ";"])[0];
if (res) {
var structured = [];
res.values.forEach(vals => {
var row = {};
var i = 0;
res.columns.forEach(column => {
row[column] = vals[i];
i++;
})
structured.push(row);
});
asql("SELECT * INTO " + elem + " FROM ?", [structured]);
}
});
})
.then(() => {
self.state.db_type = db_type;
self.state.db_name = db_name;
self.state.db_object = asql;
resolve();
});
});
}
throw new Error("Unsupported copy creation from db_type " + this.state.db_type + " to " + db_type);
}
@ -113,6 +190,28 @@ function fetch_db_from_sqlite(filename, db_name) {
});
}
function fetch_sqljs_db_from_sqlite(filename, db_name) {
return new Promise(function (resolve, reject) {
var initSqlJs = require('sql.js');
initSqlJs({ locateFile: filename => `/sql.js/dist/${filename}` })
.then(SQL => {
fetch(filename)
.then(res => {
if (!res.ok) {
throw new Error(res.status);
}
return res.arrayBuffer();
})
.then(data => {
var array = new Uint8Array(data);
var sqljs_db = new SQL.Database(array);
var imported_db = new DB(sqljs_db, db_name, DBTypeEnum.SQLJS_SQLITE);
resolve(imported_db);
})
});
});
}
function create_indexed_db(name) {
return new Promise(function (resolve, reject) {
var asql = require('alasql');
@ -141,11 +240,6 @@ function create_native_db(name) {
});
}
export const DBSourceEnum = {
ATTACHFILE: 1,
CREATE: 2
}
export class ProvideDB extends React.Component {
state = {
loading: true,
@ -180,6 +274,15 @@ export class ProvideDB extends React.Component {
.then(() => { this.setState({ loading: false, done: true, db: db }) });
} else { this.setState({ loading: false, done: true, db: db }) };
});
} else if (this.props.db_source_type === DBTypeEnum.SQLJS_SQLITE && this.props.db_source === DBSourceEnum.ATTACHFILE) {
fetch_sqljs_db_from_sqlite(this.props.db_file, this.props.db_source_name)
.then(db => {
if (this.props.db_target_type !== this.props.db_source_type) {
db.migrate_async(this.props.db_target_type, this.props.db_target_name)
.then(() => { this.setState({ loading: false, done: true, db: db }) });
} else { this.setState({ loading: false, done: true, db: db }) };
})
.catch(error => this.setState({ loading: false, done: false, error }));
} else {
throw new Error("Unsupported ProvideDB configuration: from source " + this.props.db_source +
" with source type " + this.props.db_source_type + " to target type " + this.props.db_target_type + ".");

@ -6,21 +6,21 @@ import { Photo, PhotoView } from './media.js';
import './index.css';
const URLLoading = ({url}) => <p>Loading: {url}</p>;
const URLError = ({error}) => <p>Failed to load URL resource: {error.message}</p>;
const URLFinished = ({url, data}) => <p>{url} was loaded: {data.byteLength} bytes received.</p>;
const URLLoading = ({ url }) => <p>Loading: {url}</p>;
const URLError = ({ error }) => <p>Failed to load URL resource: {error.message}</p>;
const URLFinished = ({ url, data }) => <p>{url} was loaded: {data.byteLength} bytes received.</p>;
const DBLoading = ({sqlite_file}) => <p>Loading: {sqlite_file}</p>;
const DBError = ({error}) => <p>Failed to load database: {error.message}</p>;
const DBFinished = ({sqlite_file, db}) => <p>{sqlite_file} was loaded. Name: { db.state.db_name }. </p>;
const DBLoading = ({ sqlite_file }) => <p>Loading: {sqlite_file}</p>;
const DBError = ({ error }) => <p>Failed to load database: {error.message}</p>;
const DBFinished = ({ sqlite_file, db }) => <p>{sqlite_file} was loaded. Name: {db.state.db_name}. </p>;
const TestFetch = ({url}) => (
const TestFetch = ({ url }) => (
<Fetch url={url}>
{({ loading, error, done, data }) => (
<>
{ loading && <URLLoading url={url}/> }
{ error && <URLError error={error} />}
{ done && <URLFinished url={url} data={data} /> }
{loading && <URLLoading url={url} />}
{error && <URLError error={error} />}
{done && <URLFinished url={url} data={data} />}
</>
)}
</Fetch>
@ -33,9 +33,11 @@ export class PhotoFromDB extends React.Component {
}
componentDidMount() {
this.props.database.queries_async(["SELECT * FROM Images;"])
this.props.database.queries_async(["SELECT Images.name, Albums.relativePath FROM Images INNER JOIN Albums ON Images.album=Albums.id;"])
.then(res => {
var photo = new Photo(res[0]);
var photo = new Photo;
var imagepath = process.env.PUBLIC_URL + "/test_photos" + res[0]["relativePath"] + "/" + res[0]["name"];
photo.state.path = imagepath;
this.setState({ done: true, photo: photo });
});
}
@ -43,34 +45,34 @@ export class PhotoFromDB extends React.Component {
render() {
return (
<>
{ this.state.done && <PhotoView photo={ this.state.photo }/> }
{this.state.done && <PhotoView photo={this.state.photo} />}
</>
);
}
}
const TestDBFetch = ({sqlite_file}) => (
<ProvideDB db_file={sqlite_file} db_source_type={DBTypeEnum.ALASQL_SQLITE} db_target_type={DBTypeEnum.ALASQL_NATIVE}
const TestDBFetch = ({ sqlite_file }) => (
<ProvideDB db_file={sqlite_file} db_source_type={DBTypeEnum.SQLJS_SQLITE} db_target_type={DBTypeEnum.ALASQL_NATIVE}
db_source_name="sqlite_db" db_target_name="db" db_source={DBSourceEnum.ATTACHFILE}>
{({ loading, error, done, db }) => (
<>
{ loading && <DBLoading sqlite_file={sqlite_file}/> }
{ error && <DBError error={error} />}
{ done && <DBFinished sqlite_file={sqlite_file} db={db} /> }
{ done && <DBQueryConsole database={db}/> }
{ done && <PhotoFromDB database={db}/> }
{loading && <DBLoading sqlite_file={sqlite_file} />}
{error && <DBError error={error} />}
{done && <DBFinished sqlite_file={sqlite_file} db={db} />}
{done && <DBQueryConsole database={db} />}
{done && <PhotoFromDB database={db} />}
</>
)}
</ProvideDB>
)
const TestDBPlayground = ({db_name}) => (
const TestDBPlayground = ({ db_name }) => (
<ProvideDB db_source_type={DBTypeEnum.ALASQL_INDEXEDDB} db_target_type={DBTypeEnum.ALASQL_INDEXEDDB}
db_source_name={db_name} db_target_name={db_name} db_source={DBSourceEnum.CREATE}>
{({ loading, error, done, db }) => (
<>
{ error && <DBError error={error} />}
{ done && <DBQueryConsole database={db}/> }
{error && <DBError error={error} />}
{done && <DBQueryConsole database={db} />}
</>
)}
</ProvideDB>
@ -81,9 +83,9 @@ ReactDOM.render(
<h1>Test file fetching:</h1>
<TestFetch url={process.env.PUBLIC_URL + "/stuff"} />
<h1>IndexedDB playground:</h1>
<TestDBPlayground db_name="playground_db"/>
<TestDBPlayground db_name="playground_db" />
<h1>Test DB fetching:</h1>
<TestDBFetch sqlite_file={process.env.PUBLIC_URL + "/digikam4.db"} />
<TestDBFetch sqlite_file={process.env.PUBLIC_URL + "/test_photos_db/digikam4.db"} />
</>,
document.getElementById('root')
);

@ -5,11 +5,7 @@ export class Media {}
export class Photo extends Media {
state = {
path: false,
}
constructor(entry) {
super();
this.state.path = entry.ImagePath;
thumbnailpath: false
}
}

Loading…
Cancel
Save