I have a collection which contains both _id and id field. When I search by id field in mongo client everything is fine. When I search through mongomapper model like: Product.find_by_id(6) or Product.where(:id => 6) it return empty Plucky object and I can see that it looks for an _id field instead of id.
As I understand mongomapper just always using _id, no matter if you specifically want to find something by id.
Is there any work around for it or I'm doing it wrong?
I believe MongoMapper treats id and _id both equally. id is just a friendlier representation of _id.
In your particular case, is there any reason that you need to have the id field as well? I'd recommend changing that, particularly if there is another more descriptive name which would fit. If you are actually using the id field as a unique identifier (which it sounds like you might be), the best approach would probably be to store it in the _id field instead. As you will already be aware, this is required on all MongoDB documents and can either be specified by you (your application), or added on later by your driver outside of the scope of your application code.
Hope that helps.
It could be caused by this issue (https://github.com/jnunemaker/mongomapper/issues/195) if you ever had an instance with a key of "id." Mongo remembers every key from every instance, unless you clear the key explicitly.
Related
I am using react-native-firebase, and I generally want to use custom keys on the table's JSON objects. I have searched and I know that I can use the set method instead of push, to accomplish that. My problem is when creating new users.
React-native-firebase uses the function signInWithEmailAndPassword(email,password) which automatically creates a new user with a random ID like 9dBqfh5yfbd2dMGIieIe3tPs2ba2. Now since I am used to mySQL database structure, instinctively I would like to use either a numeric key, or even the username as an ID. However I am not really sure if the corect way to store the uid on the database is on the object's key, or inside the object on a uid property.
Is there any way except from using the admin SDK which requires of course a server and cannot run natively on my application to achieve this?
You can set custom UIDs while creating a user using the Admin SDK only. You can store both the random ID and the integer along with other user info in the database if necessary. For example if you are using Firestore then a document can be like:
{
uid: 'firebase_auth_id',
num: 1,
...otherUserInfo
}
Now since I am used to mySQL database structure, instinctively I would like to use either a numeric key, or even the username as an ID
It's quite common to use such random IDs in NoSQL databases instead of integers (like UUIDs as key in SQL). Storing such sequential IDs might lead to hotspots and is not advised as mentioned in Firestore's documentation.
If you need the ID to an integer for querying/sorting purposes then you can store a createdAt Timestamp field instead.
I'm using EF code first with a fairly large number of entity types and I am building a number of reports with filter models that refer to the ID (primary key) fields in the different entity types. Depending on the table, the ID field is either a Byte, Short, Integer or Long (depending on how many records I expect in that table and where it is referenced from).
Eg. I may have a "Channel" table which has an ID field of type Short, whereas "Order" has an ID field of type "Integer".
When I create a model for a view to filter on eg. Channel, I would do a ChannelID Property, which would either be a Short or a Short? (as in Nullable(Of Short)). However, this is a pain as I have to look up the correct ID type for the relevant entity type each time I add a filter and in the long run, it is fragile as I may change the ID type on a particular entity and don't want to have to hunt down every reference to it.
Ideally, I would like to have some way of referring to it eg.
Public Property ChannelID As TypeOf([Channel].[ID])
but I can't see any way to do that.
If I was using C, I would probably #define ChannelID short and just refer to the type as ChannelID throughout, but I can't see any way of using the VB.NET compiler to achieve something like that (which isn't quite as good as the above solution either.
I have thought of the idea of implementing it as an interface for each type, but that doesn't work if there are multiple properties filtering on the same entity in a single model (which is possible), and it is messy as it means an interface for every entity type.
I realise that I could just set them all as Long and there isn't really a major disadvantage to doing this, but I am picky about matching types.
Does anyone have any clever ideas for dealing with this?
Type aliasing will work across files - set this on the 'References' tab of the project properties - e.g., test = System.Int32.
Then in any file in the project you can use the type alias 'test'.
e.g.,
Dim myInt As test
I have REST API URL structure similar to:
/api/contacts GET Returns an array of contacts
/api/contacts/:id GET Returns the contact with id of :id
/api/contacts POST Adds a new contact and return it with an id added
/api/contacts/:id PUT Updates the contact with id of :id
/api/contacts/:id PATCH Partially updates the contact with id of :id
/api/contacts/:id DELETE Deletes the contact with id of :id
My question is about:
/api/contacts/:id GET
Suppose that in addition to fetching the contact by ID, I also want to fetch it by an unique alias.
What should be URI structure be if I want to be able to fetch contact by either ID or Alias?
If you're alias's are not numeric i would suggest using the same URI structure and figuring out if it's an ID or an alias on your end. Just like Facebook does with username and user_id. facebook.com/user_id or facebook.com/username.
Another approach would be to have the client use GET /contacts with some extra GET parameters as filters to first search for a contact and then looking up the ID from that response.
Last option i think would be to use a structure like GET /contacts/alias/:alias. But this would kinda imply that alias is a subresource of contacts.
The path and query part of IRIs are up to you. The path is for hierarchical data, like api/version/module/collection/item/property, the query is for non-hierarchical data, like ?display-fields="id,name,etc..." or ?search="brown teddy bear"&offset=125&count=25, etc...
What you have to keep in mind, that you are working with resources and not operations. So the IRIs are resource identifiers, like DELETE /something, and not operation identifiers, like POST /something/delete. You don't have to follow any structure by IRIs, so for example you could use simply POST /dashuif328rgfiwa. The server would understand, but it would be much harder to write a router for this kind of IRIs, that's why we use nice IRIs.
What is important that a single IRI always belongs only to a single resource. So you cannot read cat properties with GET /cats/123 and write dog properties with PUT /cats/123. What ppl usually don't understand, that a single resource can have multiple IRIs, so for example /cats/123, /cats/name:kitty, /users/123/cats/kitty, cats/123?fields="id,name", etc... can belong to the same resource. Or if you want to give an IRI to a thing (the living cat, not the document which describes it), then you can use /cats/123#thing or /users/123#kitty, etc... You usually do that in RDF documents.
What should be URI structure be if I want to be able to fetch contact
by either ID or Alias?
It can be /api/contacts/name:{name} for example /api/contacts/name:John, since it is clearly hierarchical. Or you can check if the param contains numeric or string in the /api/contacts/{param}.
You can use the query too, but I don't recommend that. For example the following IRI can have 2 separate meanings: /api/contacts?name="John". You want to list every contact with name John, or you want one exact contact. So you have to make some conventions about this kind of requests in the router of your server side application.
I would consider adding a "search" resource when you are trying to resolve a resource with the alias:
GET /api/contacts/:id
and
GET /api/contacts?alias=:alias
or
GET /api/contacts/search?q=:alias
First of all, the 'ID' in the URL doesn't have to be a numerical ID generated by your database. You could use any piece of data (including the alias) in the URL, as long as its unique. Of course, if you are using numerical ID's everywhere, it is more consistent to do the same in your contacts API. But you could choose to use the aliases instead of numeric IDs (as long as they are always unique).
Another approach would be, as Stromgren suggested, to allow both numeric IDs and aliases in the URL:
/api/contacts/123
/api/contacts/foobar
But this can obviously cause problems if aliases can be numeric, because then you wouldn't have any way to differentiate between an ID and a (numeric) alias.
Last but not least, you can implement a way of filtering the complete collection, as shlomi33 already suggested. I wouldn't introduce a search resource, as that isn't really RESTful, so I'd go for the other solution instead:
/api/contacts?alias=foobar
Which should return all contacts with foobar as alias. Since the alias should be unique, this will return 1 or 0 results.
I have a User ndb.Model which has a username StringProperty that allows upper en lower case letters, at some point I wanted to fetch users by username but have the case forced to lowercase for the filtering. Therefor I added a ComputedProperty to User: username_lower which returns the lowercase version of the username as follows:
#ndb.ComputedProperty
def username_lower(self):
return self.username.lower()
then I filter the query like so:
query = query.filter(User.username_lower==username_input.lower())
This works, however it only does for users created (put) after I added this to the model. Users created before don't get filtered by this query. I first thought the ComputedProperty wasn't working for the older users. However, tried this and calling .username_lower on an old user does work.
Finally, I found a solution to this is to fetch all users and just run a .put_multi(all_users)
So seems like a ComputedProperty added later to the model works when you invoke it straight but doesn't filter at first. Does it not get indexed automatically ? or could it be a caching thing.. ?
any insight to why it was behaving like this would be welcome
thanks
this is the expected behaviour. The value of a ComputedProperty (or any property for that matter I guess) is indexed when the object is "put". The datastore does not do automatic schema updates or anything like that. When you update your schema you need to either allow for different schema versions in your code or update your entities individually. In the case of changes to indexing you have no choice but to update your entities. The MapReduce API can be used for updating entities to avoid request limitations and the like.
I have a HasMany mapping that needs a condition. I have this partially working, but there's got to be a better way than what I'm doing. The condition I'm using needs to look at a property on another table that I'm joining to. What I have so far is:
HasMany<MetaData>(x => x.MetaData).Table("MetaData")
.KeyColumn("DefinitionID")
.KeyColumn("TableID")
.Where("metadatade1_.SourceTable = 'Providers'")
.Cascade.SaveUpdate();
In the code above, the where clause is referencing "metadatade1_", because it's trying to fully qualify the name, and that is the name NH is generating. I've tried using "MetaDataDefinitions.SourceTable" (MetaDataDef... is the physical table name), and also just "SourceTable" by itself, however none of these worked.
Is there a way to not have it try and fully qualify the name on the condition and just pass "SourceTable='Providers'" OR is there a way I can have it reference the generated name without me having to manually plug it in?
In short, no. The Where method (and respectively the where= attribute in HBM.XML) accept only raw sql, and as such is prone to the problems you're seeing.
Your best option is to not use a collection and instead rely on a query to retrieve your metadata instances.