# Querying facts database with records

**URL:** <https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447>\
**Category:** General\
**Created:** [May 13, 2024, 1:41pm UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447 "2024-05-13T13:41:12Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![esad](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/esad/32/884_2.png) [@esad](https://swi-prolog.discourse.group/u/esad)\
**Post date:** [May 13, 2024, 1:41pm UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/1 "2024-05-13T13:41:12Z")

</div>

I’m trying to use library(record) to manage and query a fact database but I’m not sure if this is how it should be done. Let’s say I have

`:- record user(name, location).`

If I now do `user_location(U, "Vienna")`, the U is unbound, so in `user_name(U, Name)` `Name` is still unbound and even `user_name(U, "Non existent name") ` will also succeed. Only by doing `call(U)` do I get an instantiation. Is this how the library is supposed to be used for querying?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [May 13, 2024, 2:33pm UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/2 "2024-05-13T14:33:32Z")

</div>

> [@esad](#):
>
> I’m trying to use library(record) to manage and query a fact database

It is not meant for that. It is meant to provide named arguments to compound terms. Similar facilities go by names such as `argnames` (Ciao), `structs` (ECLiPSe) and quite likely a lot more.

Since version 7, SWI-Prolog has dicts that provide more or less the same functionality, but using a more pleasant syntax.

So, what are you trying to do? Have predicate arguments by name rather than position?

---

<div class="post-metadata">

**Author:** ![esad](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/esad/32/884_2.png) [@esad](https://swi-prolog.discourse.group/u/esad)\
**Post date:** [May 13, 2024, 2:42pm UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/3 "2024-05-13T14:42:57Z")

</div>

> [@jan](#):
>
> So, what are you trying to do? Have predicate arguments by name rather than position?

Exactly… I am storing some 100K-1M facts per file, so I was hoping to save some space by using records instead of dictionaries, so key names don’t have to repeat, think CSV vs JSON 😉

What about indexing of dictionary-facts? I need to do quick lookups of facts via numeric ids, will this work?

The `call()` trick is actually kind of working 🙂

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [May 13, 2024, 3:10pm UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/4 "2024-05-13T15:10:24Z")

</div>

All this is not going to be any more efficient than using a predicate with a lot of clauses

```
fact(Col1, Col2, ...)

```

Turning that into something like this only costs more memory and slows down indexing (a little).

```
fact(mystruct(Col1, Col2, ...))

```

You can indeed use records to create a compound with the arguments in the right position, as in

```
make_my_struct([name('Bob'), age(Age)], Term),
call(Term).

```

If you want better performance, write a wrapper for these two and use goal\_expansion/2 to turn that into a straight call at compile time.

I’ve done similar tricks using dicts. As these are not meant as goals, you can make a declaration for the argument names of a predicate and then use goal\_expansion/2 to turn `tag{k1:V1, ...}` into `tag(..., V1, ....)`.

Possibly that should be turned into a library. As we speak though, discussions between various Prolog systems are ongoing to synchronize these issues.

---

<div class="post-metadata">

**Author:** ![esad](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/esad/32/884_2.png) [@esad](https://swi-prolog.discourse.group/u/esad)\
**Post date:** [May 13, 2024, 9:10pm UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/5 "2024-05-13T21:10:58Z")

</div>

My worry is that something like `fact(user{name: ..., location: ...})` will not be as good index-wise as using plain `user/2` (Some of my facts/terms have arity \>10). If I understand it correctly, `library(record)` just lets me give names to positional arguments so there is no performance overhead.

Now let’s imagine that `user` has more fields/higher arity - wouldn’t using dictionaries then require the actual query from my first example to be in form of `fact(D), user{location: "Vienna", name: Name} :< D` because partial unification like `fact(user{location: "Vienna", name: Name})` does’t work? And I guess there will be 0 use of indexing in this case?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [May 14, 2024, 7:16am UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/6 "2024-05-14T07:16:22Z")

</div>

See mail post above this. Dictionaries are not useful for your case. The way to go is to use a predicate with flat facts and some goal expansion mechanism that rewrites a term with named variables to the proper goal as I explained above. Using dicts for this representation is a good option, so you’re program looks like

```
...,
user{location:"Vienna", name:Name},
...

```

and compiles to e.g.,

```
user(_, _, "Vienna", _, Name, _, _, _),

```

Note that for indexing, atoms are faster than strings as atoms just look at the handle and strings actually need to access the text.

---

<div class="post-metadata">

**Author:** ![esad](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/esad/32/884_2.png) [@esad](https://swi-prolog.discourse.group/u/esad)\
**Post date:** [May 14, 2024, 9:59am UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/7 "2024-05-14T09:59:50Z")

</div>

I see, so it is about dictionaries as “natural” interface to records. If I arrive at something useful, would such addition/predicate be a good addition to `library(record)`?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [May 14, 2024, 10:20am UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/8 "2024-05-14T10:20:59Z")

</div>

> [@esad](#):
>
> If I arrive at something useful, would such addition/predicate be a good addition to `library(record)`?

I’m afraid not. First of all, dicts are an alternative to library(record), so one would either have to build something inside library(record) that does not use dicts (which is possible) or create a new library.

More importantly though, this type of stuff is under discussion between SWI, XSB and Ciao at the moment. I prefer to wait and come with something portable at this stage.

---

<div class="post-metadata">

**Author:** ![esad](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/esad/32/884_2.png) [@esad](https://swi-prolog.discourse.group/u/esad)\
**Post date:** [May 14, 2024, 12:33pm UTC](https://swi-prolog.discourse.group/t/querying-facts-database-with-records/7447/9 "2024-05-14T12:33:08Z")

</div>

I’ve arrived at this (runtime) version:

```prolog
find_record(Dict) :-
  find_record(Dict, _).

find_record(Dict, R) :-
  is_dict(Dict, Tag),
  atomic_list_concat([Tag,'_',data], Functor),
  bagof(
    P,
    Key^Value^(get_dict(Key, Dict, Value), P =.. [Functor, Key, R, Value]),
    Preds
  ),
  maplist(call, Preds), call(R).

```

Thank you for the idea, this makes for very nice interface for querying records, it’s pretty cool 🙂

I am not sure how to do the expansion version it without relying on internals of `library(record)` to check if dictionary tag is defined as a record, I don’t see any public API for this, as well as looking up position of each record field?
