Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Unique Entity Key String #625

Closed
shaunc869 opened this issue May 23, 2015 · 22 comments
Closed

Get Unique Entity Key String #625

shaunc869 opened this issue May 23, 2015 · 22 comments
Assignees
Labels
api: datastore Issues related to the Datastore API. type: question Request for information or clarification. Not an issue.

Comments

@shaunc869
Copy link

When I view an entity in the web based datastore viewer I see a big long guid-esq string that I can normally use in Python to walk back to the entity without knowing its type, for example:

ahNzfnByb21wdGFwcG9pbnRtZW50chULEghTY2hlZHVsZRiAgICD2e7xxxxx

get me back a specific entity and with Python I can get to this by saying:

my_entity = ndb.Key(urlsafe="ahNzfnByb21wdGFwcG9pbnRtZW50chULEghTY2hlZHVsZRiAgICD2xxxx")

Can this be done with the gcloud nodejs library and/or is this entirely a bad method for some reason to begin with? Thanks!

@jgeewax
Copy link
Contributor

jgeewax commented May 24, 2015

That's a good question. What you're looking at is really just a (mostly) base64 encoded version of the entity_pb.Reference protobuf. Since a key can be encoded and decoded anyway you want, and the path (ie, ['Person', 1, 'Playlist', 1234]) is unique to the entity, you might want to consider just serializing the path and using that as a unique identifier.

So for example:

var path = ['Person', 1, 'Playlist', 1234];

var encodedPath = btoa(JSON.stringify(path)); // "WyJQZXJzb24iLDEsIlBsYXlsaXN0IiwxMjM0XQ=="
var decodedPath = JSON.parse(atob(encodedPath)); // ["Person",1,"Playlist",1234]

@ryanseys @stephenplusplus : Do you think it's worthwhile to add this method to the Key class so we don't make people implement it themselves?

@jgeewax jgeewax added type: question Request for information or clarification. Not an issue. api: datastore Issues related to the Datastore API. labels May 24, 2015
@jgeewax
Copy link
Contributor

jgeewax commented May 24, 2015

(For reference, here is the code that deals with the urlsafe stuff in NDB: https://code.google.com/p/appengine-ndb-experiment/source/browse/ndb/key.py#803)

@ryanseys
Copy link
Contributor

Hmm, trying to see the use case for this. I think we'd suggest that gcloud-node be used primarily driven by API calls, and I don't see that this information (base64'd reference protobuf stuff) is directly accessible through the API, rather you'd have to do extraneous work to put it in that format or manually extract it from the web view, so it's likely an edge case we won't directly support. @jgeewax does it sound like I am understanding you correctly given what you've said above?

@shaunc869
Copy link
Author

I can understand if this is an edge case, but in general I have worked with the datastore in both Java and Python, but it looks like the return values in both of these languages when trying to convert them to json come out a lot different than gcloud is there a reason why in Java and Python the format is a bit flatter and the keys don't include paths (see to_dict() method for example in Python)? I was thinking this might be why the guid keys were being used to keep the json a bit flatter, maybe?

Also I tried running the code above on a guid key and it doesn't seem to come out right, is that more pseudo code or should it work on a full key guid string?

Thanks for all the help!

@jgeewax
Copy link
Contributor

jgeewax commented May 25, 2015

@ryanseys If we think of this use case as ... "Make it easy for people to pass around key=" and retrieve it on a request, I think it'd be useful to have a method that serializes a string in something that is URL safe... That is, in express...

var express = require('express');
var gcloud = require('gcloud')({ projectId: 'my-project' });

var app = express();
var dataset = gcloud.datastore.dataset();

// GET /?key=WyJQZXJzb24iLDEsIlBsYXlsaXN0IiwxMjM0XQ==
app.get('/', function(req, res){
  dataset.get(dataset.key({encodedValue: req.query.key}, function(err, entity) {
    res.redirect('/otherPath?key=' + entity.key.encodedValue());
  });
});

app.listen(3000);

@shaunc869 What problems are you seeing with that same code...? Can you be more specific? I'm just using Javascript to JSON stringify a list and base-64 encode it... nothing gcloud-specific here.

@shaunc869
Copy link
Author

@jgeewax I agree, that helper method would be awesome. I am trying to copy and past the entity key guid string from the datastore entity editor on the developers console and then base64 decode it and I am not getting the full path for some reason.

@jgeewax
Copy link
Contributor

jgeewax commented May 25, 2015

@shaunc869 That code wasn't a way of decoding from the exact same format as the entity editor -- it was just an example demonstrating how we might go about this. You could certainly figure it out assuming it's using the same encoding style that NDB does (NDB's decoding logic is here: https://code.google.com/p/appengine-ndb-experiment/source/browse/ndb/key.py#803).

@shaunc869
Copy link
Author

Ah that makes sense, I will try my best with that, but I generally wait for much smarter developers like you to figure this stuff out. I will take a crack at the ndb stuff and see what I can come up. If this was to be implemented here would you use the same algorithm as ndb, I was assuming this was some kind of standard?

@jgeewax
Copy link
Contributor

jgeewax commented May 25, 2015

I believe (I could be wrong) but all that's happening is base64 encoding with a couple find/replace characters (-'s and /'s?) and then chopping off trailing ='s.

@shaunc869
Copy link
Author

I copied the exact code from that ndb code and ran it against the app engine console string and it does not come as a readable path. What am I doing wrong?

@jgeewax
Copy link
Contributor

jgeewax commented May 25, 2015

To help you, I'd need to see the exact code you're talking about... @shaunc869

@shaunc869
Copy link
Author

I grabbed the ndb code:

def _DecodeUrlSafe(urlsafe):
  """Decode a url-safe base64-encoded string.

  This returns the decoded string.
  """
  if not isinstance(urlsafe, basestring):
    raise TypeError('urlsafe must be a string; received %r' % urlsafe)
  if isinstance(urlsafe, unicode):
    urlsafe = urlsafe.encode('utf8')
  mod = len(urlsafe) % 4
  if mod:
    urlsafe += '=' * (4 - mod)
  # This is 3-4x faster than urlsafe_b64decode()
  return base64.b64decode(urlsafe.replace('-', '+').replace('_', '/'))

And if you try to run this you don't get a path like I would expect, I must be missing something?

@jgeewax
Copy link
Contributor

jgeewax commented May 25, 2015

It's not returning the path -- it's returning the binary of a Reference protobuf. What I wrote above:

What you're looking at is really just a (mostly) base64 encoded version of the entity_pb.Reference protobuf.

So you'd need to then create the protobuf object ... and then read the path property from that... :(

It doesn't look like it'd be fun code to write or use (and seems like it's totally overkill when a path suffices) so it's really really unlikely that gcloud-node would go that route when "coming up with a serializable version" of a key. (We'd likely go the route of just serializing the path, since that's unique...)

Is there some specific reason that you need to emulate the exact same encoding of the key from the Datastore UI?

@shaunc869
Copy link
Author

My current situation is I have an app engine Python app that I want to move to node so I am trying to emulate the same ajax methods I have on my python webapp2 app, which has a getter for all the entity types and returns a nice flat JSON model that I can manipulate on the client side and then save back.

This is probably related, but just now when I went to save back the data I sent the following:

{
  key: {
    path: [ 
      "PhoneNumber",
      5313141421375488
    ]
  },
  data: {
    company_id: {
      path: [
        "Company",
        4791049928048640
      ]
    },
    sid: "PN07d490d21f4f803c82bcadad342348",
    phone_number: "+13601234333",
    call_recording_enabled: false,
    date_added: "2015-04-26T22:10:48.000Z",
    date_updated: "2015-04-26T22:19:52.818Z",
    name: "Main Phone Number",
    form_id: {
      path: [
        "Form",
        473767298046333336
      ]
    },
    twiml: [ ]
  }
}

Normally I would expect this to update the datastore and save a reference to both the Form object and Company object in the datastore like it does in Python, but instead I see:

51 bytes, SHA-1 = c25e70c16756daa25d5149437471b06647b957cd for the company_id

and

48 bytes, SHA-1 = 47b70d6b2be9960fd0ad6f164b56ae756e1a503c for the form,

the datastore viewer isn't resolving this to the appropriate objects in the datastore like it would had I converted the JSON to a Python object via:

phone_number = PhoneNumber(**number_json)
phone_number.put()

Thanks for all the help!

@jgeewax
Copy link
Contributor

jgeewax commented May 25, 2015

Hm.. I think this kind of stuff might be better answered over on Stack Overflow as it's getting pretty specific about your app and less so about gcloud-node.

I'm going to leave this issue open, with the goal that we expose some way of nicely serializing a key's path so that users don't have to come up with their own ways of doing it, but I don't think I'm going to be as much help on your specific case (but SO is probably going to be much more useful)!

@shaunc869
Copy link
Author

Sounds good I will move it over there, thanks!

On Mon, May 25, 2015 at 11:04 AM, JJ Geewax notifications@github.com
wrote:

Hm.. I think this kind of stuff might be better answered over on Stack
Overflow as it's getting pretty specific about your app and less so about
gcloud-node.

I'm going to leave this issue open, with the goal that we expose some way
of nicely serializing a key's path so that users don't have to come up with
their own ways of doing it, but I don't think I'm going to be as much help
on your specific case (but SO is probably going to be much more useful)!


Reply to this email directly or view it on GitHub
#625 (comment)
.

Shaun
shaun@invoicesherpa.com
http://www.invoicesherpa.com
888-898-8302

@jgeewax
Copy link
Contributor

jgeewax commented Jun 8, 2015

Just a ping on the main issue here (which is "let's provide some URL-encodable option for the Key"):

Couldn't someone wanting to pass a key around simply do:

key = dataset.key({
  namespace: 'my-namespace',
  path: ['Kind', 'asdf', 'ChildKind', 1234]
});
var keyForURI = encodeURIComponent(JSON.stringify(key));

? Or does that not work...?

@stephenplusplus
Copy link
Contributor

Yeah, that would produce:

encoded:

%7B%22namespace%22%3A%22my-namespace%22%2C%22path%22%3A%5B%22Kind%22%2C%22asdf%22%2C%22ChildKind%22%2C1234%5D%7D

decoded:

{"namespace":"my-namespace","path":["Kind","asdf","ChildKind",1234]}

That's what I would do!

@jgeewax
Copy link
Contributor

jgeewax commented Jul 1, 2015

Is it worth providing a .to<something>() method on the Key class? Or should we close this out as ... "ya'll figure it out"?

@stephenplusplus
Copy link
Contributor

I think it should be figured out by the user, as we're not doing anything unique. Just stringifying and uri encoding. The encoding can be skipped in most libraries:

var postKey = dataset.key(['Post', 'post1']);

require('request').get({
  uri: 'http://localhost/',
  qs: {
    key: JSON.stringify(postKey) // sending a key object as a string. JSON.stringify is perfect
  }
});

// GET /saveKey?key=%7B%22path%22%3A%5B%22Post%22%2C%22post1%22%5D%7D

@arliber
Copy link

arliber commented Jul 5, 2017

I might have missed something but the referenced articles do not provide any progress on the matter and the suggested solution from above - using atob doesn't work for me.

Are there any news regarding the ability to query datastore using the URL-safe key in Node?

@BransonGitomeh
Copy link

BransonGitomeh commented Oct 13, 2017

hey, for me on datastore, i found out that the results from an insertion matches something like

const key = {
    kind: 'projects',
    path: ['projects', id],
    id,
  }

so the id is a number matching something like 5664248772427776

meaning that you can pass this around in your app and then when you need to fetch it you construct the object in the way datastore.get expects it to be

ie to fetch a project

const project = async ({ id }, { datastore }) => {
  const entity = await datastore.get({
    kind: 'projects',
    path: ['projects', id],
    id,
  });

  return Object.assign(entity.pop(), {
    id,
  });
};

will return the data as it in on the data store row.

when creating the entity on data store, you can get the id like

const create = async ({ project }, { datastore }) => {
  const key = datastore.key('projects');
  await datastore.save({
    key,
    data: project,
  });
  const { id } = key;
  return Object.assign(project, {
    id,
  });
};

returns an object with an id you can use around in url's

chingor13 pushed a commit that referenced this issue Aug 22, 2022
* feat!: Update library to use Node 12

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
chingor13 pushed a commit that referenced this issue Aug 22, 2022
* feat!: Update library to use Node 12

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
chingor13 pushed a commit that referenced this issue Aug 22, 2022
* feat!: Update library to use Node 12

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
sofisl pushed a commit that referenced this issue Nov 10, 2022
This PR was generated using Autosynth. 🌈

Synth log will be available here:
https://source.cloud.google.com/results/invocations/2812cef8-9404-4275-bb51-8c6dafc1bc6f/targets

- [ ] To automatically regenerate this PR, check this box.

PiperOrigin-RevId: 361273630
Source-Link: googleapis/googleapis@5477122
sofisl pushed a commit that referenced this issue Nov 10, 2022
- [ ] Regenerate this pull request now.

PiperOrigin-RevId: 470911839

Source-Link: googleapis/googleapis@3527566

Source-Link: googleapis/googleapis-gen@f16a1d2
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZjE2YTFkMjI0ZjAwYTYzMGVhNDNkNmE5YTFhMzFmNTY2ZjQ1Y2RlYSJ9

feat: accept google-gax instance as a parameter
Please see the documentation of the client constructor for details.

PiperOrigin-RevId: 470332808

Source-Link: googleapis/googleapis@d4a2367

Source-Link: googleapis/googleapis-gen@e97a1ac
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZTk3YTFhYzIwNGVhZDRmZTczNDFmOTFlNzJkYjdjNmFjNjAxNjM0MSJ9
sofisl added a commit that referenced this issue Nov 10, 2022
* build!: Update library to use Node 12

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
sofisl pushed a commit that referenced this issue Nov 11, 2022
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
sofisl pushed a commit that referenced this issue Nov 11, 2022
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [mocha](https://mochajs.org/) ([source](https://togithub.com/mochajs/mocha)) | devDependencies | major | [`^7.0.0` -> `^8.0.0`](https://renovatebot.com/diffs/npm/mocha/7.2.0/8.0.1) |

---

### Release Notes

<details>
<summary>mochajs/mocha</summary>

### [`v8.0.1`](https://togithub.com/mochajs/mocha/blob/master/CHANGELOG.md#&#8203;801--2020-06-10)

[Compare Source](https://togithub.com/mochajs/mocha/compare/v8.0.0...v8.0.1)

The obligatory patch after a major.

#### 🐛 Fixes

-   [#&#8203;4328](https://togithub.com/mochajs/mocha/issues/4328): Fix `--parallel` when combined with `--watch` ([**@&#8203;boneskull**](https://togithub.com/boneskull))

### [`v8.0.0`](https://togithub.com/mochajs/mocha/blob/master/CHANGELOG.md#&#8203;800--2020-06-10)

[Compare Source](https://togithub.com/mochajs/mocha/compare/v7.2.0...v8.0.0)

In this major release, Mocha adds the ability to _run tests in parallel_. Better late than never! Please note the **breaking changes** detailed below.

Let's welcome [**@&#8203;giltayar**](https://togithub.com/giltayar) and [**@&#8203;nicojs**](https://togithub.com/nicojs) to the maintenance team!

#### 💥 Breaking Changes

-   [#&#8203;4164](https://togithub.com/mochajs/mocha/issues/4164): **Mocha v8.0.0 now requires Node.js v10.0.0 or newer.** Mocha no longer supports the Node.js v8.x line ("Carbon"), which entered End-of-Life at the end of 2019 ([**@&#8203;UlisesGascon**](https://togithub.com/UlisesGascon))

-   [#&#8203;4175](https://togithub.com/mochajs/mocha/issues/4175): Having been deprecated with a warning since v7.0.0, **`mocha.opts` is no longer supported** ([**@&#8203;juergba**](https://togithub.com/juergba))

    ✨ **WORKAROUND:** Replace `mocha.opts` with a [configuration file](https://mochajs.org/#configuring-mocha-nodejs).

-   [#&#8203;4260](https://togithub.com/mochajs/mocha/issues/4260): Remove `enableTimeout()` (`this.enableTimeout()`) from the context object ([**@&#8203;craigtaub**](https://togithub.com/craigtaub))

    ✨ **WORKAROUND:** Replace usage of `this.enableTimeout(false)` in your tests with `this.timeout(0)`.

-   [#&#8203;4315](https://togithub.com/mochajs/mocha/issues/4315): The `spec` option no longer supports a comma-delimited list of files ([**@&#8203;juergba**](https://togithub.com/juergba))

    ✨ **WORKAROUND**: Use an array instead (e.g., `"spec": "foo.js,bar.js"` becomes `"spec": ["foo.js", "bar.js"]`).

-   [#&#8203;4309](https://togithub.com/mochajs/mocha/issues/4309): Drop support for Node.js v13.x line, which is now End-of-Life ([**@&#8203;juergba**](https://togithub.com/juergba))

-   [#&#8203;4282](https://togithub.com/mochajs/mocha/issues/4282): `--forbid-only` will throw an error even if exclusive tests are avoided via `--grep` or other means ([**@&#8203;arvidOtt**](https://togithub.com/arvidOtt))

-   [#&#8203;4223](https://togithub.com/mochajs/mocha/issues/4223): The context object's `skip()` (`this.skip()`) in a "before all" (`before()`) hook will no longer execute subsequent sibling hooks, in addition to hooks in child suites ([**@&#8203;juergba**](https://togithub.com/juergba))

-   [#&#8203;4178](https://togithub.com/mochajs/mocha/issues/4178): Remove previously soft-deprecated APIs ([**@&#8203;wnghdcjfe**](https://togithub.com/wnghdcjfe)):
    -   `Mocha.prototype.ignoreLeaks()`
    -   `Mocha.prototype.useColors()`
    -   `Mocha.prototype.useInlineDiffs()`
    -   `Mocha.prototype.hideDiff()`

#### 🎉 Enhancements

-   [#&#8203;4245](https://togithub.com/mochajs/mocha/issues/4245): Add ability to run tests in parallel for Node.js (see [docs](https://mochajs.org/#parallel-tests)) ([**@&#8203;boneskull**](https://togithub.com/boneskull))

    ❗ See also [#&#8203;4244](https://togithub.com/mochajs/mocha/issues/4244); [Root Hook Plugins (docs)](https://mochajs.org/#root-hook-plugins) -- _root hooks must be defined via Root Hook Plugins to work in parallel mode_

-   [#&#8203;4304](https://togithub.com/mochajs/mocha/issues/4304): `--require` now works with ES modules ([**@&#8203;JacobLey**](https://togithub.com/JacobLey))

-   [#&#8203;4299](https://togithub.com/mochajs/mocha/issues/4299): In some circumstances, Mocha can run ES modules under Node.js v10 -- _use at your own risk!_ ([**@&#8203;giltayar**](https://togithub.com/giltayar))

#### 📖 Documentation

-   [#&#8203;4246](https://togithub.com/mochajs/mocha/issues/4246): Add documentation for parallel mode and Root Hook plugins ([**@&#8203;boneskull**](https://togithub.com/boneskull))

#### 🐛 Fixes

(All bug fixes in Mocha v8.0.0 are also breaking changes, and are listed above)

</details>

---

### Renovate configuration

📅 **Schedule**: "after 9am and before 3pm" (UTC).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻️ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#googleapis/nodejs-dialogflow).
sofisl pushed a commit that referenced this issue Nov 11, 2022
* feat!: Update library to use Node 12

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
sofisl pushed a commit that referenced this issue Jan 24, 2023
This PR was generated using Autosynth. 🌈

Synth log will be available here:
https://source.cloud.google.com/results/invocations/91408a5a-0866-4f1e-92b1-4f0e885b0e2e/targets

- [ ] To automatically regenerate this PR, check this box.
sofisl pushed a commit that referenced this issue Jan 25, 2023
This PR was generated using Autosynth. 🌈

Synth log will be available here:
https://source.cloud.google.com/results/invocations/91408a5a-0866-4f1e-92b1-4f0e885b0e2e/targets

- [ ] To automatically regenerate this PR, check this box.
sofisl pushed a commit that referenced this issue Sep 13, 2023
- [ ] Regenerate this pull request now.

PiperOrigin-RevId: 468790263

Source-Link: googleapis/googleapis@873ab45

Source-Link: googleapis/googleapis-gen@cb6f37a
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiY2I2ZjM3YWVmZjJhMzQ3MmU0MGE3YmJhY2U4YzY3ZDc1ZTI0YmVlNSJ9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: datastore Issues related to the Datastore API. type: question Request for information or clarification. Not an issue.
Projects
None yet
Development

No branches or pull requests

6 participants