I am using CouchDb to store my documents and figuring out how to pull 1:1 related data on a document using CouchDb views.
For this given document structure,
{
_id: 'some_id',
doc_type: 'item',
name: 'Steel Caliper'
description: 'Steel Caliper description',
classification: {
_id: 'some_classification_id'
}
}
classification: {
_id: 'some_classification_id',
doc_type: 'classification',
name: 'Hand Tools'
}
When I retrieve a list of item documents using a CouchDb view like below (called with include_docs=true)
function(doc) {
if(doc_type==='item') {
emit(doc.name, null);
}
}
I get the item document but without the classification data pulled in.
{
"total_rows": 10,
"offset": 0,
"rows": [
{
"id": "some_id",
"key": "Steel Caliper",
"value": null,
"doc": {
"_id": "some_id",
"_rev": "1-65d32fe22a0b949ff73f23c65042ae3c"
"doc_type": "item"
"name": "Steel Caliper"
"classification": {
"_id": "some_classification_id"
}
}
},
{ ... }
}
Is there a way to pull in the classification data using a view and get an output like below ?
...
"doc": {
"_id": "some_id",
"_rev": "1-65d32fe22a0b949ff73f23c65042ae3c"
"doc_type": "item"
"name": "Steel Caliper"
"classification": {
"_id": "some_classification_id",
"doc_type": "classification",
"name": "Hand Tools"
}
}
...
yes, this is called view collation. You can emit multiple docs if you are joining by _ids.
in your case you can call
function(doc) {
if(doc_type==='item') {
emit(doc.name, null);
if( doc.classification ){
emit( doc.classification._id, {_id: doc.classification._id });
}
}
}
you might still need play around with the returned result to get it into the form you want.
http://docs.couchdb.org/en/2.0.0/couchapp/views/joins.html
Related
This might be a stupid question. I am building a server with Node JS that exposes an API to clients. I was wondering what is the recommended way to handle "ref" in the query results.
For example, I have a query like this -
q.Map(
q.Paginate(
q.Match(q.Index("network_by_creator"), q.Select("ref", q.Call(q.Function("getUserByUsername"), username))),
options
),
q.Lambda("x", q.Get(q.Var("x")))
)
This returns all the networks created by the user. An example result looks like this -
{
"before": [
{
"#ref": {
"id": "279699094284272133",
"collection": {
"#ref": {
"id": "networks",
"collection": {
"#ref": {
"id": "collections"
}
}
}
}
}
}
],
"data": [
{
"ref": {
"#ref": {
"id": "279699094284272133",
"collection": {
"#ref": {
"id": "networks",
"collection": {
"#ref": {
"id": "collections"
}
}
}
}
}
},
"ts": 1603000692605000,
"data": {
"creator": {
"#ref": {
"id": "279656553326313989",
"collection": {
"#ref": {
"id": "users",
"collection": {
"#ref": {
"id": "collections"
}
}
}
}
}
},
"name": "Hello2"
}
}
]
}
I was wondering should I remove the "ref" fields from the objects before sending them to the client (the "name" field can be used to search a network), or should I extract the "id" from the "ref" and send it along? In any case, can that be done through FQL, instead of querying the result and manually modifying?
A Reference is a compound value, comprised of the document's collection reference and a document ID.
You can certainly extract the document ID to send to the client, provided that when the client sends it to your API, you know which collection to use. If your API transits data for multiple collections, you should also return the collection reference name too.
Here is a query that demonstrates how to pull out the components of a Reference:
> Let(
{
ref: Ref(Collection("networks"), "279699094284272133")
},
{
collection: Select("collection", Var("ref")),
collection_Name: Select(["collection", "id"], Var("ref")),
documentID: Select("id", Var("ref"))
}
)
{
collection: Collection("networks"),
collection_Name: 'networks',
documentID: '279699094284272133'
}
Just use the appropriate Select expression to extract what you need.
I need to get the documents where exists Archery in the array list of games. How can i do with CouchDB selector?
[{
"name": "John",
"games": ["Archery", "Board sports"]
},
{
"name": "Sara",
"games": ["Fishing", "Archery"]
},
{
"name": "Tara",
"games": ["Decathlon"]
}]
You can use $elemMatch:
{
"selector": {
"games": {
"$elemMatch": {
"$eq": "Archery"
}
}
}
}
It will return all objects where games field equals 'Archery'
My object:
[
{
"_id": "568ad3db59b494d4284ac191",
"name": "Test",
"groups": [
{
"number": "1",
"name": "GroupTest",
"_id": "568ad3db59b494d4284ac19b",
"orders": [
{
"date": "2016-03-06T13:07:40.990Z",
"_id": "56dc2b9c1d47772806e4f0f4",
"readings": [
{
"readingid": "568ad3db59b494d4284ac1a5",
"_id": "56dc2b9c1d47772806e4f0fc"
},
{
"readingid": "568ad3db59b494d4284ac1a4",
"_id": "56dc2b9c1d47772806e4f0fb"
},
{
"readingid": "568ad3db59b494d4284ac1a3",
"_id": "56dc2b9c1d47772806e4f0fa"
},
{
"readingid": "568ad3db59b494d4284ac1a2",
"_id": "56dc2b9c1d47772806e4f0f9"
},
{
"readingid": "56d48ae1a0f6e04413fc8b3e",
"_id": "56dc2b9c1d47772806e4f0f8"
},
{
"readingid": "56d48ae1a0f6e04413fc8b3f",
"_id": "56dc2b9c1d47772806e4f0f7"
},
{
"readingid": "568ad3db59b494d4284ac1a1",
"_id": "56dc2b9c1d47772806e4f0f6"
},
{
"readingid": "568ad3db59b494d4284ac1a0",
"_id": "56dc2b9c1d47772806e4f0f5"
}
]
},
{....}
]
},
{....}
]
},
{.....}
]
I need to finde the order with the _id: "56dc2b9c1d47772806e4f0f4" in the group with the _id: "568ad3db59b494d4284ac19b" inside the client object with _id:"568ad3db59b494d4284ac191" and I only want to get that order subobject, not the whole client object.
I tried something like:
Client.find(
{_id: "568ad3db59b494d4284ac191", groups._id: "568ad3db59b494d4284ac19b", groups.orders._id:"56dc2b9c1d47772806e4f0f4"},
{groups.orders:{$elemMatch:{_id: "56dc2b9c1d47772806e4f0f4"}}})
Antoher attempt without success:
Client.find(
{_id: req.company, groups:ObjectId(req.params.groupId)},
{"groups.orders":{$elemMatch:{_id: ObjectId(req.params.orderId)}}}, function(e,company){
if(!e) {
console.log(company);
}
});
mongo is a "document-oriented" database, so all the normal operations and queries it provides return "documents" and not "parts of documents", so with the normal query methods there is no way to get just a subobject.
You can use the mongo aggregation framework to achieve what you want (https://docs.mongodb.org/manual/reference/operator/aggregation/), using two $unwind: https://docs.mongodb.org/manual/reference/operator/aggregation/unwind/ .
This will produce a document for each order and then use $match to filter out the ones you need.
But probably it might be better to rethink your data model. If you really need to query and retrieve orders only, without getting the whole client object, it might be better to store the orders in their own collection and use references to groups and clients.
What I am trying to do is the following.
I have objects that could live in multiple documents, and although I am not sure if this is best practice, I am using their ObjectId as pointers.
Document A
{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [
"51a81d8ee4b084336fea2d33",
"asdfsdfe4b084336fea2d33"
]
}
Document B
{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [
"51a81d8ee4b084336fea2d33",
"123454b084336fea2d33"
]
}
The individual artifact looks something like. If this artifact changes then there is no need to update the documents that it lives in:
{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}
What I'd like to do, is get an expanded list of all the artifacts shown in either doc A or doc B in an array something like:
[{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
},
{
"_id": {
"$oid": "123455e4b084336fea2d33"
},
"title": "Artifact B",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}]
The way I have done it in the past is by POSTing from client a list of Ids and then using
{_id: { $in: userQuestArray} }
but if i am constantly posting back to node from the client it seems a bit inefficient. Especially if the array I am posting is hundreds of items long.
Mongoose provide you a feature population which exactly does the same thing you are looking for http://mongoosejs.com/docs/populate.html. In your case what you need to do is
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var tempSchema = Schema({
title : String,
artifacts : [{ type: Schema.Types.ObjectId, ref: 'Artifacts' }]
});
var artifactsSchema = Schema({
title : String,
media : { player: String, source: String, }
});
var tempModel = mongoose.model('Temp', tempSchema);
var artifactsModel = mongoose.model('Artifacts', artifactsSchema);
Now whenever artifacts documents gets created you need to push that in respective temp dcoument artifacts array as shown in mongoose documentation. You can retrive it like that
tempModel
.find(query)
.populate('artifacts')
.exec(function (err, results) {
if (err)
console.log(err);
console.log(results);
})
//results contains data in form shown below
[{
"_id": {
"$oid": "51a02dade4b02780aeee5ab7"
},
"title": "My Document A",
"artifacts": [{
"_id": {
"$oid": "51a81d8ee4b084336fea2d33"
},
"title": "Artifact A",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
},
{
"_id": {
"$oid": "123455e4b084336fea2d33"
},
"title": "Artifact B",
"media": {
"player": "video",
"source": "http://www.youtube.com/watch?v=12334456"
}
}]
}]
I'm having a hard time getting my head around CouchDB's linked documents feature.
I have two types of data being stored in a single CouchDB database:
{
"id":"1",
"type": "track",
"title": "Bohemian Rhapsody"
}
{
"id":"2",
"type": "artist",
"name": "Queen",
"tracks": ["1"]
}
I'm under the impression that I can write a view like the one below and get the following documents emited:
{
"id":"2",
"type": "artist",
"name": "Queen",
"tracks": [
{
"id":"1",
"type": "track",
"title": "Bohemian Rhapsody"
}
]
}
I've been trying this view, but it's not working the way I'm expecting:
function(doc) {
if(doc.type == 'artist') {
var tracks = [];
for(var i = 0; i < doc.tracks.length; i++) {
tracks.push({_id:doc.tracks[i]});
}
newdoc = eval(uneval(doc));
newdoc.tracks = tracks;
emit(doc._id,newdoc);
}
}
example here: http://jphastings.iriscouch.com/_utils/database.html?music/_design/test/_view/linked
This isn't returning what I'd hope - do you have any suggestions? Thanks
Okay I finally understand what you are trying to do.Yes this is possible.Here is how.
You have 2 documents
{
"_id":"anyvalue",
"type": "track",
"title": "Bohemian Rhapsody"
}
{
"_id":"2",
"type": "artist",
"name": "Queen",
"tracks": ["anyvalue"]
}
What you were doing wrong was not having quotes around the value of tracks(the item in the array).
2)The reference id must be _id for this to work.The difference is worth noting since you can have id field but only _id are used to identify documents.
For the result you want this view would suffice
function(doc) {
if (doc.type === 'artist') {
for (var i in doc.tracks) {
var id = doc.tracks[i];
emit(id, { _id: id });
}
}
}
What you want to be doing is use an emit function inside the for loop to emit the id field of the 'track' of every artist.
Then you want to query couch db view with the include_docs=true parameter.Here is the final result for the database that you created on iris couch.
http://jphastings.iriscouch.com/music/_design/test/_view/nested?reduce=false&include_docs=true
{
"total_rows": 3,
"offset": 0,
"rows": [
{
"id": "0b86008d8490abf0b7e4f15f0c6a50a7",
"key": "0b86008d8490abf0b7e4f15f0c6a463b",
"value": {
"_id": "0b86008d8490abf0b7e4f15f0c6a463b"
},
"doc": {
"_id": "0b86008d8490abf0b7e4f15f0c6a463b",
"_rev": "3-7e4ba3bfedd29a07898125c09dd7262e",
"type": "track",
"title": "Boheniam Rhapsody"
}
},
{
"id": "0b86008d8490abf0b7e4f15f0c6a50a7",
"key": "0b86008d8490abf0b7e4f15f0c6a5ae2",
"value": {
"_id": "0b86008d8490abf0b7e4f15f0c6a5ae2"
},
"doc": {
"_id": "0b86008d8490abf0b7e4f15f0c6a5ae2",
"_rev": "2-b3989dd37ef4d8ed58516835900b549e",
"type": "track",
"title": "Another one bites the dust"
}
},
{
"id": "0b86008d8490abf0b7e4f15f0c6a695e",
"key": "0b86008d8490abf0b7e4f15f0c6a6353",
"value": {
"_id": "0b86008d8490abf0b7e4f15f0c6a6353"
},
"doc": {
"_id": "0b86008d8490abf0b7e4f15f0c6a6353",
"_rev": "2-0383f18c198b813943615d2bf59c212a",
"type": "track",
"title": "Stripper Vicar"
}
}
]
}
Jason explains it wonderfully in this post
Best way to do one-to-many "JOIN" in CouchDB
this link is also helpful for entity relationships in couch db
http://wiki.apache.org/couchdb/EntityRelationship