antoniopresto / react-native-local-mongodb

react-native local mongodb storage.
MIT License
304 stars 66 forks source link

possibility to store to file #42

Closed vaukalak closed 2 years ago

vaukalak commented 6 years ago

My app needs an offline mode, which operates ~60MB of data. And this causes 2 issues: 1 - on android the app AsyncStorage by default limits to 6MB (this can be avoided). 2 - considering indexes, that also use some memory, this will generate a big impact on RAM usage, and may lead to app crashes especially on android.

So my question is: do you have any plans to support file storage, if yes, how can I help you with that? Thanks.

antoniopresto commented 6 years ago

you can try using a custom storage. I haven't tested it in react-native yet, but you can take a look at this: https://github.com/antoniopresto/react-native-local-mongodb/blob/7147ea9a5a02577b455ee3aa52a0927858256d6a/lib/datastore.js#L42

the mock used to test in jest is an example of custom storage: https://github.com/antoniopresto/react-native-local-mongodb/blob/master/config/AsyncStorageMock.js

joaopiopedreira commented 6 years ago

Hi @vaukalak , @antoniopresto , needed to change my storage because of android limitations and finally had the time to test this. Works like a charm! Used expo FilesSystem API because I'm on ExpoKit, but I could also have used something like rn-fetch-blob to the same effect (redux-persist-filesystem-storage does exactly that).

Here's my code, for completeness:

import {FileSystem} from 'expo'

let options = {
  storagePath: `${FileSystem.documentDirectory}database`,
  toFileName: (name) => name.split(':').join('-'),
  fromFileName: (name) => name.split('-').join(':'),
};

// init directory: make sure our database path exists on startup
FileSystem.getInfoAsync(options.storagePath)
  .then(async ({exists}) => {
    return !exists &&  await FileSystem.makeDirectoryAsync(options.storagePath);
  }).catch(e => {
    console.warn('Error creating database path',e);
  });

const pathForKey = (key) => `${options.storagePath}/${options.toFileName(key)}`;

const FilesystemStorage = {
  config: (customOptions) => {
    options = {
      ...options,
      ...customOptions,
    }
  },

  setItem: (key, value, callback) =>
    FileSystem.writeAsStringAsync(pathForKey(key), value)
      .then(() => callback && callback())
      .catch(error => callback && callback(error)),

  getItem: (key, callback) =>
    FileSystem.readAsStringAsync(pathForKey(options.toFileName(key)))
      .then(data => {
        callback && callback(null, data);
        if (!callback) {
          return data
        }
      })
      .catch(error => {
        callback && callback(error);
        if (!callback) {
          throw error
        }
      }),

  removeItem: (key, callback) =>
    FileSystem.deleteAsync(pathForKey(options.toFileName(key)))
      .then(() => callback && callback())
      .catch(error => {
        callback && callback(error);
        if (!callback) {
          throw error
        }
      }),

  getAllKeys: (callback) =>
    FileSystem.getInfoAsync(options.storagePath)
      .then(async ({exists}) =>
        exists ? true : await FileSystem.makeDirectoryAsync(options.storagePath)
      )
      .then(() =>
        FileSystem.readDirectoryAsync(options.storagePath)
          .then(files => files.map(file => options.fromFileName(file)))
          .then(files => {
            callback && callback(null, files);
            if (!callback) {
              return files
            }
          })
      )
      .catch(error => {
        callback && callback(error);
        if (!callback) {
          throw error
        }
      }),
};

FilesystemStorage.clear = (callback) =>
  FilesystemStorage.getAllKeys((error, keys) => {
    if (error) {
      throw error;
    }

    if (Array.isArray(keys) && keys.length) {
      const removedKeys = [];

      keys.forEach(key => {
        FilesystemStorage.removeItem(key, error => {
          removedKeys.push(key);
          if (error && callback) {
            callback(error, false);
          }
          if (removedKeys.length === keys.length && callback) {
            callback(null, true);
          }
        })
      });
      return true
    }

    callback && callback(null, false);
    return false
  }).catch(error => {
    callback && callback(error);
    if (!callback) {
      throw error
    }
  });

export default FilesystemStorage

Later, when initialising the database:

import DataStore from 'react-native-local-mongodb';
import FilesystemStorage from './FilesystemStorage';

export const Visits = async () => {
  const db = new DataStore({filename: 'db.visits',storage: FilesystemStorage});
  await db.loadDatabaseAsync();
  db.ensureIndex({fieldName: 'companyId'});
  db.ensureIndex({fieldName: 'date'});
  db.ensureIndex({fieldName: 'driverId'});
  db.ensureIndex({fieldName: 'priority'});
  db.ensureIndex({fieldName: 'closed'});
  db.ensureIndex({fieldName: 'inProgress'});
  db.ensureIndex({fieldName: 'skipped'});
  return db
};

(...)
wuxushun commented 5 years ago

try react-native-nedb, it store to file

BenoitClaveau commented 5 years ago

@joaopiopedreira Is it possible to encrypt the storage file?

emannick commented 5 years ago

Hi @joaopiopedreira

How to use Custom Storage in React Native CLI for local Mongo DB since you have used Expo over here

BenoitClaveau commented 5 years ago

@emannick do you think it is possible to encrypt the file ?

emannick commented 5 years ago

@BenoitClaveau we should ask this to @joaopiopedreira. Since he has tried with the Expo.

antoniopresto commented 2 years ago

If you need that, just use a custom storage. or send a PR. https://github.com/antoniopresto/react-native-local-mongodb/issues/42#issuecomment-418715160