I am new to CouchDB and learning about it. I did not come across CouchDB support for referential integrity.
Can we create a foreign key for a field in the CouchDB document?
For e.g. Is it possible to ensure a vendor name used in a order document is available in the vendor database?
Does CouchDB support referential integrity?
And Is it possible to make a field in a document as Primary key?
No, CouchDB doesn't do foreign keys as such, so you can't have it handle the referential integrity of the system for you. You would need to handle checking for vendors in the application level.
As to whether you can make a field a primary key, the primary key is the _id field, but you can use any valid json as a key for the views on the db. So, for instance you could create a view of orders with their vendor as the key.
something like
function(doc) {
if (doc.type == 'order')
emit(doc.vendor,doc);
}
would grab all the docs in the database that have a type attribute with the value order and add them to a view using their vendor as the key.
Intro to CouchDB views
These questions are incredibly relational database specific.
In CouchDB, or any other non-RDBMS, you wouldn't store your data the same way you would in an RDBMS so designing the relationship this way may not be best. But, just to give you an idea of how you could do this, lets assume you have a document for a vendor and a bunch of documents for orders that need to "relate" back to the vendor document.
There are no primary keys, documents have an _id which is a uuid. If you have a document for a vendor, and you're creating a new document for something like an order, you can reference the vendor documents _id.
{"type":"order","vendor-id":"asd7d7f6ds76f7d7s"}
To look up all orders for a particular vendor you would have a map view something like:
function(doc) { if (doc.type == 'order') {emit(doc['vendor-id'], doc)}}
The document _id will not change, so there is "integrity" there, even if you change other attributes on the vendor document like their name or billing information. If you stick the vendor name or other attributes from the vendor document directly in to the order document you would need to write a script if you ever wanted to change them in bulk.
Hope that helps a bit.
While not possible to create an FK constraint, it is possible using Couch's Validate function
function(newDoc, oldDoc, userCtx, secObj) {
if(newDoc && newDoc.type) switch(newDoc.type){
case 'fish':
var allSpecies = ['trout','goldfish'];
if(!allSpecies.contains(newDoc.species)){
throw({forbidden : 'fish must be of a know species'});
}
break;
case 'mammals':
if(!['M','F'].contains(newDoc.sex)){
throw({forbidden : 'mammals must have their sex listed'});
}
break;
}
}
Now, if a person were really clever (I'm not), they might do a call out to the DB itself for the list of Species... that would be a foreign key.
You may also want to read up on:
How do I DRY up my CouchDB views?
Related
I have only rev id and now I want to retrieve data by rev id in the Couch Db.
So you wish to fetch any document that has a specific _rev, regardless of its _id. Leaving aside the obvious "why???" here, there is nothing in the CouchDB API that supports this, as the rev is not intended to be used without a corresponding id.
But if you really want to do this, I guess you could create a view that emits the _rev as the key and then query the view:
function (doc) {
emit(doc._rev, 1);
}
But note: this is a really bad idea.
I'm using CosmosDB as a document database, for some things I have to store a 'foreign key' link and the way I've been doing it is as a string property on a document pointing at the ID of the 'foreign key' document.
If the foreign document is deleted what would the most efficient way be of finding all foreign key links and ensuring they are removed?
I'd like it to be as automated as possible so I don't have to 'think' about it too hard in a project, my best solution in my head is to store foreign key links in a well defined structure like:
{
"foreignId": "crazy_person",
"foreignType": "person"
}
And store that structure in any way shape or form across various documents, then when "crazy_person" is deleted I find all documents which have that structure defined and if the foreignId/foreignType matches crazy_person to remove them.
Not sure how I would implement this though as the structure above could be in any given document, like so:
{
"foreignPerson": {
"foreignId": "crazy_person",
"foreignType": "person"
},
"foreignPeople": [
{
"foreignId": "crazy_person",
"foreignType": "person"
}
]
}
There is NO Foreign Key concept in CosmosDB. According to the docs,
any inter-document relationships that you have in documents are
effectively "weak links" and will not be verified by the database
itself. If you want to ensure that the data a document is referring to
actually exists, then you need to do this in your application, or
through the use of server-side triggers or stored procedures on Azure
Cosmos DB.
Switching from mongo to pouchdb (with Cloudant), i like the "one database per user" concept, but is there a way to create multiple collections/tables per database ?
Example
- Peter
- History
- Settings
- Friends
- John
- History
- Settings
- Friends
etc...
Couchdb does not have the concept of collections. However, you can achieve similar results using type identifiers on your documents in conjunction with Couchdb views.
Type Identifiers
When you save a document in Couchdb add a field that specifies the type. For example, you would store a friend like so:
{
_id: "XXXX",
type: "Friend",
first_name: "John",
...
}
And you would store history like this:
{
_id: "XXXX",
type: "History",
url: "http://www.google.com",
...
}
Both of these documents would be in the same database, and if you queried all documents on that database then you would receive both.
Views
You can create views that filter on type and then query those views directly. For example, create a view to retrieve friends like so (in Cloudant you can go to add new Design Document and you can copy and paste this directly):
{
"_id" : "_design/friends",
"views" : {
"all" : {
"map" : "function(doc){ if (doc.type && doc.type == 'Friend') { emit(doc._id, doc._rev)}}"
}
}
}
Let's expand the map function:
function(doc) {
if (doc.type && doc.type == "Friend") {
emit(doc._id, doc._rev);
}
}
Essentially this map function is saying to only associate documents to this view that have type == "Friend". Now, we can query this view and only friends will be returned:
http://SERVER/DATABASE/_design/friends/_view/all
Where friends = name of the design document and all = name of the view. Replace SERVER with your server and DATABASE with your database name.
You can find more information about views here:
https://wiki.apache.org/couchdb/Introduction_to_CouchDB_views
You could look into relational-pouch for something like this. Else you could do "3 databases per user." ;)
I may not fully understand what you need here but in general you can achieve what you describe in 3 different ways in CouchDB/Cloudant/PouchDB.
Single document per person (Peter, John). Sure - if the collections are not enormous and more importantly if they are not updated by different users concurrently (or worse in different database instances) leading to conflicts then, in JSON just an element for each collection, holding an array and you can manipulate everything with just one document. Makes access a breeze.
Single document per collection (Peter History, Peter Settings ect). Similar constraints, but you could create a document to hold each of these collections. Provided they will not be concurrently modified often, you would then have a document for Peter's History, and another for Peter's Settings.
Single document per item. This is the finest grain approach - lots of small simple documents each containing one element (say a single History entry for Peter). The code gets slightly simpler because removing items becomes a delete and many clients can update items simultaneously, but now you depend on Views to bring all the items into a list. A view with keys [person, listName, item] for example would let you access what you want.
Generally your data schema decisions come down to concurrency. You mention PouchDB so it may be that you have a single threaded client and option 1 is nice and easy?
I've never worked with a database before, but I chose Couch DB because I needed a Json database, and HTTP queries seemed kinda simple. However the documentation assumes a level of knowledge I just don't have.
Assuming I have a database called 'subjects', it seems I can access the json by using GET on
http://localhost:5984/subjects/c6604f65029f1a6a5d565da029001f4c
However beyond that I'm stuck. Ideally I want to be able to:
Access a list of all the keys in the database (not their values)
Access an individual element by its key
Do I need to use views for this? Or can I just set fields in my GET request? Can someone give me a complete example of the request they'd use? Please don't link to the CouchDB documentation, it really hasn't helped me so far.
Views can be used to fetch the data
1) In order to get all keys from the database you can use below view
function(doc) {
if (doc.type=="article")
emit(doc._id,null); //emit(key,value), if you have any other field as key then specify as doc.key e.g doc.
}
You can access this view from browser using below URL
http://<ipaddress>:<port>/databasename/_design/designdocumentname/_view/viewname
e.g :
http://<ipaddress>:<port>/article/_design/articlelist/_view/articlelist
article is the database name,articlelist is name of the design document as well as view.
2) In order to access individual document by key
Below view will return all the articles belonging to a particular department
function(doc) {
if(doc.type == 'article' ) {
emit([doc.departmentname], doc);
}
}
Query this view based on the "department name"
e.g: Get all the articles belonging to "IBU3" department
http://<ipaddress>:<port>/department/_design/categoryname/_view/categoryname?key=[%22IBU3%22]
I prepare to use CouchDB to my project. but cannot find a way to implement a view like an SQL SELECT * FROM Employees WHERE LastName NOT IN (SELECT LastName FROM Managers). In other words, I want to get a set from view A but not in view B. Question: how to implement not-in condition in CouchDB?
Keeping employees and managers lists different sets of documents is using relational structure where you DB is not relational. If, for some reason, you are forced to do that, you need some way to distinguish the scheme of the doc (from which table it is). Lets say you are doing it with field scheme:
{ _id: "EMPL_ID", scheme: "employee", ... }
{ _id: "MNGR_ID", scheme: "manager", employee: "EMPL_ID", ... }
Then you can use map:
function (doc) {
if (!doc.scheme) return;
if (doc.scheme != "manager") emit(doc.last_name, doc);
}
If, for some strange reason, you cannot do that, and you only have the reference to employee doc in manager doc, you can emit both documents:
function (doc) {
if (some_test_for_being_employee_scheme(doc))
emit([doc._id, 1], doc);
if (doc.emp_id)
emit([doc.emp_id, 0], null);
}
You will get the list of employees with keys ["employee_id", 1], and each manager is preceded with the row labeled as manager (key [..., 0]). This will require some space, but with list function you can filter out managers easily and the client will receive from DB only the non-managers.
Keep in mind, that it is only the workaround for not making proper DB design.
If you change the model to make it fit a document-oriented database, this would be easy. I generally keep a "type" key in all of my documents to keep different types of documents straight. If you have a single "person" type and decorate all "person" documents who are also "manager" with a separate key, you could easily emit view keys only for non-managerial personnel. If you opt to have a separate "manager" type, you could similarly restrict emitted view keys to non-managers only.
I think the answer is simply: you can't mix view results. Views are independent.
However, there is a strategy called view collation that probably solves your problems. I suggest reading this: http://wiki.apache.org/couchdb/View_collation
To summarize it: You need to use different document types and then use a single view to collate the results.