question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Multi-paths update and Geofire

See original GitHub issue

Hi,

As mentioned here, I think it should be great to easily multi update data paths and a geofire path at the same time. As the set of Geofire is pretty complex, it’s hard to directly copy/paste the code to make it works.

Here the use case : (I use the same as the link above but add a path to know where a post is located). So the structure :

{
  posts: {
    post1: {
      from : user1,
      message: "Hi",
    },
    post2: {
      from : user2,
      message: "Hey",
    }
  },
  posts-location: {
    post1: {
      g : 'xxxxxxxx',
      l: {
         0 : latitude,
         1 : longitude
      }
    },
    post2: {
       g : 'xxxxxxxx',
       l: {
         0 : latitude,
         1 : longitude
      }
    }
  },
  users: {
    user1: {
      posts: {
        post1: true
      }
    },
    user2: {
      posts: {
        post2: true
      }
    }
  }
}

Now I can add a post at the same time at users and posts (with multi-path update). But I need to set the position after. In my use case, a post without a position is not valid data, it’s for that the best should be to do the 3 updates at the same times (if one fails, all fail).

Deleting Geofire path is easy (using update(null)), we can multi-update for delete, but for set/update, I loop for a way to do that easily,

Cheers, Clément

Issue Analytics

  • State:open
  • Created 8 years ago
  • Reactions:1
  • Comments:8

github_iconTop GitHub Comments

1reaction
kzcommented, Jul 15, 2016

+1. Atomic updates are really important to ensure the integrity of the database.

0reactions
deflexablecommented, Nov 16, 2021

checking the set function inside module at …/node_modules/geofire/dist/geofire/index.cjs.js" there wasn’t really sophisticated method or external library that help update geofire hash to firebase. It’s just plan method and validation.

as @ghost suggested earlier, instead of returning a promise for updating the data, you can simple return newData, then do the uploading yourself.

you can simply copy and paste below function, then you can do something like

`var map = {}; map[‘eventIndex/’ + v] = getGeoFireHashes(eventKey, [location.lat, location.lon]);

firebase.database().ref(‘/’).update(map);`

`function getGeoFireHashes(keyOrLocations, location) { var locations; if (typeof keyOrLocations === ‘string’ && keyOrLocations.length !== 0) { // If this is a set for a single location, convert it into a object locations = {}; locations[keyOrLocations] = location; } else if (typeof keyOrLocations === ‘object’) { if (typeof location !== ‘undefined’) { throw new Error(‘The location argument should not be used if you pass an object to set().’); } locations = keyOrLocations; } else { throw new Error(‘keyOrLocations must be a string or a mapping of key - location pairs.’); } var newData = {}; Object.keys(locations).forEach(function (key) { validateKey(key); var location = locations[key]; if (location === null) { // Setting location to null is valid since it will remove the key newData[key] = null; } else { validateLocation(location); var geohash = geohashForLocation(location); newData[key] = encodeGeoFireObject(location, geohash); } }); return newData; }

/**

  • Encodes a location and geohash as a GeoFire object.
  • @param location The location as [latitude, longitude] pair.
  • @param geohash The geohash of the location.
  • @returns The location encoded as GeoFire object. */ function encodeGeoFireObject(location, geohash) { validateLocation(location); validateGeohash(geohash); return { ‘.priority’: geohash, ‘g’: geohash, ‘l’: location }; }

/**

  • Generates a geohash of the specified precision/string length from the [latitude, longitude]
  • pair, specified as an array.
  • @param location The [latitude, longitude] pair to encode into a geohash.
  • @param precision The length of the geohash to create. If no precision is specified, the
  • global default is used.
  • @returns The geohash of the inputted location. */ function geohashForLocation(location, precision) { if (precision === void 0) { precision = GEOHASH_PRECISION; } validateLocation(location); if (typeof precision !== ‘undefined’) { if (typeof precision !== ‘number’ || isNaN(precision)) { throw new Error(‘precision must be a number’); } else if (precision <= 0) { throw new Error(‘precision must be greater than 0’); } else if (precision > 22) { throw new Error(‘precision cannot be greater than 22’); } else if (Math.round(precision) !== precision) { throw new Error(‘precision must be an integer’); } } var latitudeRange = { min: -90, max: 90 }; var longitudeRange = { min: -180, max: 180 }; var hash = ‘’; var hashVal = 0; var bits = 0; var even = 1; while (hash.length < precision) { var val = even ? location[1] : location[0]; var range = even ? longitudeRange : latitudeRange; var mid = (range.min + range.max) / 2; if (val > mid) { hashVal = (hashVal << 1) + 1; range.min = mid; } else { hashVal = (hashVal << 1) + 0; range.max = mid; } even = !even; if (bits < 4) { bits++; } else { bits = 0; hash += BASE32[hashVal]; hashVal = 0; } } return hash; }

/**

  • Validates the inputted geohash and throws an error if it is invalid.
  • @param geohash The geohash to be validated. */ function validateGeohash(geohash) { var error; if (typeof geohash !== ‘string’) { error = ‘geohash must be a string’; } else if (geohash.length === 0) { error = ‘geohash cannot be the empty string’; } else { for (var _i = 0, geohash_1 = geohash; _i < geohash_1.length; _i++) { var letter = geohash_1[_i]; if (BASE32.indexOf(letter) === -1) { error = ‘geohash cannot contain '’ + letter + ‘'’; } } } if (typeof error !== ‘undefined’) { throw new Error(‘Invalid GeoFire geohash '’ + geohash + '': ’ + error); } }

function validateKey(key) { var error; if (typeof key !== ‘string’) { error = ‘key must be a string’; } else if (key.length === 0) { error = ‘key cannot be the empty string’; } else if (1 + GEOHASH_PRECISION + key.length > 755) { // Firebase can only stored child paths up to 768 characters // The child path for this key is at the least: ‘i/<geohash>key’ error = ‘key is too long to be stored in Firebase’; } else if (/[[].#$/\u0000-\u001F\u007F]/.test(key)) { // Firebase does not allow node keys to contain the following characters error = ‘key cannot contain any of the following characters: . # $ ] [ /’; } if (typeof error !== ‘undefined’) { throw new Error(‘Invalid GeoFire key '’ + key + '': ’ + error); } }

function validateLocation(location) { var error; if (!Array.isArray(location)) { error = ‘location must be an array’; } else if (location.length !== 2) { error = 'expected array of length 2, got length ’ + location.length; } else { var latitude = location[0]; var longitude = location[1]; if (typeof latitude !== ‘number’ || isNaN(latitude)) { error = ‘latitude must be a number’; } else if (latitude < -90 || latitude > 90) { error = ‘latitude must be within the range [-90, 90]’; } else if (typeof longitude !== ‘number’ || isNaN(longitude)) { error = ‘longitude must be a number’; } else if (longitude < -180 || longitude > 180) { error = ‘longitude must be within the range [-180, 180]’; } } if (typeof error !== ‘undefined’) { throw new Error(‘Invalid GeoFire location '’ + location + '': ’ + error); } }`

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to perform a multi-path data update on Firebase when ...
I'm currently looking for a way to automatically update the /geofire side of my database when I update the latitude or longitude of...
Read more >
Data consistency with Multi-path updates - YouTube
Check out our blog posts for more information: - https://goo.gl/l2Nxo8- https://goo.gl/GSCn60Welcome to the seventh video in the Firebase ...
Read more >
GeoFire for Android — Realtime location queries with Firebase
GeoFire allows you to query all keys within a geographic area using GeoQuery objects. As the locations for keys change, the query is...
Read more >
Firebase Update Multiple Items - ADocLib
Updating data with transactions. Using the Cloud Firestore client libraries you can group multiple operations into a single transaction. Transactions are.
Read more >
Firebase Multi-path Updates — Updating Denormalized Data ...
TLDR — When using .update to change data in multiple locations with a single call, .update acts as .set. If you are updating...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found